diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index abcc08bf..5b1f80bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,25 +2,19 @@ name: publish on: release: - types: - - created + types: [created] workflow_dispatch: jobs: publish: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - cache: pip - - run: pip install .[dev] + - name: Set up uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + - run: uv sync - name: Build a binary wheel and a source tarball - run: | - python -m build --sdist --wheel --outdir dist/ . - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + run: uv run python -m build --sdist --wheel --outdir dist/ . + - uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 89ff62f6..30f06363 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,27 +4,54 @@ on: push: branches: [main] pull_request: - branches: [main] jobs: - test: + pre-commit: runs-on: ubuntu-latest + if: github.event_name == 'pull_request' # pre-commit-ci/lite-action only runs here strategy: matrix: - python-version: ["3.11"] - + python-version: [3.12] # Our min and max supported Python versions steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - cache: pip - - run: pip install .[dev] - - name: Check pre-commit - run: pre-commit run --all-files || ( git status --short ; git diff ; exit 1 ) - - name: Run Test + - uses: pre-commit/action@v3.0.1 + - uses: pre-commit-ci/lite-action@v1.0.2 + if: always() + lint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.11] # Our min supported Python version + steps: + - uses: actions/checkout@v4 + - name: Set up uv + run: |- + curl -LsSf https://astral.sh/uv/install.sh | sh + uv python pin ${{ matrix.python-version }} + - run: uv sync --python-preference=only-managed + - run: uv run refurb paperqa tests + - run: uv run pylint paperqa + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.12] # Our min and max supported Python versions + steps: + - uses: actions/checkout@v4 + - name: Set up uv + run: |- + curl -LsSf https://astral.sh/uv/install.sh | sh + uv python pin ${{ matrix.python-version }} + - run: uv sync --python-preference=only-managed + - uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }} + - run: uv run pytest -n auto env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: pytest + SEMANTIC_SCHOLAR_API_KEY: ${{ secrets.SEMANTIC_SCHOLAR_API_KEY }} + CROSSREF_API_KEY: ${{ secrets.CROSSREF_API_KEY }} diff --git a/.gitignore b/.gitignore index 7edec0e8..304d979c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,138 @@ +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets +.vscode + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon[\r] + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -20,7 +155,6 @@ parts/ sdist/ var/ wheels/ -pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg @@ -50,6 +184,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +cover/ # Translations *.mo @@ -72,6 +207,7 @@ instance/ docs/_build/ # PyBuilder +.pybuilder/ target/ # Jupyter Notebook @@ -82,7 +218,9 @@ profile_default/ ipython_config.py # pyenv -.python-version +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -91,7 +229,24 @@ ipython_config.py # install all needed dependencies. #Pipfile.lock -# PEP 582; used by e.g. github.com/David-OConnor/pyflow +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff @@ -128,12 +283,23 @@ dmypy.json # Pyre type checker .pyre/ -# testing files generated -*.txt.json +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ -*.ipynb -env +# Version files made by setuptools_scm +**/version.py -# Matching pyproject.toml -paperqa/version.py -tests/example* +# Tests +tests/*txt +tests/*html +tests/test_index/* diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..de79b152 --- /dev/null +++ b/.mailmap @@ -0,0 +1,8 @@ +Andrew White +James Braza +Geemi Wellawatte +Geemi Wellawatte <49410838+geemi725@users.noreply.github.com> +Michael Skarlinski mskarlin <12701035+mskarlin@users.noreply.github.com> +Odhran O'Donoghue odhran-o-d +Odhran O'Donoghue <39832722+odhran-o-d@users.noreply.github.com> +Samantha Cox diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc5f44bb..99546e31 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,8 @@ repos: - id: check-added-large-files exclude: | (?x)^( - paperqa/clients/client_data.* + paperqa/clients/client_data.*| + tests/stub_data.* )$ - id: check-byte-order-marker - id: check-case-conflict @@ -22,27 +23,16 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.0 + rev: v0.6.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 - hooks: - - id: mypy - args: [--pretty, --ignore-missing-imports] - additional_dependencies: - - numpy - - openai>=1 # Match pyproject.toml - - pydantic~=2.0 # Match pyproject.toml - - types-requests - - types-setuptools - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.3.2 + rev: v3.3.3 hooks: - id: prettier - repo: https://github.com/pappasam/toml-sort @@ -57,15 +47,44 @@ repos: exclude: | (?x)^( tests/cassettes.*| - paperqa/clients/client_data.* + paperqa/clients/client_data.*| + tests/stub_data.* )$ + - repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: check-mailmap - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.18 + rev: v0.19 hooks: - id: validate-pyproject additional_dependencies: - "validate-pyproject-schema-store[all]>=2024.06.24" # Pin for Ruff's FURB154 + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.4.8 + hooks: + - id: uv-lock - repo: https://github.com/adamchainz/blacken-docs - rev: v1.12.1 + rev: 1.18.0 hooks: - id: blacken-docs + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.2 + hooks: + - id: mypy + args: [--pretty, --ignore-missing-imports] + additional_dependencies: + - aiohttp + - fhaviary[llm]>=0.6 # Match pyproject.toml + - ldp>=0.4 # Match pyproject.toml + - html2text + - httpx + - numpy + - openai>=1 # Match pyproject.toml + - pydantic~=2.0 # Match pyproject.toml + - pydantic-settings + - tantivy + - tenacity + - tiktoken>=0.4.0 # Match pyproject.toml + - types-setuptools + - types-PyYAML diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..e4fba218 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..0ef8a230 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,51 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it as below." +authors: + - family-names: "Skarlinski" + given-names: "Michael D." + - family-names: "Cox" + given-names: "Sam" + - family-names: "Laurent" + given-names: "Jon M." + - family-names: "Braza" + given-names: "James D." + - family-names: "Hinks" + given-names: "Michaela" + - family-names: "Hammerling" + given-names: "Michael J." + - family-names: "Ponnapati" + given-names: "Manvitha" + - family-names: "Rodriques" + given-names: "Samuel G." + - family-names: "White" + given-names: "Andrew D." +title: "Language agents achieve superhuman synthesis of scientific knowledge" +version: 2024 +doi: "10.xxxx/xxxxxx" +date-released: 2024 +url: "https://paper.wikicrow.ai" +preferred-citation: + type: article + authors: + - family-names: "Skarlinski" + given-names: "Michael D." + - family-names: "Cox" + given-names: "Sam" + - family-names: "Laurent" + given-names: "Jon M." + - family-names: "Braza" + given-names: "James D." + - family-names: "Hinks" + given-names: "Michaela" + - family-names: "Hammerling" + given-names: "Michael J." + - family-names: "Ponnapati" + given-names: "Manvitha" + - family-names: "Rodriques" + given-names: "Samuel G." + - family-names: "White" + given-names: "Andrew D." + title: "Language agents achieve superhuman synthesis of scientific knowledge" + journal: "preprint" + year: 2024 + month: 9 # Adjust month if known diff --git a/README.md b/README.md index 056bfcf4..e521e8c8 100644 --- a/README.md +++ b/README.md @@ -1,268 +1,416 @@ -# PaperQA +# PaperQA2 [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/whitead/paper-qa) [![tests](https://github.com/whitead/paper-qa/actions/workflows/tests.yml/badge.svg)](https://github.com/whitead/paper-qa) [![PyPI version](https://badge.fury.io/py/paper-qa.svg)](https://badge.fury.io/py/paper-qa) -This is a minimal package for doing question and answering from -PDFs or text files (which can be raw HTML). It strives to give very good answers, with no hallucinations, by grounding responses with in-text citations. +PaperQA2 is a package for doing high-accuracy retrieval augmented generation (RAG) on PDFs or text files, +with a focus on the scientific literature. +See our [recent 2024 paper](https://paper.wikicrow.ai) to see examples of PaperQA2's superhuman performance in scientific tasks like +question answering, summarization, and contradiction detection. -By default, it uses [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings) with a simple numpy vector DB to embed and search documents. However, via [langchain](https://github.com/hwchase17/langchain) you can use open-source models or embeddings (see details below). +## Quickstart -paper-qa uses the process shown below: +In this example we take a folder of research paper PDFs, magically get their metadata - including citation counts and a retraction check, then parse and cache PDFs into a full-text search index, and finally answer the user question with an LLM agent. -1. embed docs into vectors -2. embed query into vector -3. search for top k passages in docs -4. create summary of each passage relevant to query -5. use an LLM to re-score and select only relevant summaries -6. put summaries into prompt -7. generate answer with prompt - -See our paper for more details: - -```bibtex -@article{lala2023paperqa, - title={PaperQA: Retrieval-Augmented Generative Agent for Scientific Research}, - author={L{\'a}la, Jakub and O'Donoghue, Odhran and Shtedritski, Aleksandar and Cox, Sam and Rodriques, Samuel G and White, Andrew D}, - journal={arXiv preprint arXiv:2312.07559}, - year={2023} -} +```bash +pip install paper-qa +cd my_papers +pqa ask 'How can carbon nanotubes be manufactured at a large scale?' ``` -## Output Example +### Example Output + +Question: Has anyone designed neural networks that compute with proteins or DNA? -Question: How can carbon nanotubes be manufactured at a large scale? +> The claim that neural networks have been designed to compute with DNA is supported by multiple sources. The work by Qian, Winfree, and Bruck demonstrates the use of DNA strand displacement cascades to construct neural network components, such as artificial neurons and associative memories, using a DNA-based system (Qian2011Neural pages 1-2, Qian2011Neural pages 15-16, Qian2011Neural pages 54-56). This research includes the implementation of a 3-bit XOR gate and a four-neuron Hopfield associative memory, showcasing the potential of DNA for neural network computation. Additionally, the application of deep learning techniques to genomics, which involves computing with DNA sequences, is well-documented. Studies have applied convolutional neural networks (CNNs) to predict genomic features such as transcription factor binding and DNA accessibility (Eraslan2019Deep pages 4-5, Eraslan2019Deep pages 5-6). These models leverage DNA sequences as input data, effectively using neural networks to compute with DNA. While the provided excerpts do not explicitly mention protein-based neural network computation, they do highlight the use of neural networks in tasks related to protein sequences, such as predicting DNA-protein binding (Zeng2016Convolutional pages 1-2). However, the primary focus remains on DNA-based computation. -Carbon nanotubes can be manufactured at a large scale using the electric-arc technique (Journet6644). This technique involves creating an arc between two electrodes in a reactor under a helium atmosphere and using a mixture of a metallic catalyst and graphite powder in the anode. Yields of 80% of entangled carbon filaments can be achieved, which consist of smaller aligned SWNTs self-organized into bundle-like crystallites (Journet6644). Additionally, carbon nanotubes can be synthesized and self-assembled using various methods such as DNA-mediated self-assembly, nanoparticle-assisted alignment, chemical self-assembly, and electro-addressed functionalization (Tulevski2007). These methods have been used to fabricate large-area nanostructured arrays, high-density integration, and freestanding networks (Tulevski2007). 98% semiconducting CNT network solution can also be used and is separated from metallic nanotubes using a density gradient ultracentrifugation approach (Chen2014). The substrate is incubated in the solution and then rinsed with deionized water and dried with N2 air gun, leaving a uniform carbon network (Chen2014). +## What is PaperQA2 -### References +PaperQA2 is engineered to be the best RAG model for working with scientific papers. Here are some features: -Journet6644: Journet, Catherine, et al. "Large-scale production of single-walled carbon nanotubes by the electric-arc technique." nature 388.6644 (1997): 756-758. +- A simple interface to get good answers with grounded responses containing in-text citations. +- State-of-the-art implementation including document metadata-awareness + in embeddings and LLM-based re-ranking and contextual summarization (RCS). +- Support for agentic RAG, where a language agent can iteratively refine queries and answers. +- Automatic redundant fetching of paper metadata, + including citation and journal quality data from multiple providers. +- A usable full-text search engine for a local repository of PDF/text files. +- A robust interface for customization, with default support for all [LiteLLM][LiteLLM providers] models. -Tulevski2007: Tulevski, George S., et al. "Chemically assisted directed assembly of carbon nanotubes for the fabrication of large-scale device arrays." Journal of the American Chemical Society 129.39 (2007): 11964-11968. +[LiteLLM providers]: https://docs.litellm.ai/docs/providers +[LiteLLM general docs]: https://docs.litellm.ai/docs/ -Chen2014: Chen, Haitian, et al. "Large-scale complementary macroelectronics using hybrid integration of carbon nanotubes and IGZO thin-film transistors." Nature communications 5.1 (2014): 4097. +By default, it uses [OpenAI embeddings](https://platform.openai.com/docs/guides/embeddings) and [models](https://platform.openai.com/docs/models) with a Numpy vector DB to embed and search documents. However, you can easily use other closed-source, open-source models or embeddings (see details below). -## What's New? +PaperQA2 depends on some awesome libraries/APIs that make our repo possible. Here are some in a random order: -Version 4 removed langchain from the package because it no longer supports pickling. This also simplifies the package a bit - especially prompts. Langchain can still be used, but it's not required. You can use any LLMs from langchain, but you will need to use the `LangchainLLMModel` class to wrap the model. +1. [Semantic Scholar](https://www.semanticscholar.org/) +2. [Crossref](https://www.crossref.org/) +3. [Unpaywall](https://unpaywall.org/) +4. [Pydantic](https://docs.pydantic.dev/latest/) +5. [tantivy](https://github.com/quickwit-oss/tantivy) +6. [LiteLLM][LiteLLM general docs] +7. [pybtex](https://pybtex.org/) +8. [PyMuPDF](https://pymupdf.readthedocs.io/en/latest/) ## Install -Install with pip: +You can install PaperQA2 via pip: ```bash pip install paper-qa ``` -You need to have an LLM to use paper-qa. You can use OpenAI, llama.cpp (via Server), or any LLMs from langchain. OpenAI just works, as long as you have set your OpenAI API key (`export OPENAI_API_KEY=sk-...`). See instructions below for other LLMs. +PaperQA2 uses an LLM to operate, +so you'll need to either set an appropriate [API key environment variable][LiteLLM providers] (i.e. `export OPENAI_API_KEY=sk-...`) +or set up an open source LLM server (i.e. using [llamafile](https://github.com/Mozilla-Ocho/llamafile). +Any LiteLLM compatible model can be configured to use with PaperQA2. + +If you need to index a large set of papers (100+), +you will likely want an API key for both [Crossref](https://www.crossref.org/documentation/metadata-plus/metadata-plus-keys/) and [Semantic Scholar](https://www.semanticscholar.org/product/api#api-key), +which will allow you to avoid hitting public rate limits using these metadata services. +Those can be exported as `CROSSREF_API_KEY` and `SEMANTIC_SCHOLAR_API_KEY` variables. + +## PaperQA2 vs PaperQA + +We've been working on hard on fundamental upgrades for a while and mostly followed [SemVer](https://semver.org/). +meaning we've incremented the major version number on each breaking change. +This brings us to the current major version number v5. +So why call is the repo now called PaperQA2? +We wanted to remark on the fact though that we've exceeded human performance on [many important metrics](https://paper.wikicrow.ai). +So we arbitrarily call version 5 and onward PaperQA2, +and versions before it as PaperQA1 to denote the significant change in performance. +We recognize that we are challenged at naming and counting at FutureHouse, +so we reserve the right at any time to arbitrarily change the name to PaperCrow. + +## What's New in Version 5 (aka PaperQA2)? + +Version 5 added: + +- A CLI `pqa` +- Agentic workflows invoking tools for + paper search, gathering evidence, and generating an answer +- Removed much of the statefulness from the `Docs` object +- A migration to LiteLLM for compatibility with many LLM providers + as well as centralized rate limits and cost tracking +- A bundled set of configurations (read [here](#bundled-settings))) + containing known-good hyperparameters + +Note that `Docs` objects pickled from prior versions of `PaperQA` are incompatible with version 5 and will need to be rebuilt. +Also, our minimum Python version is now Python 3.11. ## Usage -To use paper-qa, you need to have a list of paths/files/urls (valid extensions include: .pdf, .txt). You can then use the `Docs` class to add the documents and then query them. `Docs` will try to guess citation formats from the content of the files, but you can also provide them yourself. +To understand PaperQA2, let's start with the pieces of the underlying algorithm. The default workflow of PaperQA2 is as follows: -```python -from paperqa import Docs +| Phase | PaperQA2 Actions | +| ---------------------- | ------------------------------------------------------------------------- | +| **1. Paper Search** | - Get candidate papers from LLM-generated keyword query | +| | - Chunk, embed, and add candidate papers to state | +| **2. Gather Evidence** | - Embed query into vector | +| | - Rank top _k_ document chunks in current state | +| | - Create scored summary of each chunk in the context of the current query | +| | - Use LLM to re-score and select most relevant summaries | +| **3. Generate Answer** | - Put best summaries into prompt with context | +| | - Generate answer with prompt | -my_docs = ... # get a list of paths +The tools can be invoked in any order by a language agent. +For example, an LLM agent might do a narrow and broad search, +or using different phrasing for the gather evidence step from the generate answer step. -docs = Docs() -for d in my_docs: - docs.add(d) +### CLI -answer = docs.query( - "What manufacturing challenges are unique to bispecific antibodies?" -) -print(answer.formatted_answer) +The fastest way to test PaperQA2 is via the CLI. First navigate to a directory with some papers and use the `pqa` cli: + +```bash +$ pqa ask 'What manufacturing challenges are unique to bispecific antibodies?' ``` -The answer object has the following attributes: `formatted_answer`, `answer` (answer alone), `question` , and `context` (the summaries of passages found for answer). +You will see PaperQA2 index your local PDF files, gathering the necessary metadata for each of them (using [Crossref](https://www.crossref.org/) and [Semantic Scholar](https://www.semanticscholar.org/)), +search over that index, then break the files into chunked evidence contexts, rank them, and ultimately generate an answer. The next time this directory is queried, your index will already be built (save for any differences detected, like new added papers), so it will skip the indexing and chunking steps. -### Async +All prior answers will be indexed and stored, you can view them by querying via the `search` subcommand, or access them yourself in your `PQA_HOME` directory, which defaults to `~/.pqa/`. -paper-qa is written to be used asynchronously. The synchronous API is just a wrapper around the async. Here are the methods and their async equivalents: +```bash +$ pqa search -i 'answers' 'antibodies' +``` -| Sync | Async | -| ------------------- | -------------------- | -| `Docs.add` | `Docs.aadd` | -| `Docs.add_file` | `Docs.aadd_file` | -| `Docs.add_url` | `Docs.aadd_url` | -| `Docs.get_evidence` | `Docs.aget_evidence` | -| `Docs.query` | `Docs.aquery` | +PaperQA2 is highly configurable, when running from the command line, `pqa --help` shows all options and short descriptions. For example to run with a higher temperature: -The synchronous version just call the async version in a loop. Most modern python environments support async natively (including Jupyter notebooks!). So you can do this in a Jupyter Notebook: +```bash +$ pqa --temperature 0.5 ask 'What manufacturing challenges are unique to bispecific antibodies?' +``` -```python -from paperqa import Docs +You can view all settings with `pqa view`. Another useful thing is to change to other templated settings - for example `fast` is a setting that answers more quickly and you can see it with `pqa -s fast view` -my_docs = ... # get a list of paths +Maybe you have some new settings you want to save? You can do that with -docs = Docs() -for d in my_docs: - await docs.aadd(d) +```bash +pqa -s my_new_settings --temperature 0.5 --llm foo-bar-5 save +``` -answer = await docs.aquery( - "What manufacturing challenges are unique to bispecific antibodies?" -) +and then you can use it with + +```bash +pqa -s my_new_settings ask 'What manufacturing challenges are unique to bispecific antibodies?' +``` + +If you run `pqa` with a command which requires a new indexing, say if you change the default chunk_size, a new index will automatically be created for you. + +```bash +pqa --parsing.chunk_size 5000 ask 'What manufacturing challenges are unique to bispecific antibodies?' ``` -### Adding Documents +You can also use `pqa` to do full-text search with use of LLMs view the search command. For example, let's save the index from a directory and give it a name: -`add` will add from paths. You can also use `add_file` (expects a file object) or `add_url` to work with other sources. +```bash +pqa -i nanomaterials index +``` -### Choosing Model +Now I can search for papers about thermoelectrics: + +```bash +pqa -i nanomaterials search thermoelectrics +``` + +or I can use the normal ask + +```bash +pqa -i nanomaterials ask 'Are there nm scale features in thermoelectric materials?' +``` + +Both the CLI and module have pre-configured settings based on prior performance and our publications, they can be invoked as follows: + +```bash +pqa --settings ask 'Are there nm scale features in thermoelectric materials?' +``` -By default, it uses OpenAI models with a hybrid of `gpt-4o-mini` (for the re-ranking and summary step, `summary_llm` argument) and `gpt-4-turbo` (for the answering step, `llm` argument). You can adjust this: +#### Bundled Settings + +Inside [`paperqa/configs`](paperqa/configs) we bundle known useful settings: + +| Setting Name | Description | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------- | +| high_quality | Highly performant, relatively expensive (due to having `evidence_k` = 15) query using a `ToolSelector` agent. | +| fast | Setting to get answers cheaply and quickly. | +| wikicrow | Setting to emulate the Wikipedia article writing used in our WikiCrow publication. | +| contracrow | Setting to find contradictions in papers, your query should be a claim that needs to be flagged as a contradiction (or not). | +| debug | Setting useful solely for debugging, but not in any actual application beyond debugging. | + +### Module Usage + +PaperQA2's full workflow can be accessed via Python directly: ```python -docs = Docs(llm="gpt-4o-mini", summary_llm="gpt-4o") +from paperqa import Settings, ask + +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings(temperature=0.5), +) ``` -You can use Anthropic models by specifying an Anthropic client: +The answer object has the following attributes: `formatted_answer`, `answer` (answer alone), `question` , and `context` (the summaries of passages found for answer). `ask` will use the `SearchPapers` tool, which will query a local index of files, you can specify this location via the `Settings` object: ```python -from paperqa import Docs -from anthropic import AsyncAnthropic +from paperqa import Settings, ask -docs = Docs( - llm="claude-3-5-sonnet-20240620", - summary_llm="claude-3-5-sonnet-20240620", - client=AsyncAnthropic(), +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings(temperature=0.5, paper_directory="my_papers"), ) ``` -Or you can use any other model available in [langchain](https://github.com/hwchase17/langchain): +`ask` is just a convenience wrapper around the real entrypoint, which can be accessed if you'd like to run concurrent asynchronous workloads: ```python -from paperqa import Docs -from langchain_community.chat_models import ChatAnthropic +from paperqa import Settings, agent_query, QueryRequest -docs = Docs(llm="langchain", client=ChatAnthropic()) +answer = await agent_query( + QueryRequest( + query="What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings(temperature=0.5, paper_directory="my_papers"), + ) +) ``` -Note we split the model into the wrapper and `client`, which is `ChatAnthropic` here. This is because `client` stores the non-pickleable part and langchain LLMs are only sometimes serializable/pickleable. The paper-qa `Docs` must always serializable. Thus, we split the model into two parts. +The default agent will use an LLM based agent, +but you can also specify a `"fake"` agent to use a hard coded call path of search -> gather evidence -> answer to reduce token usage. + +### Adding Documents Manually + +If you prefer fine grained control, and you wish to add objects to the docs object yourself (rather than using the search tool), then the previously existing `Docs` object interface can be used: ```python -import pickle +from paperqa import Docs, Settings + +# valid extensions include .pdf, .txt, and .html +doc_paths = ("myfile.pdf", "myotherfile.pdf") + +docs = Docs() + +for doc in doc_paths: + doc.add(doc_paths) + +settings = Settings() +settings.llm = "claude-3-5-sonnet-20240620" +settings.answer.answer_max_sources = 3 + +answer = docs.query( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=settings, +) -docs = Docs(llm="langchain", client=ChatAnthropic()) -model_str = pickle.dumps(docs) -docs = pickle.loads(model_str) -# but you have to set the client after loading -docs.set_client(ChatAnthropic()) +print(answer.formatted_answer) ``` -We also support using [Anyscale](https://docs.anyscale.com/examples/work-with-openai/#call-the-openai-endpoint-with-an-openai-api-key) to utilized hosted open source models. To use it, you only need to set your `ANYSCALE_API_KEY` and `ANYSCALE_BASE_URL` environment variables or use an OpenAI client initialized with your `api_key` and `base_url` arguments from Anyscale. +### Async -#### Locally Hosted +PaperQA2 is written to be used asynchronously. The synchronous API is just a wrapper around the async. Here are the methods and their async equivalents: -You can use llama.cpp to be the LLM. Note that you should be using relatively large models, because paper-qa requires following a lot of instructions. You won't get good performance with 7B models. +| Sync | Async | +| ------------------- | -------------------- | +| `Docs.add` | `Docs.aadd` | +| `Docs.add_file` | `Docs.aadd_file` | +| `Docs.add_url` | `Docs.aadd_url` | +| `Docs.get_evidence` | `Docs.aget_evidence` | +| `Docs.query` | `Docs.aquery` | -The easiest way to get set-up is to download a [llama file](https://github.com/Mozilla-Ocho/llamafile) and execute it with `-cb -np 4 -a my-llm-model --embedding` which will enable continuous batching and embeddings. +The synchronous version just call the async version in a loop. Most modern python environments support async natively (including Jupyter notebooks!). So you can do this in a Jupyter Notebook: ```python -from paperqa import Docs, LlamaEmbeddingModel -from openai import AsyncOpenAI +from paperqa import Docs + +# valid extensions include .pdf, .txt, and .html +doc_paths = ("myfile.pdf", "myotherfile.pdf") -# start llamap.cpp client with +docs = Docs() + +for doc in doc_paths: + await doc.aadd(doc_paths) -local_client = AsyncOpenAI( - base_url="http://localhost:8080/v1", api_key="sk-no-key-required" +answer = await docs.aquery( + "What manufacturing challenges are unique to bispecific antibodies?" ) -docs = Docs( - client=local_client, - docs_index=NumpyVectorStore(embedding_model=LlamaEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=LlamaEmbeddingModel()), - llm_model=OpenAILLMModel( - config=dict( - model="my-llm-model", temperature=0.1, frequency_penalty=1.5, max_tokens=512 - ) +print(answer.formatted_answer) +``` + +### Choosing Model + +By default, it uses OpenAI models with `gpt-4o-2024-08-06` for both the re-ranking and summary step, the `summary_llm` setting, and for the answering step, the `llm` setting. You can adjust this easily: + +```python +from paperqa import Settings, ask + +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings( + llm="gpt-4o-mini", summary_llm="gpt-4o-mini", paper_directory="my_papers" ), ) ``` -### Changing Embedding Model - -paper-qa defaults to using OpenAI (`text-embedding-3-small`) embeddings, but has flexible options for both vector stores and embedding choices. The simplest way to change an embedding is via the `embedding` argument to the `Docs` object constructor: +You can use Anthropic or any other model supported by `litellm`: ```python -from paperqa import Docs +from paperqa import Settings, ask -docs = Docs(embedding="text-embedding-3-large") +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings( + llm="claude-3-5-sonnet-20240620", summary_llm="claude-3-5-sonnet-20240620" + ), +) ``` -`embedding` accepts: +#### Locally Hosted -- Any OpenAI embedding model name -- [VoyageAI](https://docs.voyageai.com/docs/embeddings) model names (usable if `voyageai` is installed and `VOYAGE_API_KEY` is set) -- `"sentence-transformers"` to use `multi-qa-MiniLM-L6-cos-v1` via [Sentence Transformers](https://huggingface.co/sentence-transformers) -- `"hybrid-"` i.e. `"hybrid-text-embedding-3-small"` to use a hybrid sparse keyword (based on a token modulo embedding) and dense vector embedding, any OpenAI or VoyageAI model can be used in the dense model name -- `"sparse"` to use a sparse keyword embedding only +You can use llama.cpp to be the LLM. Note that you should be using relatively large models, because PaperQA2 requires following a lot of instructions. You won't get good performance with 7B models. -For deeper embedding customization, embedding models and vector stores can be built separately and passed into the `Docs` object. Embedding models are used to create both paper-qa's index of document citation embedding vectors (`docs_index` argument) as well as the full-text embedding vectors (`texts_index` argument). They can both be specified as arguments when you create a new `Docs` object. You can use use any embedding model which implements paper-qa's `EmbeddingModel` class. For example, to use `text-embedding-3-large`: +The easiest way to get set-up is to download a [llama file](https://github.com/Mozilla-Ocho/llamafile) and execute it with `-cb -np 4 -a my-llm-model --embedding` which will enable continuous batching and embeddings. ```python -from paperqa import Docs, NumpyVectorStore, OpenAIEmbeddingModel +from paperqa import Settings, ask + +local_llm_config = dict( + model_list=dict( + model_name="my_llm_model", + litellm_params=dict( + model="my-llm-model", + api_base="http://localhost:8080/v1", + api_key="sk-no-key-required", + temperature=0.1, + frequency_penalty=1.5, + max_tokens=512, + ), + ) +) -docs = Docs( - docs_index=NumpyVectorStore( - embedding_model=OpenAIEmbeddingModel(name="text-embedding-3-large") - ), - texts_index=NumpyVectorStore( - embedding_model=OpenAIEmbeddingModel(name="text-embedding-3-large") +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings( + llm="my-llm-model", + llm_config=local_llm_config, + summary_llm="my-llm-model", + summary_llm_config=local_llm_config, ), ) ``` -Note that embedding models are specified as attributes of paper-qa's `VectorStore` base class. `NumpyVectorStore` is the best place to start, it's a simple in-memory store, without an index. If a larger-than-memory vector store is needed, you can use the `LangchainVectorStore` like this: +### Changing Embedding Model + +PaperQA2 defaults to using OpenAI (`text-embedding-3-small`) embeddings, but has flexible options for both vector stores and embedding choices. The simplest way to change an embedding is via the `embedding` argument to the `Settings` object constructor: ```python -from langchain_community.vectorstores.faiss import FAISS -from langchain_openai import OpenAIEmbeddings -from paperqa import Docs, LangchainVectorStore +from paperqa import Settings, ask -docs = Docs( - docs_index=LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()), - texts_index=LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()), +answer = ask( + "What manufacturing challenges are unique to bispecific antibodies?", + settings=Settings(embedding="text-embedding-3-large"), ) ``` -We support both local langchain embedding models and the [SentenceTransformer](https://www.sbert.net/) models. For example: +`embedding` accepts any embedding model name supported by litellm. PaperQA2 also supports an embedding input of `"hybrid-"` i.e. `"hybrid-text-embedding-3-small"` to use a hybrid sparse keyword (based on a token modulo embedding) and dense vector embedding, where any litellm model can be used in the dense model name. `"sparse"` can be used to use a sparse keyword embedding only. + +Embedding models are used to create PaperQA2's index of the full-text embedding vectors (`texts_index` argument). The embedding model can be specified as a setting when you are adding new papers to the `Docs` object: ```python -from paperqa import Docs, SentenceTransformerEmbeddingModel -from openai import AsyncOpenAI +from paperqa import Docs, Settings -# start llamap.cpp client with +doc_paths = ("myfile.pdf", "myotherfile.pdf") -local_client = AsyncOpenAI( - base_url="http://localhost:8080/v1", api_key="sk-no-key-required" -) +docs = Docs() -docs = Docs( - client=local_client, - docs_index=NumpyVectorStore(embedding_model=SentenceTransformerEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=SentenceTransformerEmbeddingModel()), - llm_model=OpenAILLMModel( - config=dict( - model="my-llm-model", temperature=0.1, frequency_penalty=1.5, max_tokens=512 - ) - ), -) +for doc in doc_paths: + doc.add(doc_paths, Settings(embedding="text-embedding-large-3")) ``` -We also support hybrid keyword (sparse token modulo vectors) and dense embedding vectors. They can be specified as follows: +Note that PaperQA2 uses Numpy as a dense vector store. +Its design of using a keyword search initially reduces the number of chunks needed for each answer to a relatively small number < 1k. +Therefore, `NumpyVectorStore` is a good place to start, it's a simple in-memory store, without an index. +However, if a larger-than-memory vector store is needed, we are currently lacking here. + +The hybrid embeddings can be customized: ```python -from paperqa import Docs, HybridEmbeddingModel, SparseEmbeddingModel, NumpyVectorStore +from paperqa import ( + Docs, + HybridEmbeddingModel, + SparseEmbeddingModel, + LiteLLMEmbeddingModel, +) -model = HybridEmbeddingModel(models=[OpenAIEmbeddingModel(), SparseEmbeddingModel()]) -docs = Docs( - docs_index=NumpyVectorStore(embedding_model=model), - texts_index=NumpyVectorStore(embedding_model=model), + +doc_paths = ("myfile.pdf", "myotherfile.pdf") + +model = HybridEmbeddingModel( + models=[LiteLLMEmbeddingModel(), SparseEmbeddingModel(ndim=1024)] ) +docs = Docs() +for doc in doc_paths: + doc.add(doc_paths, embedding_model=model) ``` The sparse embedding (keyword) models default to having 256 dimensions, but this can be specified via the `ndim` argument. @@ -272,10 +420,15 @@ The sparse embedding (keyword) models default to having 256 dimensions, but this You can adjust the numbers of sources (passages of text) to reduce token usage or add more context. `k` refers to the top k most relevant and diverse (may from different sources) passages. Each passage is sent to the LLM to summarize, or determine if it is irrelevant. After this step, a limit of `max_sources` is applied so that the final answer can fit into the LLM context window. Thus, `k` > `max_sources` and `max_sources` is the number of sources used in the final answer. ```python +from paperqa import Settings + +settings = Settings() +settings.answer.answer_max_sources = 3 +settings.answer.k = 5 + docs.query( "What manufacturing challenges are unique to bispecific antibodies?", - k=5, - max_sources=2, + settings=settings, ) ``` @@ -285,6 +438,8 @@ You do not need to use papers -- you can use code or raw HTML. Note that this to ```python import glob +import os +from paperqa import Docs source_files = glob.glob("**/*.js") @@ -301,6 +456,8 @@ print(answer) You may want to cache parsed texts and embeddings in an external database or file. You can then build a Docs object from those directly: ```python +from paperqa import Docs, Doc, Text + docs = Docs() for ... in my_docs: @@ -309,36 +466,25 @@ for ... in my_docs: docs.add_texts(texts, doc) ``` -If you want to use an external vector store, you can also do that directly via langchain. For example, to use the [FAISS](https://ai.meta.com/tools/faiss/) vector store from langchain: - -```python -from paperqa import LangchainVectorStore, Docs -from langchain_community.vector_store import FAISS -from langchain_openai import OpenAIEmbeddings - -docs = Docs( - texts_index=LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()), - docs_index=LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()), -) -``` - ## Where do I get papers? Well that's a really good question! It's probably best to just download PDFs of papers you think will help answer your question and start from there. ### Zotero +_It's been a while since we've tested this - so let us know if it runs into issues!_ + If you use [Zotero](https://www.zotero.org/) to organize your personal bibliography, you can use the `paperqa.contrib.ZoteroDB` to query papers from your library, which relies on [pyzotero](https://github.com/urschrei/pyzotero). -Install `pyzotero` to use this feature: +Install `pyzotero` via the `zotero` extra for this feature: ```bash -pip install pyzotero +pip install paperqa[zotero] ``` -First, note that `paperqa` parses the PDFs of papers to store in the database, +First, note that PaperQA2 parses the PDFs of papers to store in the database, so all relevant papers should have PDFs stored inside your database. You can get Zotero to automatically do this by highlighting the references you wish to retrieve, right clicking, and selecting _"Find Available PDFs"_. @@ -352,12 +498,13 @@ To download papers, you need to get an API key for your account. 2. Create a new API key [here](https://www.zotero.org/settings/keys/new) and set it as the environment variable `ZOTERO_API_KEY`. - The key will need read access to the library. -With this, we can download papers from our library and add them to `paperqa`: +With this, we can download papers from our library and add them to PaperQA2: ```python +from paperqa import Docs from paperqa.contrib import ZoteroDB -docs = paperqa.Docs() +docs = Docs() zotero = ZoteroDB(library_type="user") # "group" if group library for item in zotero.iterate(limit=20): @@ -391,9 +538,11 @@ If you want to search for papers outside of your own collection, I've found an u like it might help. But beware, this project looks like it uses some scraping tools that may violate publisher's rights or be in a gray area of legality. ```python +from paperqa import Docs + keyword_search = "bispecific antibody manufacture" papers = paperscraper.search_papers(keyword_search) -docs = paperqa.Docs() +docs = Docs() for path, data in papers.items(): try: docs.add(path) @@ -406,45 +555,37 @@ answer = docs.query( print(answer) ``` -## PDF Reading Options +## Callbacks -By default [PyPDF](https://pypi.org/project/pypdf/) is used since it's pure python and easy to install. For faster PDF reading, paper-qa will detect and use [PymuPDF (fitz)](https://pymupdf.readthedocs.io/en/latest/): - -```sh -pip install pymupdf -``` - -## Callbacks Factory - -To execute a function on each chunk of LLM completions, you need to provide a function that when called with the name of the step produces a list of functions to execute on each chunk. For example, to get a typewriter view of the completions, you can do: +To execute a function on each chunk of LLM completions, you need to provide a function that can be executed on each chunk. For example, to get a typewriter view of the completions, you can do: ```python -def make_typewriter(step_name): - def typewriter(chunk): - print(chunk, end="") +def typewriter(chunk: str) -> None: + print(chunk, end="") - return [typewriter] # <- note that this is a list of functions +docs = Docs() + +# add some docs... -... docs.query( "What manufacturing challenges are unique to bispecific antibodies?", - get_callbacks=make_typewriter, + callbacks=[typewriter], ) ``` ### Caching Embeddings -In general, embeddings are cached when you pickle a `Docs` regardless of what vector store you use. See above for details on more explicit management of them. +In general, embeddings are cached when you pickle a `Docs` regardless of what vector store you use. So as long as you save your underlying `Docs` object, you should be able to avoid re-embedding your documents. ## Customizing Prompts -You can customize any of the prompts, using the `PromptCollection` class. For example, if you want to change the prompt for the question, you can do: +You can customize any of the prompts using settings. ```python -from paperqa import Docs, Answer, PromptCollection +from paperqa import Docs, Settings -my_qaprompt = ( +my_qa_prompt = ( "Answer the question '{question}' " "Use the context below if helpful. " "You can cite the context using the key " @@ -453,8 +594,14 @@ my_qaprompt = ( "about how you cannot answer.\n\n" "Context: {context}\n\n" ) -prompts = PromptCollection(qa=my_qaprompt) -docs = Docs(prompts=prompts) + +docs = Docs() +settings = Settings() +settings.prompts.qa = my_qa_prompt +docs.query( + "Are covid-19 vaccines effective?", + settings=settings, +) ``` ### Pre and Post Prompts @@ -466,11 +613,11 @@ are executed after the query and before the query. For example, you can use this ### How is this different from LlamaIndex? -It's not that different! This is similar to the tree response method in LlamaIndex. I just have included some prompts I find useful, readers that give page numbers/line numbers, and am focused on one task - answering technical questions with cited sources. +It's not that different! This is similar to the tree response method in LlamaIndex. We also support agentic workflows and local indexes for easier operations with the scientific literature. Another big difference is our strong focus on scientific papers and their underlying metadata. ### How is this different from LangChain? -There has been some great work on retrievers in langchain and you could say this is an example of a retriever with an LLM-based re-ranking and contextual summary. +There has been some great work on retrievers in LangChain, and you could say this is an example of a retriever with an LLM-based re-ranking and contextual summary. Another big difference is our strong focus on scientific papers and their underlying metadata. ### Can I save or load? @@ -486,6 +633,35 @@ with open("my_docs.pkl", "wb") as f: # load with open("my_docs.pkl", "rb") as f: docs = pickle.load(f) +``` + +## Citation -docs.set_client() # defaults to OpenAI +Please read and cite the following papers if you use this software: + +```bibtex +@article{skarlinski2024language, + title={Language agents achieve superhuman synthesis of scientific knowledge}, + author={ + Michael D. Skarlinski and + Sam Cox and + Jon M. Laurent and + James D. Braza and + Michaela Hinks and + Michael J. Hammerling and + Manvitha Ponnapati and + Samuel G. Rodriques and + Andrew D. White}, + year={2024}, + journal={preprint}, + url={https://paper.wikicrow.ai} +} + + +@article{lala2023paperqa, + title={PaperQA: Retrieval-Augmented Generative Agent for Scientific Research}, + author={L{\'a}la, Jakub and O'Donoghue, Odhran and Shtedritski, Aleksandar and Cox, Sam and Rodriques, Samuel G and White, Andrew D}, + journal={arXiv preprint arXiv:2312.07559}, + year={2023} +} ``` diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index a2f226af..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -anthropic -faiss-cpu -langchain-community -langchain-openai -pymupdf -python-dotenv -pyzotero -requests -sentence_transformers -voyageai - -# Code QA dependencies -build -mypy -pre-commit -pytest -pytest-asyncio -pytest-sugar -pytest-vcr -pytest-timer -types-requests -types-setuptools diff --git a/paperqa/__init__.py b/paperqa/__init__.py index d4291b30..534096bb 100644 --- a/paperqa/__init__.py +++ b/paperqa/__init__.py @@ -1,29 +1,32 @@ -from .docs import Answer, Context, Doc, Docs, PromptCollection, Text, print_callback -from .llms import ( - AnthropicLLMModel, +import warnings + +# TODO: remove after refactoring all models to avoid using _* private vars +warnings.filterwarnings( + "ignore", message="Valid config keys have changed in V2:", module="pydantic" +) + + +from paperqa.agents import ask # noqa: E402 +from paperqa.agents.main import agent_query # noqa: E402 +from paperqa.agents.models import QueryRequest # noqa: E402 +from paperqa.docs import Answer, Docs, print_callback # noqa: E402 +from paperqa.llms import ( # noqa: E402 EmbeddingModel, HybridEmbeddingModel, - LangchainEmbeddingModel, - LangchainLLMModel, - LangchainVectorStore, - LlamaEmbeddingModel, + LiteLLMEmbeddingModel, + LiteLLMModel, LLMModel, LLMResult, NumpyVectorStore, - OpenAIEmbeddingModel, - OpenAILLMModel, - SentenceTransformerEmbeddingModel, SparseEmbeddingModel, embedding_model_factory, - llm_model_factory, - vector_store_factory, ) -from .types import DocDetails -from .version import __version__ +from paperqa.settings import Settings, get_settings # noqa: E402 +from paperqa.types import Context, Doc, DocDetails, Text # noqa: E402 +from paperqa.version import __version__ # noqa: E402 __all__ = [ "Answer", - "AnthropicLLMModel", "Context", "Doc", "DocDetails", @@ -32,20 +35,17 @@ "HybridEmbeddingModel", "LLMModel", "LLMResult", - "LangchainEmbeddingModel", - "LangchainLLMModel", - "LangchainVectorStore", - "LlamaEmbeddingModel", + "LiteLLMEmbeddingModel", + "LiteLLMModel", "NumpyVectorStore", - "OpenAIEmbeddingModel", - "OpenAILLMModel", - "PromptCollection", - "SentenceTransformerEmbeddingModel", + "QueryRequest", + "Settings", "SparseEmbeddingModel", "Text", "__version__", + "agent_query", + "ask", "embedding_model_factory", - "llm_model_factory", + "get_settings", "print_callback", - "vector_store_factory", ] diff --git a/paperqa/agents/__init__.py b/paperqa/agents/__init__.py new file mode 100644 index 00000000..be31fd96 --- /dev/null +++ b/paperqa/agents/__init__.py @@ -0,0 +1,238 @@ +from __future__ import annotations + +import argparse +import logging +import os +from typing import Any + +from pydantic_settings import CliSettingsSource +from rich.console import Console +from rich.logging import RichHandler + +from paperqa.settings import Settings +from paperqa.utils import get_loop, pqa_directory, setup_default_logs +from paperqa.version import __version__ + +from .main import agent_query, index_search +from .models import AnswerResponse, QueryRequest +from .search import SearchIndex, get_directory_index + +logger = logging.getLogger(__name__) + + +def configure_cli_logging(verbosity: int = 0) -> None: + """Suppress loquacious loggers according to verbosity level.""" + setup_default_logs() + + verbosity_map = { + 0: { + "paperqa.agents": logging.INFO, + "paperqa.agents.helpers": logging.WARNING, + "paperqa.agents.main": logging.WARNING, + "paperqa.agents.main.agent_callers": logging.INFO, + "anthropic": logging.WARNING, + "openai": logging.WARNING, + "httpx": logging.WARNING, + "paperqa.agents.models": logging.WARNING, + "paperqa.agents.search": logging.INFO, + "litellm": logging.WARNING, + "LiteLLM Router": logging.WARNING, + "LiteLLM Proxy": logging.WARNING, + } + } + + verbosity_map[1] = verbosity_map[0] | { + "paperqa.agents.main": logging.INFO, + "paperqa.models": logging.INFO, + } + + verbosity_map[2] = verbosity_map[1] | { + "paperqa.agents.helpers": logging.DEBUG, + "paperqa.agents.main": logging.DEBUG, + "paperqa.agents.main.agent_callers": logging.DEBUG, + "paperqa.models": logging.DEBUG, + "paperqa.agents.search": logging.DEBUG, + "litellm": logging.INFO, + "LiteLLM Router": logging.INFO, + "LiteLLM Proxy": logging.INFO, + } + + verbosity_map[3] = verbosity_map[2] | { + "litellm": logging.DEBUG, # <-- every single LLM call + } + + rich_handler = RichHandler( + rich_tracebacks=True, + markup=True, + show_path=False, + show_level=False, + console=Console(force_terminal=True), + ) + + rich_handler.setFormatter(logging.Formatter("%(message)s", datefmt="[%X]")) + + module_logger = logging.getLogger(__name__.split(".", maxsplit=1)[0]) + + if not any(isinstance(h, RichHandler) for h in module_logger.handlers): + module_logger.addHandler(rich_handler) + + for logger_name, logger_ in logging.Logger.manager.loggerDict.items(): + if isinstance(logger_, logging.Logger) and ( + log_level := verbosity_map.get(min(verbosity, 2), {}).get(logger_name) + ): + logger_.setLevel(log_level) + + if verbosity > 0: + print(f"PaperQA version: {__version__}") + + +def ask(query: str, settings: Settings) -> AnswerResponse: + """Query PaperQA via an agent.""" + configure_cli_logging(verbosity=settings.verbosity) + return get_loop().run_until_complete( + agent_query( + QueryRequest(query=query, settings=settings), + verbosity=settings.verbosity, + agent_type=settings.agent.agent_type, + ) + ) + + +def search_query( + query: str, + index_name: str, + settings: Settings, +) -> list[tuple[AnswerResponse, str] | tuple[Any, str]]: + """Search using a pre-built PaperQA index.""" + configure_cli_logging(verbosity=settings.verbosity) + if index_name == "default": + index_name = settings.get_index_name() + return get_loop().run_until_complete( + index_search( + query, + index_name=index_name, + index_directory=settings.index_directory, + ) + ) + + +def build_index( + index_name: str, + directory: str | os.PathLike, + settings: Settings, +) -> SearchIndex: + """Build a PaperQA search index, this will also happen automatically upon using `ask`.""" + if index_name == "default": + index_name = settings.get_index_name() + configure_cli_logging(verbosity=settings.verbosity) + settings.paper_directory = directory + return get_loop().run_until_complete( + get_directory_index(index_name=index_name, settings=settings) + ) + + +def save_settings( + settings: Settings, + settings_path: str | os.PathLike, +) -> None: + """Save the settings to a file.""" + configure_cli_logging(verbosity=settings.verbosity) + # check if this could be interpreted at an absolute path + if os.path.isabs(settings_path): + full_settings_path = os.path.expanduser(settings_path) + else: + full_settings_path = os.path.join(pqa_directory("settings"), settings_path) + if not full_settings_path.endswith(".json"): + full_settings_path += ".json" + + is_overwrite = os.path.exists(full_settings_path) + + with open(full_settings_path, "w") as f: + f.write(settings.model_dump_json(indent=2)) + + if is_overwrite: + logger.info(f"Settings overwritten to: {full_settings_path}") + else: + logger.info(f"Settings saved to: {full_settings_path}") + + +def main() -> None: + parser = argparse.ArgumentParser(description="PaperQA CLI") + + parser.add_argument( + "--settings", + "-s", + default="default", + help=( + "Named settings to use. Will search in local, pqa directory, and package" + " last" + ), + ) + + parser.add_argument( + "--index", "-i", default="default", help="Index name to search or create" + ) + + subparsers = parser.add_subparsers( + title="commands", dest="command", description="Available commands" + ) + + subparsers.add_parser("view", help="View the chosen settings") + save_parser = subparsers.add_parser("save", help="View the chosen settings") + save_parser.add_argument( + "location", help="Location for new settings (name or an absolute path)" + ) + + ask_parser = subparsers.add_parser( + "ask", help="Ask a question of current index (based on settings)" + ) + ask_parser.add_argument("query", help="Question to ask") + + search_parser = subparsers.add_parser( + "search", + help=( + "Search the index specified by --index." + " Pass `--index answers` to search previous answers." + ), + ) + search_parser.add_argument("query", help="Keyword search") + + build_parser = subparsers.add_parser( + "index", help="Build a search index from given directory" + ) + build_parser.add_argument("directory", help="Directory to build index from") + + # Create CliSettingsSource instance + cli_settings = CliSettingsSource[argparse.ArgumentParser]( + Settings, root_parser=parser + ) + + # Now use argparse to parse the remaining arguments + args, remaining_args = parser.parse_known_args() + # Parse arguments using CliSettingsSource + settings = Settings.from_name( + args.settings, cli_source=cli_settings(args=remaining_args) + ) + + match args.command: + case "ask": + ask(args.query, settings) + case "view": + configure_cli_logging(settings.verbosity) + logger.info(f"Viewing: {args.settings}") + logger.info(settings.model_dump_json(indent=2)) + case "save": + save_settings(settings, args.location) + case "search": + search_query(args.query, args.index, settings) + case "index": + build_index(args.index, args.directory, settings) + case _: + commands = ", ".join({"view", "ask", "search", "index"}) + brief_help = f"\nRun with commands: {{{commands}}}\n\n" + brief_help += "For more information, run with --help" + print(brief_help) + + +if __name__ == "__main__": + main() diff --git a/paperqa/agents/env.py b/paperqa/agents/env.py new file mode 100644 index 00000000..6171baf4 --- /dev/null +++ b/paperqa/agents/env.py @@ -0,0 +1,180 @@ +import logging +from typing import cast + +from aviary.env import Environment as _Environment +from aviary.env import Frame +from aviary.message import Message +from aviary.tools import Tool, ToolRequestMessage, ToolResponseMessage + +from paperqa.docs import Docs +from paperqa.llms import EmbeddingModel, LiteLLMModel +from paperqa.settings import Settings +from paperqa.types import Answer, LLMResult +from paperqa.utils import get_year + +from .models import QueryRequest +from .tools import ( + AVAILABLE_TOOL_NAME_TO_CLASS, + EnvironmentState, + GatherEvidence, + GenerateAnswer, + PaperSearch, +) + +logger = logging.getLogger(__name__) + +POPULATE_FROM_SETTINGS = None + + +def settings_to_tools( + settings: Settings, + llm_model: LiteLLMModel | None = POPULATE_FROM_SETTINGS, + summary_llm_model: LiteLLMModel | None = POPULATE_FROM_SETTINGS, + embedding_model: EmbeddingModel | None = POPULATE_FROM_SETTINGS, +) -> list[Tool]: + """ + Convert a Settings into tools, confirming the gen_answer tool is present. + + NOTE: the last element of the return will always be GenerateAnswer. + """ + llm_model = llm_model or settings.get_llm() + summary_llm_model = summary_llm_model or settings.get_summary_llm() + embedding_model = embedding_model or settings.get_embedding_model() + tools: list[Tool] = [] + has_answer_tool = False + for tool_type in ( + (PaperSearch, GatherEvidence, GenerateAnswer) + if settings.agent.tool_names is None + else [ + AVAILABLE_TOOL_NAME_TO_CLASS[name] + for name in set(settings.agent.tool_names) + ] + ): + if issubclass(tool_type, PaperSearch): + tool = Tool.from_function( + PaperSearch( + settings=settings, embedding_model=embedding_model + ).paper_search + ) + for pname in ("min_year", "max_year"): + tool.info.parameters.properties[pname]["description"] = cast( + str, tool.info.parameters.properties[pname]["description"] + ).format(current_year=get_year()) + elif issubclass(tool_type, GatherEvidence): + tool = Tool.from_function( + GatherEvidence( + settings=settings, + summary_llm_model=summary_llm_model, + embedding_model=embedding_model, + ).gather_evidence + ) + elif issubclass(tool_type, GenerateAnswer): + tool = Tool.from_function( + GenerateAnswer( + settings=settings, + llm_model=llm_model, + summary_llm_model=summary_llm_model, + embedding_model=embedding_model, + ).gen_answer + ) + else: + raise NotImplementedError(f"Didn't handle tool type {tool_type}.") + if tool.info.name == GenerateAnswer.gen_answer.__name__: + tools.append(tool) + has_answer_tool = True + else: + tools.insert(0, tool) + if not has_answer_tool: + raise ValueError( + f"{GenerateAnswer.gen_answer.__name__} must be one of the tools." + ) + return tools + + +class Environment(_Environment[EnvironmentState]): + """Environment to connect agents with paper-qa.""" + + def __init__( + self, + query: QueryRequest, + docs: Docs, + llm_model: LiteLLMModel | None = POPULATE_FROM_SETTINGS, + summary_llm_model: LiteLLMModel | None = POPULATE_FROM_SETTINGS, + embedding_model: EmbeddingModel | None = POPULATE_FROM_SETTINGS, + **env_kwargs, + ): + super().__init__(**env_kwargs) + # Hold onto QueryRequest to create fresh tools and answer during each reset + self._query = query + # Hold onto Docs to clear and reuse in state during each reset + self._docs = docs + self._llm_model = llm_model + self._summary_llm_model = summary_llm_model + self._embedding_model = embedding_model + + def make_initial_state_and_tools(self) -> tuple[EnvironmentState, list[Tool]]: + self.state = EnvironmentState( + docs=self._docs, + answer=Answer( + question=self._query.query, + config_md5=self._query.settings.md5, + id=self._query.id, + ), + ) + self.tools = settings_to_tools( + settings=self._query.settings, + llm_model=self._llm_model, + summary_llm_model=self._summary_llm_model, + embedding_model=self._embedding_model, + ) + return self.state, self.tools + + async def reset(self) -> tuple[list[Message], list[Tool]]: + self._docs.clear_docs() + self.state, self.tools = self.make_initial_state_and_tools() + return ( + [ + Message( + content=self._query.settings.agent.agent_prompt.format( + question=self.state.answer.question, + status=self.state.status, + gen_answer_tool_name=GenerateAnswer.TOOL_FN_NAME, + ), + ) + ], + self.tools, + ) + + def export_frame(self) -> Frame: + return Frame(state=self.state, info={"query": self._query}) + + async def step( + self, action: ToolRequestMessage + ) -> tuple[list[Message], float, bool, bool]: + + # add usage for action if it has usage + info = action.info + if info and "usage" in info and "model" in info: + r = LLMResult( + model=info["model"], + prompt_count=info["usage"][0], + completion_count=info["usage"][1], + ) + self.state.answer.add_tokens(r) + + # If the action has empty tool_calls, the agent can later take that into account + msgs = cast( + list[Message], + await self.exec_tool_calls(action, state=self.state, handle_tool_exc=True), + ) + return ( + msgs, + 0, # Reward is computed in post-processing, use 0 as a placeholder + any( + isinstance(msg, ToolResponseMessage) + and msg.name == GenerateAnswer.gen_answer.__name__ + and GenerateAnswer.did_not_fail_to_answer(msg.content) + for msg in msgs + ), + False, + ) diff --git a/paperqa/agents/helpers.py b/paperqa/agents/helpers.py new file mode 100644 index 00000000..650f7c77 --- /dev/null +++ b/paperqa/agents/helpers.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import logging +import re +from datetime import datetime +from typing import cast + +from rich.table import Table + +from paperqa.docs import Docs +from paperqa.llms import LiteLLMModel + +from .models import AnswerResponse + +logger = logging.getLogger(__name__) + + +def get_year(ts: datetime | None = None) -> str: + """Get the year from the input datetime, otherwise using the current datetime.""" + if ts is None: + ts = datetime.now() + return ts.strftime("%Y") + + +async def litellm_get_search_query( + question: str, + count: int, + template: str | None = None, + llm: str = "gpt-4o-mini", + temperature: float = 1.0, +) -> list[str]: + search_prompt = "" + if isinstance(template, str) and all( + x in template for x in ("{count}", "{question}", "{date}") + ): + # partial formatting + search_prompt = template.replace("{date}", get_year()) + elif isinstance(template, str): + logger.warning( + "Template does not contain {count}, {question} and {date} variables." + " Ignoring template and using default search prompt." + ) + if not search_prompt: + search_prompt = ( + "We want to answer the following question: {question}\nProvide" + " {count} unique keyword searches (one search per line) and year ranges" + " that will find papers to help answer the question. Do not use boolean" + " operators. Make sure not to repeat searches without changing the" + " keywords or year ranges. Make some searches broad and some narrow. Use" + " this format: [keyword search], [start year]-[end year]. where end year" + f" is optional. The current year is {get_year()}." + ) + + model = LiteLLMModel(name=llm) + model.config["model_list"][0]["litellm_params"].update({"temperature": temperature}) + result = await model.run_prompt( + prompt=search_prompt, + data={"question": question, "count": count}, + skip_system=True, + ) + search_query = result.text + queries = [s for s in search_query.split("\n") if len(s) > 3] # noqa: PLR2004 + # remove "2.", "3.", etc. -- https://regex101.com/r/W2f7F1/1 + queries = [re.sub(r"^\d+\.\s*", "", q) for q in queries] + # remove quotes + return [re.sub(r'["\[\]]', "", q) for q in queries] + + +def table_formatter( + objects: list[tuple[AnswerResponse | Docs, str]], max_chars_per_column: int = 2000 +) -> Table: + example_object, _ = objects[0] + if isinstance(example_object, AnswerResponse): + table = Table(title="Prior Answers") + table.add_column("Question", style="cyan") + table.add_column("Answer", style="magenta") + for obj, _ in objects: + table.add_row( + cast(AnswerResponse, obj).answer.question[:max_chars_per_column], + cast(AnswerResponse, obj).answer.answer[:max_chars_per_column], + ) + return table + if isinstance(example_object, Docs): + table = Table(title="PDF Search") + table.add_column("Title", style="cyan") + table.add_column("File", style="magenta") + for obj, filename in objects: + try: + display_name = cast(Docs, obj).texts[0].doc.title # type: ignore[attr-defined] + except AttributeError: + display_name = cast(Docs, obj).texts[0].doc.citation + table.add_row(display_name[:max_chars_per_column], filename) + return table + raise NotImplementedError( + f"Object type {type(example_object)} can not be converted to table." + ) diff --git a/paperqa/agents/main.py b/paperqa/agents/main.py new file mode 100644 index 00000000..837ed100 --- /dev/null +++ b/paperqa/agents/main.py @@ -0,0 +1,416 @@ +import asyncio +import logging +import os +from collections.abc import Awaitable, Callable +from pydoc import locate +from typing import TYPE_CHECKING, Any, cast + +from aviary.message import Message +from aviary.tools import ( + Tool, + ToolCall, + ToolRequestMessage, + ToolResponseMessage, + ToolSelector, +) +from pydantic import BaseModel, Field, TypeAdapter + +try: + from ldp.agent import ( + Agent, + HTTPAgentClient, + MemoryAgent, + ReActAgent, + SimpleAgent, + SimpleAgentState, + ) + from ldp.graph.memory import Memory, UIndexMemoryModel + from ldp.graph.op_utils import set_training_mode + from ldp.llms import EmbeddingModel + + _Memories = TypeAdapter(dict[int, Memory] | list[Memory]) # type: ignore[var-annotated] + + HAS_LDP_INSTALLED = True +except ImportError: + HAS_LDP_INSTALLED = False +from rich.console import Console + +from paperqa.docs import Docs +from paperqa.types import Answer +from paperqa.utils import pqa_directory + +from .env import Environment +from .helpers import litellm_get_search_query, table_formatter +from .models import AgentStatus, AnswerResponse, QueryRequest, SimpleProfiler +from .search import SearchDocumentStorage, SearchIndex +from .tools import EnvironmentState, GatherEvidence, GenerateAnswer, PaperSearch + +if TYPE_CHECKING: + from ldp.graph.ops import OpResult + +logger = logging.getLogger(__name__) +agent_logger = logging.getLogger(__name__ + ".agent_callers") + +DEFAULT_AGENT_TYPE = ToolSelector + + +async def agent_query( + query: str | QueryRequest, + docs: Docs | None = None, + agent_type: str | type = DEFAULT_AGENT_TYPE, + verbosity: int = 0, + **env_kwargs, +) -> AnswerResponse: + if isinstance(query, str): + query = QueryRequest(query=query) + if docs is None: + docs = Docs() + + search_index = SearchIndex( + fields=[*SearchIndex.REQUIRED_FIELDS, "question"], + index_name="answers", + index_directory=query.settings.index_directory, + storage=SearchDocumentStorage.JSON_MODEL_DUMP, + ) + + response = await run_agent(docs, query, agent_type, **env_kwargs) + agent_logger.debug(f"agent_response: {response}") + truncation_chars = 1_000_000 if verbosity > 1 else 1500 * (verbosity + 1) + agent_logger.info( + f"[bold blue]Answer: {response.answer.answer[:truncation_chars]}" + f"{'...(truncated)' if len(response.answer.answer) > truncation_chars else ''}" + "[/bold blue]" + ) + + await search_index.add_document( + { + "file_location": str(response.answer.id), + "body": response.answer.answer, + "question": response.answer.question, + }, + document=response, + ) + await search_index.save_index() + return response + + +def to_aviary_tool_selector( + agent_type: str | type, query: QueryRequest +) -> ToolSelector | None: + """Attempt to convert the agent type to an aviary ToolSelector.""" + if agent_type is ToolSelector or ( + isinstance(agent_type, str) + and ( + agent_type == ToolSelector.__name__ + or ( + agent_type.startswith(ToolSelector.__module__.split(".", maxsplit=1)[0]) + and locate(agent_type) is ToolSelector + ) + ) + ): + return ToolSelector( + model_name=query.settings.agent.agent_llm, + acompletion=query.settings.get_agent_llm().router.acompletion, + **(query.settings.agent.agent_config or {}), + ) + return None + + +async def to_ldp_agent( + agent_type: str | type, query: QueryRequest +) -> "Agent[SimpleAgentState] | None": + """Attempt to convert the agent type to an ldp Agent.""" + if not isinstance(agent_type, str): # Convert to fully qualified name + agent_type = f"{agent_type.__module__}.{agent_type.__name__}" + if not agent_type.startswith("ldp"): + return None + if not HAS_LDP_INSTALLED: + raise ImportError( + "ldp agents requires the 'ldp' extra for 'ldp'. Please:" + " `pip install paper-qa[ldp]`." + ) + + # TODO: support general agents + agent_cls = cast(type[Agent], locate(agent_type)) + agent_settings = query.settings.agent + agent_llm, config = agent_settings.agent_llm, agent_settings.agent_config or {} + if issubclass(agent_cls, ReActAgent | MemoryAgent): + if ( + issubclass(agent_cls, MemoryAgent) + and "memory_model" in config + and "memories" in config + ): + if "embedding_model" in config["memory_model"]: + # Work around EmbeddingModel not yet supporting deserialization + config["memory_model"]["embedding_model"] = EmbeddingModel.from_name( + embedding=config["memory_model"].pop("embedding_model")["name"] + ) + config["memory_model"] = UIndexMemoryModel(**config["memory_model"]) + memories = _Memories.validate_python(config.pop("memories")) + await asyncio.gather( + *( + config["memory_model"].add_memory(memory) + for memory in ( + memories.values() if isinstance(memories, dict) else memories + ) + ) + ) + return agent_cls( + llm_model={"model": agent_llm, "temperature": query.settings.temperature}, + **config, + ) + if issubclass(agent_cls, SimpleAgent): + return agent_cls( + llm_model={"model": agent_llm, "temperature": query.settings.temperature}, + sys_prompt=agent_settings.agent_system_prompt, + **config, + ) + if issubclass(agent_cls, HTTPAgentClient): + set_training_mode(False) + return HTTPAgentClient[SimpleAgentState]( + agent_state_type=SimpleAgentState, **config + ) + raise NotImplementedError(f"Didn't yet handle agent type {agent_type}.") + + +async def run_agent( + docs: Docs, + query: QueryRequest, + agent_type: str | type = DEFAULT_AGENT_TYPE, + **env_kwargs, +) -> AnswerResponse: + """ + Run an agent. + + Args: + docs: Docs to run upon. + query: Query to answer. + agent_type: Agent type (or fully qualified name to the type) to pass to + AgentType.get_agent, or "fake" to TODOC. + env_kwargs: Keyword arguments to pass to Environment instantiation. + + Returns: + Tuple of resultant answer, token counts, and agent status. + """ + profiler = SimpleProfiler() + outer_profile_name = f"agent-{agent_type}-{query.settings.agent.agent_llm}" + profiler.start(outer_profile_name) + + logger.info( + f"Beginning agent {agent_type!r} run with question {query.query!r} and full" + f" query {query.model_dump()}." + ) + + if agent_type == "fake": + answer, agent_status = await run_fake_agent(query, docs, **env_kwargs) + elif tool_selector_or_none := to_aviary_tool_selector(agent_type, query): + answer, agent_status = await run_aviary_agent( + query, docs, tool_selector_or_none, **env_kwargs + ) + elif ldp_agent_or_none := await to_ldp_agent(agent_type, query): + answer, agent_status = await run_ldp_agent( + query, docs, ldp_agent_or_none, **env_kwargs + ) + else: + raise NotImplementedError(f"Didn't yet handle agent type {agent_type}.") + + if "cannot answer" in answer.answer.lower() and agent_status != AgentStatus.TIMEOUT: + agent_status = AgentStatus.UNSURE + # stop after, so overall isn't reported as long-running step. + logger.info( + f"Finished agent {agent_type!r} run with question {query.query!r} and status" + f" {agent_status}." + ) + return AnswerResponse(answer=answer, status=agent_status) + + +async def run_fake_agent( + query: QueryRequest, + docs: Docs, + on_env_reset_callback: Callable[[EnvironmentState], Awaitable] | None = None, + on_env_step_callback: ( + Callable[[list[Message], float, bool, bool], Awaitable] | None + ) = None, + **env_kwargs, +) -> tuple[Answer, AgentStatus]: + env = Environment(query, docs, **env_kwargs) + _, tools = await env.reset() + if on_env_reset_callback: + await on_env_reset_callback(env.state) + + question = env.state.answer.question + search_tool = next(filter(lambda x: x.info.name == PaperSearch.TOOL_FN_NAME, tools)) + gather_evidence_tool = next( + filter(lambda x: x.info.name == GatherEvidence.TOOL_FN_NAME, tools) + ) + generate_answer_tool = next( + filter(lambda x: x.info.name == GenerateAnswer.TOOL_FN_NAME, tools) + ) + + async def step(tool: Tool, **call_kwargs) -> None: + obs, reward, done, truncated = await env.step( + action=ToolRequestMessage( + tool_calls=[ToolCall.from_tool(tool, **call_kwargs)] + ) + ) + if on_env_step_callback: + await on_env_step_callback(obs, reward, done, truncated) + + # Seed docs with a few keyword searches + for search in await litellm_get_search_query( + question, llm=query.settings.llm, count=3 + ): + await step(search_tool, query=search, min_year=None, max_year=None) + await step(gather_evidence_tool, question=question) + await step(generate_answer_tool, question=question) + return env.state.answer, AgentStatus.SUCCESS + + +class ToolSelectorLedger(BaseModel): + """ + Simple ledger to record tools and messages. + + TODO: remove this after it's upstreamed into aviary. + """ + + tools: list[Tool] = Field(default_factory=list) + messages: list[ToolRequestMessage | ToolResponseMessage | Message] = Field( + default_factory=list + ) + + +async def run_aviary_agent( + query: QueryRequest, + docs: Docs, + agent: ToolSelector, + on_env_reset_callback: Callable[[EnvironmentState], Awaitable] | None = None, + on_agent_action_callback: ( + Callable[[ToolRequestMessage, BaseModel], Awaitable] | None + ) = None, + on_env_step_callback: ( + Callable[[list[Message], float, bool, bool], Awaitable] | None + ) = None, + **env_kwargs, +) -> tuple[Answer, AgentStatus]: + env = Environment(query, docs, **env_kwargs) + done = False + + try: + async with asyncio.timeout(query.settings.agent.timeout): + obs, tools = await env.reset() + if on_env_reset_callback: + await on_env_reset_callback(env.state) + + agent_state = ToolSelectorLedger( + messages=( + [ + Message( + role="system", + content=query.settings.agent.agent_system_prompt, + ) + ] + if query.settings.agent.agent_system_prompt + else [] + ), + tools=tools, + ) + + while not done: + agent_state.messages += obs + action = await agent(agent_state.messages, tools) + agent_state.messages = [*agent_state.messages, action] + if on_agent_action_callback: + await on_agent_action_callback(action, agent_state) + + obs, reward, done, truncated = await env.step(action) + if on_env_step_callback: + await on_env_step_callback(obs, reward, done, truncated) + status = AgentStatus.SUCCESS + except TimeoutError: + logger.warning( + f"Agent timeout after {query.settings.agent.timeout}-sec, just answering." + ) + status = AgentStatus.TIMEOUT + await tools[-1]._tool_fn(question=query.query, state=env.state) + except Exception: + logger.exception(f"Agent {agent} failed.") + status = AgentStatus.FAIL + return env.state.answer, status + + +async def run_ldp_agent( + query: QueryRequest, + docs: Docs, + agent: "Agent[SimpleAgentState]", + on_env_reset_callback: Callable[[EnvironmentState], Awaitable] | None = None, + on_agent_action_callback: "Callable[[OpResult[ToolRequestMessage], SimpleAgentState, float], Awaitable] | None" = None, # noqa: E501 + on_env_step_callback: ( + Callable[[list[Message], float, bool, bool], Awaitable] | None + ) = None, + **env_kwargs, +) -> tuple[Answer, AgentStatus]: + env = Environment(query, docs, **env_kwargs) + done = False + + try: + async with asyncio.timeout(query.settings.agent.timeout): + obs, tools = await env.reset() + if on_env_reset_callback: + await on_env_reset_callback(env.state) + + agent_state = await agent.init_state(tools=tools) + + while not done: + action, agent_state, value = await agent.get_asv(agent_state, obs) + if on_agent_action_callback: + await on_agent_action_callback(action, agent_state, value) + + obs, reward, done, truncated = await env.step(action.value) + if on_env_step_callback: + await on_env_step_callback(obs, reward, done, truncated) + status = AgentStatus.SUCCESS + except TimeoutError: + logger.warning( + f"Agent timeout after {query.settings.agent.timeout}-sec, just answering." + ) + status = AgentStatus.TIMEOUT + await tools[-1]._tool_fn(question=query.query, state=env.state) + except Exception: + logger.exception(f"Agent {agent} failed.") + status = AgentStatus.FAIL + return env.state.answer, status + + +async def index_search( + query: str, + index_name: str = "answers", + index_directory: str | os.PathLike | None = None, +) -> list[tuple[AnswerResponse, str] | tuple[Any, str]]: + fields = [*SearchIndex.REQUIRED_FIELDS] + if index_name == "answers": + fields.append("question") + search_index = SearchIndex( + fields=fields, + index_name=index_name, + index_directory=index_directory or pqa_directory("indexes"), + storage=( + SearchDocumentStorage.JSON_MODEL_DUMP + if index_name == "answers" + else SearchDocumentStorage.PICKLE_COMPRESSED + ), + ) + + results = [ + (AnswerResponse(**a[0]) if index_name == "answers" else a[0], a[1]) + for a in await search_index.query(query=query, keep_filenames=True) + ] + + if results: + console = Console(record=True) + # Render the table to a string + console.print(table_formatter(results)) + else: + count = await search_index.count + agent_logger.info(f"No results found. Searched {count} docs") + + return results diff --git a/paperqa/agents/models.py b/paperqa/agents/models.py new file mode 100644 index 00000000..12567393 --- /dev/null +++ b/paperqa/agents/models.py @@ -0,0 +1,183 @@ +from __future__ import annotations + +import asyncio +import logging +import time +from contextlib import asynccontextmanager +from enum import StrEnum +from typing import Any, ClassVar, Protocol +from uuid import UUID, uuid4 + +from pydantic import ( + BaseModel, + ConfigDict, + Field, + PrivateAttr, + ValidationInfo, + computed_field, + field_validator, +) + +from paperqa.llms import LiteLLMModel +from paperqa.settings import Settings +from paperqa.types import Answer +from paperqa.version import __version__ + +logger = logging.getLogger(__name__) + + +class SupportsPickle(Protocol): + """Type protocol for typing any object that supports pickling.""" + + def __reduce__(self) -> str | tuple[Any, ...]: ... + def __getstate__(self) -> object: ... + def __setstate__(self, state: object) -> None: ... + + +class AgentStatus(StrEnum): + # FAIL - no answer could be generated + FAIL = "fail" + # SUCCESS - answer was generated + SUCCESS = "success" + # TIMEOUT - agent took too long, but an answer was generated + TIMEOUT = "timeout" + # UNSURE - the agent was unsure, but an answer is present + UNSURE = "unsure" + + +class MismatchedModelsError(Exception): + """Error to throw when model clients clash .""" + + LOG_METHOD_NAME: ClassVar[str] = "warning" + + +class QueryRequest(BaseModel): + query: str = "" + id: UUID = Field( + default_factory=uuid4, + description="Identifier which will be propagated to the Answer object.", + ) + settings_template: str | None = None + settings: Settings = Field(default_factory=Settings, validate_default=True) + # provides post-hoc linkage of request to a docs object + # NOTE: this isn't a unique field, on the user to keep straight + _docs_name: str | None = PrivateAttr(default=None) + + model_config = ConfigDict(extra="forbid") + + @field_validator("settings") + @classmethod + def apply_settings_template(cls, v: Settings, info: ValidationInfo) -> Settings: + if info.data["settings_template"] and isinstance(v, Settings): + base_settings = Settings.from_name(info.data["settings_template"]) + return Settings(**(base_settings.model_dump() | v.model_dump())) + return v + + @computed_field # type: ignore[prop-decorator] + @property + def docs_name(self) -> str | None: + return self._docs_name + + def set_docs_name(self, docs_name: str) -> None: + """Set the internal docs name for tracking.""" + self._docs_name = docs_name + + +class AnswerResponse(BaseModel): + answer: Answer + bibtex: dict[str, str] | None = None + status: AgentStatus + timing_info: dict[str, dict[str, float]] | None = None + duration: float = 0.0 + # A placeholder for interesting statistics we can show users + # about the answer, such as the number of sources used, etc. + stats: dict[str, str] | None = None + + @field_validator("answer") + def strip_answer( + cls, v: Answer, info: ValidationInfo # noqa: ARG002, N805 + ) -> Answer: + # This modifies in place, this is fine + # because when a response is being constructed, + # we should be done with the Answer object + v.filter_content_for_user() + return v + + async def get_summary(self, llm_model: str = "gpt-4o") -> str: + sys_prompt = ( + "Revise the answer to a question to be a concise SMS message. " + "Use abbreviations or emojis if necessary." + ) + model = LiteLLMModel(name=llm_model) + result = await model.run_prompt( + prompt="{question}\n\n{answer}", + data={"question": self.answer.question, "answer": self.answer.answer}, + system_prompt=sys_prompt, + ) + return result.text.strip() + + +class TimerData(BaseModel): + start_time: float = Field(default_factory=time.time) # noqa: FURB111 + durations: list[float] = Field(default_factory=list) + + +class SimpleProfiler(BaseModel): + """Basic profiler with start/stop and named timers. + + The format for this logger needs to be strictly followed, as downstream google + cloud monitoring is based on the following + # [Profiling] {**name** of timer} | {**elapsed** time of function} | {**__version__** of PaperQA} + """ + + timers: dict[str, list[float]] = {} + running_timers: dict[str, TimerData] = {} + uid: UUID = Field(default_factory=uuid4) + + @asynccontextmanager + async def timer(self, name: str): + start_time = asyncio.get_running_loop().time() + try: + yield + finally: + end_time = asyncio.get_running_loop().time() + elapsed = end_time - start_time + self.timers.setdefault(name, []).append(elapsed) + logger.info( + f"[Profiling] | UUID: {self.uid} | NAME: {name} | TIME: {elapsed:.3f}s" + f" | VERSION: {__version__}" + ) + + def start(self, name: str) -> None: + try: + self.running_timers[name] = TimerData() + except RuntimeError: # No running event loop (not in async) + self.running_timers[name] = TimerData(start_time=time.time()) + + def stop(self, name: str) -> None: + timer_data = self.running_timers.pop(name, None) + if timer_data: + try: + t_stop: float = asyncio.get_running_loop().time() + except RuntimeError: # No running event loop (not in async) + t_stop = time.time() + elapsed = t_stop - timer_data.start_time + self.timers.setdefault(name, []).append(elapsed) + logger.info( + f"[Profiling] | UUID: {self.uid} | NAME: {name} | TIME: {elapsed:.3f}s" + f" | VERSION: {__version__}" + ) + else: + logger.warning(f"Timer {name} not running") + + def results(self) -> dict[str, dict[str, float]]: + result = {} + for name, durations in self.timers.items(): + mean = sum(durations) / len(durations) + result[name] = { + "low": min(durations), + "mean": mean, + "max": max(durations), + "total": sum(durations), + } + return result diff --git a/paperqa/agents/search.py b/paperqa/agents/search.py new file mode 100644 index 00000000..f9625bf1 --- /dev/null +++ b/paperqa/agents/search.py @@ -0,0 +1,495 @@ +from __future__ import annotations + +import csv +import json +import logging +import os +import pathlib +import pickle +import zlib +from collections.abc import Sequence +from enum import StrEnum, auto +from io import StringIO +from typing import Any, ClassVar, cast +from uuid import UUID + +import anyio +from pydantic import BaseModel +from tantivy import ( # pylint: disable=no-name-in-module + Document, + Index, + Schema, + SchemaBuilder, + Searcher, +) +from tenacity import ( + RetryError, + retry, + retry_if_exception_type, + stop_after_attempt, + wait_random_exponential, +) + +from paperqa.docs import Docs +from paperqa.settings import MaybeSettings, Settings, get_settings +from paperqa.types import DocDetails +from paperqa.utils import ImpossibleParsingError, hexdigest, pqa_directory + +from .models import SupportsPickle + +logger = logging.getLogger(__name__) + + +class AsyncRetryError(Exception): + """Flags a retry for another tenacity attempt.""" + + +class RobustEncoder(json.JSONEncoder): + """JSON encoder that can handle UUID and set objects.""" + + def default(self, o): + if isinstance(o, UUID): + # if the obj is uuid, we simply return the value of uuid + return str(o) + if isinstance(o, set): + return list(o) + if isinstance(o, os.PathLike): + return str(o) + return json.JSONEncoder.default(self, o) + + +class SearchDocumentStorage(StrEnum): + JSON_MODEL_DUMP = auto() + PICKLE_COMPRESSED = auto() + PICKLE_UNCOMPRESSED = auto() + + def extension(self) -> str: + if self == SearchDocumentStorage.JSON_MODEL_DUMP: + return "json" + if self == SearchDocumentStorage.PICKLE_COMPRESSED: + return "zip" + return "pkl" + + def write_to_string(self, data: BaseModel | SupportsPickle) -> bytes: + if self == SearchDocumentStorage.JSON_MODEL_DUMP: + if isinstance(data, BaseModel): + return json.dumps(data.model_dump(), cls=RobustEncoder).encode("utf-8") + raise ValueError("JSON_MODEL_DUMP requires a BaseModel object.") + if self == SearchDocumentStorage.PICKLE_COMPRESSED: + return zlib.compress(pickle.dumps(data)) + return pickle.dumps(data) + + def read_from_string(self, data: str | bytes) -> BaseModel | SupportsPickle: + if self == SearchDocumentStorage.JSON_MODEL_DUMP: + return json.loads(data) + if self == SearchDocumentStorage.PICKLE_COMPRESSED: + return pickle.loads(zlib.decompress(data)) # type: ignore[arg-type] # noqa: S301 + return pickle.loads(data) # type: ignore[arg-type] # noqa: S301 + + +class SearchIndex: + REQUIRED_FIELDS: ClassVar[list[str]] = ["file_location", "body"] + + def __init__( + self, + fields: Sequence[str] | None = None, + index_name: str = "pqa_index", + index_directory: str | os.PathLike | None = None, + storage: SearchDocumentStorage = SearchDocumentStorage.PICKLE_COMPRESSED, + ): + if fields is None: + fields = self.REQUIRED_FIELDS + self.fields = fields + if not all(f in self.fields for f in self.REQUIRED_FIELDS): + raise ValueError( + f"{self.REQUIRED_FIELDS} must be included in search index fields." + ) + if index_directory is None: + index_directory = pqa_directory("indexes") + self.index_name = index_name + self._index_directory = index_directory + self._schema = None + self._index = None + self._searcher = None + self._index_files: dict[str, str] = {} + self.changed = False + self.storage = storage + + async def init_directory(self) -> None: + await anyio.Path(await self.index_directory).mkdir(parents=True, exist_ok=True) + + @staticmethod + async def extend_and_make_directory(base: anyio.Path, *dirs: str) -> anyio.Path: + directory = base.joinpath(*dirs) + await directory.mkdir(parents=True, exist_ok=True) + return directory + + @property + async def index_directory(self) -> anyio.Path: + return await self.extend_and_make_directory( + anyio.Path(self._index_directory), self.index_name + ) + + @property + async def index_filename(self) -> anyio.Path: + return await self.extend_and_make_directory(await self.index_directory, "index") + + @property + async def docs_index_directory(self) -> anyio.Path: + return await self.extend_and_make_directory(await self.index_directory, "docs") + + @property + async def file_index_filename(self) -> anyio.Path: + return (await self.index_directory) / "files.zip" + + @property + def schema(self) -> Schema: + if not self._schema: + schema_builder = SchemaBuilder() + for field in self.fields: + schema_builder.add_text_field(field, stored=True) + self._schema = schema_builder.build() # type: ignore[assignment] + return cast(Schema, self._schema) + + @property + async def index(self) -> Index: + if not self._index: + index_path = await self.index_filename + if await (index_path / "meta.json").exists(): + self._index = Index.open(str(index_path)) # type: ignore[assignment] + else: + self._index = Index(self.schema, str(index_path)) # type: ignore[assignment] + return cast(Index, self._index) + + @property + async def searcher(self) -> Searcher: + if not self._searcher: + index = await self.index + index.reload() + self._searcher = index.searcher() # type: ignore[assignment] + return cast(Searcher, self._searcher) + + @property + async def count(self) -> int: + return (await self.searcher).num_docs + + @property + async def index_files(self) -> dict[str, str]: + if not self._index_files: + file_index_path = await self.file_index_filename + if await file_index_path.exists(): + async with await anyio.open_file(file_index_path, "rb") as f: + content = await f.read() + self._index_files = pickle.loads( # noqa: S301 + zlib.decompress(content) + ) + return self._index_files + + @staticmethod + def filehash(body: str) -> str: + return hexdigest(body) + + async def filecheck(self, filename: str, body: str | None = None): + filehash = None + if body: + filehash = self.filehash(body) + index_files = await self.index_files + return bool( + index_files.get(filename) + and (filehash is None or index_files[filename] == filehash) + ) + + async def add_document( + self, index_doc: dict, document: Any | None = None, max_retries: int = 1000 + ) -> None: + @retry( + stop=stop_after_attempt(max_retries), + wait=wait_random_exponential(multiplier=0.25, max=60), + retry=retry_if_exception_type(AsyncRetryError), + reraise=True, + ) + async def _add_document_with_retry() -> None: + if not await self.filecheck(index_doc["file_location"], index_doc["body"]): + try: + index = await self.index + writer = index.writer() + writer.add_document(Document.from_dict(index_doc)) # type: ignore[call-arg] + writer.commit() + + filehash = self.filehash(index_doc["body"]) + (await self.index_files)[index_doc["file_location"]] = filehash + + if document: + docs_index_dir = await self.docs_index_directory + async with await anyio.open_file( + docs_index_dir / f"{filehash}.{self.storage.extension()}", + "wb", + ) as f: + await f.write(self.storage.write_to_string(document)) + + self.changed = True + except ValueError as e: + if "Failed to acquire Lockfile: LockBusy." in str(e): + raise AsyncRetryError("Failed to acquire lock") from e + raise + + try: + await _add_document_with_retry() + except RetryError: + logger.exception( + f"Failed to add document after {max_retries} attempts:" + f" {index_doc['file_location']}" + ) + raise + + # Success + + @staticmethod + @retry( + stop=stop_after_attempt(1000), + wait=wait_random_exponential(multiplier=0.25, max=60), + retry=retry_if_exception_type(AsyncRetryError), + reraise=True, + ) + def delete_document(index: Index, file_location: str) -> None: + try: + writer = index.writer() + writer.delete_documents("file_location", file_location) + writer.commit() + except ValueError as e: + if "Failed to acquire Lockfile: LockBusy." in str(e): + raise AsyncRetryError("Failed to acquire lock") from e + raise + + async def remove_from_index(self, file_location: str) -> None: + index_files = await self.index_files + if index_files.get(file_location): + index = await self.index + self.delete_document(index, file_location) + filehash = index_files.pop(file_location) + docs_index_dir = await self.docs_index_directory + # TODO: since the directory is part of the filehash these + # are always missing. Unsure of how to get around this. + await (docs_index_dir / f"{filehash}.{self.storage.extension()}").unlink( + missing_ok=True + ) + + self.changed = True + + async def save_index(self) -> None: + file_index_path = await self.file_index_filename + async with await anyio.open_file(file_index_path, "wb") as f: + await f.write(zlib.compress(pickle.dumps(await self.index_files))) + + async def get_saved_object( + self, file_location: str, keep_filenames: bool = False + ) -> Any | None | tuple[Any, str]: + filehash = (await self.index_files).get(file_location) + if filehash: + docs_index_dir = await self.docs_index_directory + async with await anyio.open_file( + docs_index_dir / f"{filehash}.{self.storage.extension()}", "rb" + ) as f: + content = await f.read() + if keep_filenames: + return self.storage.read_from_string(content), file_location + return self.storage.read_from_string(content) + return None + + def clean_query(self, query: str) -> str: + for replace in ("*", "[", "]", ":", "(", ")", "{", "}", "~", '"'): + query = query.replace(replace, "") + return query + + async def query( + self, + query: str, + top_n: int = 10, + offset: int = 0, + min_score: float = 0.0, + keep_filenames: bool = False, + field_subset: list[str] | None = None, + ) -> list[Any]: + query_fields = list(field_subset or self.fields) + searcher = await self.searcher + index = await self.index + results = [ + s[1] + for s in searcher.search( + index.parse_query(self.clean_query(query), query_fields), top_n + ).hits + if s[0] > min_score + ][offset : offset + top_n] + search_index_docs = [searcher.doc(result) for result in results] + return [ + result + for result in [ + await self.get_saved_object( + doc["file_location"][0], keep_filenames=keep_filenames # type: ignore[index] + ) + for doc in search_index_docs + ] + if result is not None + ] + + +async def maybe_get_manifest(filename: anyio.Path | None) -> dict[str, DocDetails]: + if not filename: + return {} + if filename.suffix == ".csv": + try: + async with await anyio.open_file(filename, mode="r") as file: + content = await file.read() + reader = csv.DictReader(StringIO(content)) + records = [DocDetails(**row) for row in reader] + return {str(r.file_location): r for r in records if r.file_location} + except FileNotFoundError: + logging.warning(f"Manifest file at {filename} could not be found.") + except Exception: + logging.exception(f"Error reading manifest file {filename}") + else: + logging.error(f"Invalid manifest file type: {filename.suffix}") + + return {} + + +async def process_file( + file_path: anyio.Path, + search_index: SearchIndex, + metadata: dict[str, Any], + semaphore: anyio.Semaphore, + settings: Settings, +) -> None: + async with semaphore: + file_name = file_path.name + if not await search_index.filecheck(str(file_path)): + logger.info(f"New file to index: {file_name}...") + + doi, title = None, None + if file_name in metadata: + doi, title = metadata[file_name].doi, metadata[file_name].title + + tmp_docs = Docs() + try: + await tmp_docs.aadd( + path=pathlib.Path(file_path), + title=title, + doi=doi, + fields=["title", "author", "journal", "year"], + settings=settings, + ) + except (ValueError, ImpossibleParsingError): + logger.exception( + f"Error parsing {file_name}, skipping index for this file." + ) + (await search_index.index_files)[str(file_path)] = "ERROR" + await search_index.save_index() + return + + this_doc = next(iter(tmp_docs.docs.values())) + + if isinstance(this_doc, DocDetails): + title = this_doc.title or file_name + year = this_doc.year or "Unknown year" + else: + title, year = file_name, "Unknown year" + + await search_index.add_document( + { + "title": title, + "year": year, + "file_location": str(file_path), + "body": "".join([t.text for t in tmp_docs.texts]), + }, + document=tmp_docs, + ) + await search_index.save_index() + logger.info(f"Complete ({title}).") + + +WARN_IF_INDEXING_MORE_THAN = 999 + + +async def get_directory_index( + index_name: str | None = None, + sync_index_w_directory: bool = True, + settings: MaybeSettings = None, +) -> SearchIndex: + """ + Create a Tantivy index from a directory of text files. + + Args: + sync_index_w_directory: Sync the index with the directory. (i.e. delete files not in directory) + index_name: Name of the index. If not given, the name will be taken from the settings + settings: Application settings. + """ + _settings = get_settings(settings) + + semaphore = anyio.Semaphore(_settings.agent.index_concurrency) + directory = anyio.Path(_settings.paper_directory) + + if _settings.index_absolute_directory: + directory = await directory.absolute() + + search_index = SearchIndex( + fields=[*SearchIndex.REQUIRED_FIELDS, "title", "year"], + index_name=index_name or _settings.get_index_name(), + index_directory=_settings.index_directory, + ) + + manifest_file = ( + anyio.Path(_settings.manifest_file) if _settings.manifest_file else None + ) + # check if it doesn't exist - if so, try to make it relative + if manifest_file and not await manifest_file.exists(): + manifest_file = directory / manifest_file + + metadata = await maybe_get_manifest(manifest_file) + valid_files = [ + file + async for file in ( + directory.rglob("*") if _settings.index_recursively else directory.iterdir() + ) + if file.suffix in {".txt", ".pdf", ".html"} + ] + if len(valid_files) > WARN_IF_INDEXING_MORE_THAN: + logger.warning( + f"Indexing {len(valid_files)} files. This may take a few minutes." + ) + index_files = await search_index.index_files + + if missing := (set(index_files.keys()) - {str(f) for f in valid_files}): + if sync_index_w_directory: + for missing_file in missing: + logger.warning( + f"[bold red]Removing {missing_file} from index.[/bold red]" + ) + await search_index.remove_from_index(missing_file) + logger.warning("[bold red]Files removed![/bold red]") + else: + logger.warning( + "[bold red]Indexed files are missing from index folder" + f" ({directory}).[/bold red]" + ) + logger.warning(f"[bold red]files: {missing}[/bold red]") + + async with anyio.create_task_group() as tg: + for file_path in valid_files: + if sync_index_w_directory: + tg.start_soon( + process_file, + file_path, + search_index, + metadata, + semaphore, + _settings, + ) + else: + logger.debug(f"New file {file_path.name} found in directory.") + + if search_index.changed: + await search_index.save_index() + else: + logger.debug("No changes to index.") + + return search_index diff --git a/paperqa/agents/tools.py b/paperqa/agents/tools.py new file mode 100644 index 00000000..0453cb1f --- /dev/null +++ b/paperqa/agents/tools.py @@ -0,0 +1,271 @@ +"""Base classes for tools, implemented in a functional manner.""" + +import inspect +import logging +import re +import sys +from typing import ClassVar + +from pydantic import BaseModel, ConfigDict, Field, computed_field + +from paperqa.docs import Docs +from paperqa.llms import EmbeddingModel, LiteLLMModel +from paperqa.settings import Settings +from paperqa.types import Answer + +from .search import get_directory_index + +logger = logging.getLogger(__name__) + + +class EnvironmentState(BaseModel): + """State here contains documents and answer being populated.""" + + model_config = ConfigDict(extra="forbid") + + docs: Docs + answer: Answer + + # SEE: https://regex101.com/r/RmuVdC/1 + STATUS_SEARCH_REGEX_PATTERN: ClassVar[str] = ( + r"Status: Paper Count=(\d+) \| Relevant Papers=(\d+) \| Current Evidence=(\d+)" + ) + RELEVANT_SCORE_CUTOFF: ClassVar[int] = 5 + + @computed_field # type: ignore[prop-decorator] + @property + def status(self) -> str: + return ( + f"Status: Paper Count={len(self.docs.docs)} | Relevant Papers=" + f"{len({c.text.doc.dockey for c in self.answer.contexts if c.score > self.RELEVANT_SCORE_CUTOFF})}" + f" | Current Evidence={len([c for c in self.answer.contexts if c.score > self.RELEVANT_SCORE_CUTOFF])}" + f" | Current Cost=${self.answer.cost:.4f}" + ) + + +class NamedTool(BaseModel): + """Base class to make looking up tools easier.""" + + TOOL_FN_NAME: ClassVar[str] = ( + "# unpopulated" # Comment symbol ensures no collisions + ) + + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) + + +class PaperSearch(NamedTool): + TOOL_FN_NAME = "paper_search" + + settings: Settings + embedding_model: EmbeddingModel + previous_searches: dict[tuple[str, str | None], int] = Field(default_factory=dict) + + async def paper_search( + self, + query: str, + min_year: int | None, + max_year: int | None, + state: EnvironmentState, + ) -> str: + """ + Search for papers to increase the paper count. + + Repeat previous calls with the same query and years to continue a search. Only repeat a maximum of twice. + This tool can be called concurrently. + This tool introduces novel papers, so invoke this tool when just beginning or when unsatisfied with the current evidence. + + Args: + query: A search query, which can be a specific phrase, complete sentence, + or general keywords, e.g. 'machine learning for immunology'. Also can be + given search operators. + min_year: Filter for minimum publication year, or None for no minimum year. + The current year is {current_year}. + max_year: Filter for maximum publication year, or None for no maximum year. + The current year is {current_year}. + state: Current state. + + Returns: + String describing searched papers and the current status. + """ # noqa: E501,W505 + # Convert to date range (e.g. 2022-2022) if date is present + year = ( + f"{min_year if min_year else ''}-{max_year if max_year else ''}" # noqa: FURB110 + if (min_year or max_year) + else None + ) + # get offset if we've done this search before (continuation of search) + # or mark this search as new (so offset 0) + search_key = query, year + try: + offset = self.previous_searches[search_key] + except KeyError: + offset = self.previous_searches[search_key] = 0 + + logger.info(f"Starting paper search for {query!r}.") + index = await get_directory_index(settings=self.settings) + results = await index.query( + query, + top_n=self.settings.agent.search_count, + offset=offset, + field_subset=[f for f in index.fields if f != "year"], + ) + logger.info( + f"{self.TOOL_FN_NAME} for query {query!r} returned {len(results)} papers." + ) + + # combine all the resulting doc objects into one and update the state + # there's only one doc per result, so we can just take the first one + all_docs = [] + for r in results: + this_doc = next(iter(r.docs.values())) + all_docs.append(this_doc) + await state.docs.aadd_texts( + texts=r.texts, + doc=this_doc, + settings=self.settings, + embedding_model=self.embedding_model, + ) + + status = state.status + logger.info(status) + # mark how far we've searched so that continuation will start at the right place + self.previous_searches[search_key] += self.settings.agent.search_count + if self.settings.agent.return_paper_metadata: + retrieved_papers = "\n".join([f"{x.title} ({x.year})" for x in all_docs]) + return f"Retrieved Papers:\n{retrieved_papers}\n\n{status}" + return status + + +class EmptyDocsError(RuntimeError): + """Error to throw when we needed docs to be present.""" + + +class GatherEvidence(NamedTool): + TOOL_FN_NAME = "gather_evidence" + + settings: Settings + summary_llm_model: LiteLLMModel + embedding_model: EmbeddingModel + + async def gather_evidence(self, question: str, state: EnvironmentState) -> str: + """ + Gather evidence from previous papers given a specific question to increase evidence and relevant paper counts. + + A valuable time to invoke this tool is right after another tool increases paper count. + Feel free to invoke this tool in parallel with other tools, but do not call this tool in parallel with itself. + Only invoke this tool when the paper count is above zero, or this tool will be useless. + + Args: + question: Specific question to gather evidence for. + state: Current state. + + Returns: + String describing gathered evidence and the current status. + """ + if not state.docs.docs: + raise EmptyDocsError("Not gathering evidence due to having no papers.") + + logger.info(f"{self.TOOL_FN_NAME} starting for question {question!r}.") + original_question = state.answer.question + try: + # Swap out the question with the more specific question + # TODO: remove this swap, as it prevents us from supporting parallel calls + state.answer.question = question + l0 = len(state.answer.contexts) + # TODO: refactor answer out of this... + state.answer = await state.docs.aget_evidence( + query=state.answer, + settings=self.settings, + embedding_model=self.embedding_model, + summary_llm_model=self.summary_llm_model, + ) + l1 = len(state.answer.contexts) + finally: + state.answer.question = original_question + + status = state.status + logger.info(status) + sorted_contexts = sorted( + state.answer.contexts, key=lambda x: x.score, reverse=True + ) + best_evidence = ( + f" Best evidence:\n\n{sorted_contexts[0].context}" + if sorted_contexts + else "" + ) + return f"Added {l1 - l0} pieces of evidence.{best_evidence}\n\n" + status + + +class GenerateAnswer(NamedTool): + TOOL_FN_NAME = "gen_answer" + + settings: Settings + llm_model: LiteLLMModel + summary_llm_model: LiteLLMModel + embedding_model: EmbeddingModel + + FAILED_TO_ANSWER: ClassVar[str] = "Failed to answer question." + + @classmethod + def did_not_fail_to_answer(cls, message: str) -> bool: + return not message.startswith(cls.FAILED_TO_ANSWER) + + async def gen_answer(self, question: str, state: EnvironmentState) -> str: + """ + Ask a model to propose an answer using current evidence. + + The tool may fail, indicating that better or different evidence should be found. + Aim for at least five pieces of evidence from multiple sources before invoking this tool. + Feel free to invoke this tool in parallel with other tools, but do not call this tool in parallel with itself. + + Args: + question: Question to be answered. + state: Current state. + """ + logger.info(f"Generating answer for '{question}'.") + # TODO: Should we allow the agent to change the question? + # self.answer.question = query + state.answer = await state.docs.aquery( + query=state.answer, + settings=self.settings, + llm_model=self.llm_model, + summary_llm_model=self.summary_llm_model, + embedding_model=self.embedding_model, + ) + + if "cannot answer" in state.answer.answer.lower(): + if self.settings.agent.wipe_context_on_answer_failure: + state.answer.contexts = [] + state.answer.context = "" + answer = self.FAILED_TO_ANSWER + else: + answer = state.answer.answer + status = state.status + logger.info(status) + return f"{answer} | {status}" + + # NOTE: can match failure to answer or an actual answer + ANSWER_SPLIT_REGEX_PATTERN: ClassVar[str] = ( + r" \| " + EnvironmentState.STATUS_SEARCH_REGEX_PATTERN + ) + + @classmethod + def extract_answer_from_message(cls, content: str) -> str: + """Extract the answer from a message content.""" + answer, *rest = re.split( + pattern=cls.ANSWER_SPLIT_REGEX_PATTERN, string=content, maxsplit=1 + ) + if len(rest) != 4 or not cls.did_not_fail_to_answer(answer): # noqa: PLR2004 + return "" + return answer + + +AVAILABLE_TOOL_NAME_TO_CLASS: dict[str, type[NamedTool]] = { + cls.TOOL_FN_NAME: cls + for _, cls in inspect.getmembers( + sys.modules[__name__], + predicate=lambda v: inspect.isclass(v) + and issubclass(v, NamedTool) + and v is not NamedTool, + ) +} diff --git a/paperqa/clients/__init__.py b/paperqa/clients/__init__.py index e3956f98..5505eaf6 100644 --- a/paperqa/clients/__init__.py +++ b/paperqa/clients/__init__.py @@ -2,29 +2,36 @@ import copy import logging -from typing import Any, Collection, Coroutine, Sequence +from collections.abc import Collection, Coroutine, Sequence +from typing import Any import aiohttp from pydantic import BaseModel, ConfigDict -from ..types import Doc, DocDetails -from ..utils import gather_with_concurrency +from paperqa.types import Doc, DocDetails +from paperqa.utils import gather_with_concurrency + from .client_models import MetadataPostProcessor, MetadataProvider from .crossref import CrossrefProvider from .journal_quality import JournalQualityPostProcessor +from .retractions import RetrationDataPostProcessor from .semantic_scholar import SemanticScholarProvider +from .unpaywall import UnpaywallProvider logger = logging.getLogger(__name__) -ALL_CLIENTS: ( - Collection[type[MetadataPostProcessor | MetadataProvider]] - | Sequence[Collection[type[MetadataPostProcessor | MetadataProvider]]] -) = { +DEFAULT_CLIENTS: Collection[type[MetadataPostProcessor | MetadataProvider]] = { CrossrefProvider, SemanticScholarProvider, JournalQualityPostProcessor, } +ALL_CLIENTS: Collection[type[MetadataPostProcessor | MetadataProvider]] = { + *DEFAULT_CLIENTS, + UnpaywallProvider, + RetrationDataPostProcessor, +} + class DocMetadataTask(BaseModel): """Holder for provider and processor tasks.""" @@ -53,13 +60,13 @@ def __repr__(self) -> str: class DocMetadataClient: - def __init__( + def __init__( # pylint: disable=dangerous-default-value self, session: aiohttp.ClientSession | None = None, clients: ( Collection[type[MetadataPostProcessor | MetadataProvider]] | Sequence[Collection[type[MetadataPostProcessor | MetadataProvider]]] - ) = ALL_CLIENTS, + ) = DEFAULT_CLIENTS, ) -> None: """Metadata client for querying multiple metadata providers and processors. @@ -150,6 +157,9 @@ async def query(self, **kwargs) -> DocDetails | None: ) break + if self._session is None: + await session.close() + return doc_details async def bulk_query( @@ -160,18 +170,31 @@ async def bulk_query( ) async def upgrade_doc_to_doc_details(self, doc: Doc, **kwargs) -> DocDetails: + + # note we have some extra fields which may have come from reading the doc text, + # but aren't in the doc object, we add them here too. + extra_fields = { + k: v for k, v in kwargs.items() if k in {"title", "authors", "doi"} + } + # abuse our doc_details object to be an int if it's empty + # our __add__ operation supports int by doing nothing + extra_doc: int | DocDetails = ( + 0 if not extra_fields else DocDetails(**extra_fields) + ) + if doc_details := await self.query(**kwargs): if doc.overwrite_fields_from_metadata: - return doc_details + return extra_doc + doc_details + # hard overwrite the details from the prior object doc_details.dockey = doc.dockey doc_details.doc_id = doc.dockey doc_details.docname = doc.docname doc_details.key = doc.docname doc_details.citation = doc.citation - return doc_details + return extra_doc + doc_details # if we can't get metadata, just return the doc, but don't overwrite any fields prior_doc = doc.model_dump() prior_doc["overwrite_fields_from_metadata"] = False - return DocDetails(**prior_doc) + return DocDetails(**(prior_doc | extra_fields)) diff --git a/paperqa/clients/client_models.py b/paperqa/clients/client_models.py index bdb8b254..672f0d51 100644 --- a/paperqa/clients/client_models.py +++ b/paperqa/clients/client_models.py @@ -2,7 +2,8 @@ import logging from abc import ABC, abstractmethod -from typing import Any, Collection, Generic, TypeVar +from collections.abc import Collection +from typing import Any, Generic, TypeVar import aiohttp from pydantic import ( @@ -14,9 +15,9 @@ model_validator, ) -from paperqa.clients.exceptions import DOINotFoundError +from paperqa.types import DocDetails -from ..types import DocDetails +from .exceptions import DOINotFoundError logger = logging.getLogger(__name__) @@ -63,9 +64,17 @@ class DOIQuery(ClientQuery): @model_validator(mode="before") @classmethod - def ensure_fields_are_present(cls, data: dict[str, Any]) -> dict[str, Any]: + def add_doi_to_fields_and_validate(cls, data: dict[str, Any]) -> dict[str, Any]: + if (fields := data.get("fields")) and "doi" not in fields: fields.append("doi") + + # sometimes the DOI has a URL prefix, remove it + remove_urls = ["https://doi.org/", "http://dx.doi.org/"] + for url in remove_urls: + if data["doi"].startswith(url): + data["doi"] = data["doi"].replace(url, "") + return data @@ -101,16 +110,16 @@ async def query(self, query: dict) -> DocDetails | None: # DOINotFoundError means the paper doesn't exist in the source, the timeout is to prevent # this service from failing us when it's down or slow. except DOINotFoundError: - logger.exception( - f"Metadata not found for " - f"{client_query.doi if isinstance(client_query, DOIQuery) else client_query.title}" - " in Crossref." + logger.warning( + "Metadata not found for" + f" {client_query.doi if isinstance(client_query, DOIQuery) else client_query.title} in" + f" {self.__class__.__name__}." ) except TimeoutError: - logger.exception( - f"Request to Crossref for " - f"{client_query.doi if isinstance(client_query, DOIQuery) else client_query.title}" - " timed out." + logger.warning( + f"Request to {self.__class__.__name__} for" + f" {client_query.doi if isinstance(client_query, DOIQuery) else client_query.title} timed" + " out." ) return None diff --git a/paperqa/clients/crossref.py b/paperqa/clients/crossref.py index 7e587514..f7882cd2 100644 --- a/paperqa/clients/crossref.py +++ b/paperqa/clients/crossref.py @@ -1,24 +1,28 @@ from __future__ import annotations +import contextlib import copy import json import logging import os +from collections.abc import Collection from datetime import datetime -from typing import Any, Collection +from typing import Any from urllib.parse import quote import aiohttp -from ..clients.exceptions import DOINotFoundError -from ..types import CITATION_FALLBACK_DATA, DocDetails -from ..utils import ( +from paperqa.types import CITATION_FALLBACK_DATA, DocDetails +from paperqa.utils import ( bibtex_field_extract, + create_bibtex_key, remove_substrings, strings_similarity, union_collections_to_ordered_list, ) + from .client_models import DOIOrTitleBasedProvider, DOIQuery, TitleAuthorQuery +from .exceptions import DOINotFoundError logger = logging.getLogger(__name__) @@ -94,7 +98,8 @@ def crossref_headers() -> dict[str, str]: if api_key := os.environ.get("CROSSREF_API_KEY"): return {CROSSREF_HEADER_KEY: f"Bearer {api_key}"} logger.warning( - "CROSSREF_API_KEY environment variable not set. Crossref API rate limits may apply." + "CROSSREF_API_KEY environment variable not set. Crossref API rate limits may" + " apply." ) return {} @@ -136,12 +141,14 @@ async def doi_to_bibtex( ] # replace the key if all the fragments are present if all(fragments): - new_key = remove_substrings(("".join(fragments)), FORBIDDEN_KEY_CHARACTERS) + new_key = create_bibtex_key( + author=fragments[0].split(), year=fragments[1], title=fragments[2] + ) # we use the count parameter below to ensure only the 1st entry is replaced return data.replace(key, new_key, 1) -async def parse_crossref_to_doc_details( # noqa: C901 +async def parse_crossref_to_doc_details( message: dict[str, Any], session: aiohttp.ClientSession, query_bibtex: bool = True, @@ -150,7 +157,7 @@ async def parse_crossref_to_doc_details( # noqa: C901 bibtex_source = "self_generated" bibtex = None - try: + with contextlib.suppress(DOINotFoundError): # get the title from the message, if it exists # rare circumstance, but bibtex may not have a title fallback_data = copy.copy(CITATION_FALLBACK_DATA) @@ -168,9 +175,6 @@ async def parse_crossref_to_doc_details( # noqa: C901 # track the origin of the bibtex entry for debugging bibtex_source = "crossref" - except DOINotFoundError: - pass - authors = [ f"{author.get('given', '')} {author.get('family', '')}".strip() for author in message.get("author", []) @@ -225,7 +229,7 @@ async def parse_crossref_to_doc_details( # noqa: C901 return doc_details -async def get_doc_details_from_crossref( # noqa: C901, PLR0912 +async def get_doc_details_from_crossref( # noqa: PLR0912 session: aiohttp.ClientSession, doi: str | None = None, authors: list[str] | None = None, @@ -249,7 +253,8 @@ async def get_doc_details_from_crossref( # noqa: C901, PLR0912 if not (CROSSREF_MAILTO := os.getenv("CROSSREF_MAILTO")): logger.warning( - "CROSSREF_MAILTO environment variable not set. Crossref API rate limits may apply." + "CROSSREF_MAILTO environment variable not set. Crossref API rate limits may" + " apply." ) CROSSREF_MAILTO = "test@example.com" quoted_doi = f"/{quote(doi, safe='')}" if doi else "" @@ -265,7 +270,8 @@ async def get_doc_details_from_crossref( # noqa: C901, PLR0912 query_bibtex = True - if fields: + # note we only do field selection if querying on title + if fields and title: # crossref has a special endpoint for bibtex, so we don't need to request it here if "bibtex" not in fields: query_bibtex = False diff --git a/paperqa/clients/exceptions.py b/paperqa/clients/exceptions.py index 09178aa1..f4f3dd8a 100644 --- a/paperqa/clients/exceptions.py +++ b/paperqa/clients/exceptions.py @@ -1,4 +1,4 @@ class DOINotFoundError(Exception): - def __init__(self, message="DOI not found"): + def __init__(self, message="DOI not found") -> None: self.message = message super().__init__(self.message) diff --git a/paperqa/clients/journal_quality.py b/paperqa/clients/journal_quality.py index 2d9580a4..067df981 100644 --- a/paperqa/clients/journal_quality.py +++ b/paperqa/clients/journal_quality.py @@ -7,7 +7,8 @@ from pydantic import ValidationError -from ..types import DocDetails +from paperqa.types import DocDetails + from .client_models import JournalQuery, MetadataPostProcessor logger = logging.getLogger(__name__) @@ -44,7 +45,12 @@ async def _process( # remember, if both have docnames (i.e. key) they are # wiped and re-generated with resultant data return doc_details + DocDetails( # type: ignore[call-arg] - source_quality=self.data.get(query.journal.casefold(), -1) # type: ignore[union-attr] + source_quality=max( + [ + self.data.get(query.journal.casefold(), DocDetails.UNDEFINED_JOURNAL_QUALITY), # type: ignore[union-attr] + self.data.get("the " + query.journal.casefold(), DocDetails.UNDEFINED_JOURNAL_QUALITY), # type: ignore[union-attr] + ] + ) ) def query_creator(self, doc_details: DocDetails, **kwargs) -> JournalQuery | None: diff --git a/paperqa/clients/retractions.py b/paperqa/clients/retractions.py new file mode 100644 index 00000000..ffceb4dd --- /dev/null +++ b/paperqa/clients/retractions.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +import csv +import datetime +import logging +import os + +import aiohttp +from anyio import open_file +from pydantic import ValidationError +from tenacity import retry, stop_after_attempt, wait_exponential + +from paperqa.types import DocDetails + +from .client_models import DOIQuery, MetadataPostProcessor + +logger = logging.getLogger(__name__) + + +class RetrationDataPostProcessor(MetadataPostProcessor[DOIQuery]): + def __init__(self, retraction_data_path: os.PathLike | str | None = None) -> None: + + if retraction_data_path is None: + # Construct the path relative to module + self.retraction_data_path = str( + os.path.join( + os.path.dirname(__file__), "client_data", "retractions.csv" + ) + ) + else: + self.retraction_data_path = str(retraction_data_path) + + self.retraction_filter: str = "Retraction" + self.doi_set: set[str] = set() + self.columns: list[str] = [ + "RetractionDOI", + "OriginalPaperDOI", + "RetractionNature", + ] + + def _has_cache_expired(self) -> bool: + creation_time = os.path.getctime(self.retraction_data_path) + file_creation_date = datetime.datetime.fromtimestamp(creation_time).replace( + tzinfo=datetime.UTC + ) + + current_time = datetime.datetime.now(datetime.UTC) + time_difference = current_time - file_creation_date + + return time_difference > datetime.timedelta(days=30) + + def _is_csv_cached(self) -> bool: + return os.path.exists(self.retraction_data_path) + + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=5, min=5), + reraise=True, + ) + async def _download_retracted_dataset(self) -> None: + + if not (CROSSREF_MAILTO := os.getenv("CROSSREF_MAILTO")): + CROSSREF_MAILTO = "test@example.com" + url = f"https://api.labs.crossref.org/data/retractionwatch?{CROSSREF_MAILTO}" + + async with ( + aiohttp.ClientSession() as session, + session.get( + url, + timeout=aiohttp.ClientTimeout(total=300), + ) as response, + ): + response.raise_for_status() + + logger.info( + f"Retraction data was not cashed. Downloading retraction data from {url}..." + ) + + async with await open_file(self.retraction_data_path, "wb") as f: + while True: + chunk = await response.content.read(1024) + if not chunk: + break + await f.write(chunk) + + if os.path.getsize(self.retraction_data_path) == 0: + raise RuntimeError("Retraction data is empty") + + def _filter_dois(self) -> None: + with open(self.retraction_data_path, newline="", encoding="utf-8") as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + if row[self.columns[2]] == self.retraction_filter: + self.doi_set.add(row[self.columns[0]]) + self.doi_set.add(row[self.columns[1]]) + + async def load_data(self) -> None: + if not self._is_csv_cached() or self._has_cache_expired(): + await self._download_retracted_dataset() + + self._filter_dois() + + if not self.doi_set: + raise RuntimeError("Retraction data was not found.") + + async def _process(self, query: DOIQuery, doc_details: DocDetails) -> DocDetails: + if not self.doi_set: + await self.load_data() + + return doc_details + DocDetails( # type: ignore[call-arg] + is_retracted=query.doi in self.doi_set + ) + + def query_creator(self, doc_details: DocDetails, **kwargs) -> DOIQuery | None: + try: + return DOIQuery(doi=doc_details.doi, **kwargs) + except ValidationError: + logger.debug( + f"Must have a valid DOI to query retraction data:{doc_details.doi} " + ) + return None diff --git a/paperqa/clients/semantic_scholar.py b/paperqa/clients/semantic_scholar.py index d11fc9cb..80adfce2 100644 --- a/paperqa/clients/semantic_scholar.py +++ b/paperqa/clients/semantic_scholar.py @@ -2,21 +2,23 @@ import logging import os +from collections.abc import Collection from datetime import datetime from enum import IntEnum, auto from http import HTTPStatus from itertools import starmap -from typing import Any, Collection +from typing import Any import aiohttp -from ..types import DocDetails -from ..utils import ( +from paperqa.types import DocDetails +from paperqa.utils import ( _get_with_retrying, clean_upbibtex, strings_similarity, union_collections_to_ordered_list, ) + from .client_models import DOIOrTitleBasedProvider, DOIQuery, TitleAuthorQuery from .crossref import doi_to_bibtex from .exceptions import DOINotFoundError @@ -158,6 +160,8 @@ async def parse_s2_to_doc_details( if paper_data.get("publicationDate"): publication_date = datetime.strptime(paper_data["publicationDate"], "%Y-%m-%d") + journal_data = paper_data.get("journal") or {} + doc_details = DocDetails( # type: ignore[call-arg] key=None if not bibtex else bibtex.split("{")[1].split(",")[0], bibtex_type="article", # s2 should be basically all articles @@ -165,13 +169,13 @@ async def parse_s2_to_doc_details( authors=[author["name"] for author in paper_data.get("authors", [])], publication_date=publication_date, year=paper_data.get("year"), - volume=paper_data.get("journal", {}).get("volume"), - pages=paper_data.get("journal", {}).get("pages"), - journal=paper_data.get("journal", {}).get("name"), + volume=journal_data.get("volume"), + pages=journal_data.get("pages"), + journal=journal_data.get("name"), url=(paper_data.get("openAccessPdf") or {}).get("url"), title=paper_data.get("title"), citation_count=paper_data.get("citationCount"), - doi=paper_data.get("externalIds", {}).get("DOI"), + doi=(paper_data.get("externalIds") or {}).get("DOI"), other={}, # Initialize empty dict for other fields ) @@ -191,7 +195,8 @@ def semantic_scholar_headers() -> dict[str, str]: if api_key := os.environ.get("SEMANTIC_SCHOLAR_API_KEY"): return {SEMANTIC_SCHOLAR_HEADER_KEY: api_key} logger.warning( - "SEMANTIC_SCHOLAR_API_KEY environment variable not set. Semantic Scholar API rate limits may apply." + "SEMANTIC_SCHOLAR_API_KEY environment variable not set. Semantic Scholar API" + " rate limits may apply." ) return {} @@ -264,6 +269,9 @@ async def get_s2_doc_details_from_doi( session=session, headers=semantic_scholar_headers(), timeout=SEMANTIC_SCHOLAR_API_REQUEST_TIMEOUT, + http_exception_mappings={ + HTTPStatus.NOT_FOUND: DOINotFoundError(f"Could not find DOI for {doi}.") + }, ) return await parse_s2_to_doc_details(details, session) diff --git a/paperqa/clients/unpaywall.py b/paperqa/clients/unpaywall.py new file mode 100644 index 00000000..c275e6c6 --- /dev/null +++ b/paperqa/clients/unpaywall.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import os +from datetime import datetime +from http import HTTPStatus +from urllib.parse import quote + +import aiohttp +from pydantic import BaseModel, ConfigDict, ValidationError + +from paperqa.types import DocDetails +from paperqa.utils import _get_with_retrying, strings_similarity + +from .client_models import DOIOrTitleBasedProvider, DOIQuery, TitleAuthorQuery +from .exceptions import DOINotFoundError + +UNPAYWALL_BASE_URL = "https://api.unpaywall.org/v2/" +UNPAYWALL_TIMEOUT = float(os.environ.get("UNPAYWALL_TIMEOUT", "10.0")) # seconds + + +class Author(BaseModel): + family: str | None = None + given: str | None = None + sequence: str | None = None + affiliation: list[dict[str, str]] | None = None + model_config = ConfigDict(extra="allow") + + +class BestOaLocation(BaseModel): + updated: datetime | None = None + url: str | None = None + url_for_pdf: str | None = None + url_for_landing_page: str | None = None + evidence: str | None = None + license: str | None = None + version: str | None = None + host_type: str | None = None + is_best: bool | None = None + pmh_id: str | None = None + endpoint_id: str | None = None + repository_institution: str | None = None + oa_date: str | None = None + model_config = ConfigDict(extra="allow") + + +class UnpaywallResponse(BaseModel): + doi: str + doi_url: str | None = None + title: str | None = None + genre: str | None = None + is_paratext: bool | None = None + published_date: str | None = None + year: int | None = None + journal_name: str | None = None + journal_issns: str | None = None + journal_issn_l: str | None = None + journal_is_oa: bool | None = None + journal_is_in_doaj: bool | None = None + publisher: str | None = None + is_oa: bool + oa_status: str | None = None + has_repository_copy: bool | None = None + best_oa_location: BestOaLocation | None = None + updated: datetime | None = None + z_authors: list[Author] | None = None + + +class SearchResponse(BaseModel): + response: UnpaywallResponse + score: float + snippet: str + + +class SearchResults(BaseModel): + results: list[SearchResponse] + elapsed_seconds: float + + +class UnpaywallProvider(DOIOrTitleBasedProvider): + + async def get_doc_details( + self, doi: str, session: aiohttp.ClientSession + ) -> DocDetails: + + try: + results = UnpaywallResponse( + **( + await _get_with_retrying( + url=( + f"{UNPAYWALL_BASE_URL}{doi}?email={os.environ.get('UNPAYWALL_EMAIL', 'test@example.com')}" + ), + params={}, + session=session, + timeout=UNPAYWALL_TIMEOUT, + http_exception_mappings={ + HTTPStatus.NOT_FOUND: DOINotFoundError( + f"Unpaywall not find DOI for {doi}." + ) + }, + ) + ) + ) + except ValidationError as e: + raise DOINotFoundError( + f"Unpaywall results returned with a bad schema for DOI {doi!r}." + ) from e + + return self._create_doc_details(results) + + async def search_by_title( + self, + query: str, + session: aiohttp.ClientSession, + title_similarity_threshold: float = 0.75, + ) -> DocDetails: + try: + results = SearchResults( + **( + await _get_with_retrying( + url=( + f"{UNPAYWALL_BASE_URL}search?query={quote(query)}" + f"&email={os.environ.get('UNPAYWALL_EMAIL', 'test@example.com')}" + ), + params={}, + session=session, + timeout=UNPAYWALL_TIMEOUT, + http_exception_mappings={ + HTTPStatus.NOT_FOUND: DOINotFoundError( + f"Could not find DOI for {query}." + ) + }, + ) + ) + ).results + except ValidationError as e: + raise DOINotFoundError( + f"Unpaywall results returned with a bad schema for title {query!r}." + ) from e + + if not results: + raise DOINotFoundError( + f"Unpaywall results did not match for title {query!r}." + ) + + details = self._create_doc_details(results[0].response) + + if ( + strings_similarity( + details.title or "", + query, + ) + < title_similarity_threshold + ): + raise DOINotFoundError( + f"Unpaywall results did not match for title {query!r}." + ) + return details + + def _create_doc_details(self, data: UnpaywallResponse) -> DocDetails: + return DocDetails( # type: ignore[call-arg] + authors=[ + f"{author.given} {author.family}" for author in (data.z_authors or []) + ], + publication_date=( + None + if not data.published_date + else datetime.strptime(data.published_date, "%Y-%m-%d") + ), + year=data.year, + journal=data.journal_name, + publisher=data.publisher, + url=None if not data.best_oa_location else data.best_oa_location.url, + title=data.title, + doi=data.doi, + doi_url=data.doi_url, + other={ + "genre": data.genre, + "is_paratext": data.is_paratext, + "journal_issns": data.journal_issns, + "journal_issn_l": data.journal_issn_l, + "journal_is_oa": data.journal_is_oa, + "journal_is_in_doaj": data.journal_is_in_doaj, + "is_oa": data.is_oa, + "oa_status": data.oa_status, + "has_repository_copy": data.has_repository_copy, + "best_oa_location": ( + None + if not data.best_oa_location + else data.best_oa_location.model_dump() + ), + }, + ) + + async def _query(self, query: TitleAuthorQuery | DOIQuery) -> DocDetails | None: + if isinstance(query, DOIQuery): + return await self.get_doc_details(doi=query.doi, session=query.session) + return await self.search_by_title( + query=query.title, + session=query.session, + title_similarity_threshold=query.title_similarity_threshold, + ) diff --git a/paperqa/configs/contracrow.json b/paperqa/configs/contracrow.json new file mode 100644 index 00000000..208be7c3 --- /dev/null +++ b/paperqa/configs/contracrow.json @@ -0,0 +1,57 @@ +{ + "llm": "claude-3-5-sonnet-20240620", + "llm_config": null, + "summary_llm": "claude-3-5-sonnet-20240620", + "summary_llm_config": null, + "embedding": "hybrid-text-embedding-3-large", + "embedding_config": null, + "temperature": 0.0, + "batch_size": 1, + "texts_index_mmr_lambda": 1.0, + "index_absolute_directory": false, + "verbosity": 0, + "manifest_file": null, + "answer": { + "evidence_k": 30, + "evidence_detailed_citations": true, + "evidence_retrieval": true, + "evidence_summary_length": "about 300 words", + "evidence_skip_summary": false, + "answer_max_sources": 15, + "answer_length": "about 200 words, but can be longer", + "max_concurrent_requests": 4, + "answer_filter_extra_background": false + }, + "parsing": { + "chunk_size": 7000, + "use_doc_details": true, + "overlap": 250, + "citation_prompt": "Provide the citation for the following text in MLA Format. Do not write an introductory sentence. If reporting date accessed, the current year is 2024\n\n{text}\n\nCitation:", + "structured_citation_prompt": "Extract the title, authors, and doi as a JSON from this MLA citation. If any field can not be found, return it as null. Use title, authors, and doi as keys, author's value should be a list of authors. {citation}\n\nCitation JSON:", + "disable_doc_valid_check": false, + "chunking_algorithm": "simple_overlap" + }, + "prompts": { + "summary": "Summarize the excerpt below to help answer a question.\n\nExcerpt from {citation}\n\n----\n\n{text}\n\n----\n\nQuestion: {question}\n\nDo not directly answer the question, instead summarize to give evidence to help answer the question. Stay detailed; report specific numbers, equations, or direct quotes (marked with quotation marks). Reply \"Not applicable\" if the excerpt is irrelevant. At the end of your response, provide an integer score from 1-10 on a newline indicating relevance to question. Do not explain your score.\n\nRelevant Information Summary ({summary_length}):", + "qa": "Determine if the claim below is contradicted by the context below\n\n\n{context}\n\n----\n\nClaim: {question}\n\n\nDetermine if the claim is contradicted by the context. For each part of your response, indicate which sources most support it via citation keys at the end of sentences, like (Example2012Example pages 3-4). Only cite from the context below and only use the valid keys.\n\nRespond with the following XML format:\n\n\n ...\n \n\n\n\nwhere `reasoning` is your reasoning ({answer_length}) about if the claim is being contradicted. `label` is one of the following (must match exactly): \n\nexplicit contradiction\nstrong contradiction\ncontradiction\nnuanced contradiction\npossibly a contradiction\nlack of evidence\npossibly an agreement\nnuanced agreement\nagreement\nstrong agreement\nexplicit agreement\n\nDon't worry about other contradictions or agreements in the context, only focus on the specific claim. If there is no evidence for the claim, you should choose lack of evidence.", + "select": "Select papers that may help answer the question below. Papers are listed as $KEY: $PAPER_INFO. Return a list of keys, separated by commas. Return \"None\", if no papers are applicable. Choose papers that are relevant, from reputable sources, and timely (if the question requires timely information).\n\nQuestion: {question}\n\nPapers: {papers}\n\nSelected keys:", + "pre": null, + "post": null, + "system": "Answer in a direct and concise tone. Your audience is an expert, so be highly specific. If there are ambiguous terms or acronyms, first define them.", + "use_json": true, + "summary_json": "Excerpt from {citation}\n\n----\n\n{text}\n\n----\n\nQuestion: {question}\n\n", + "summary_json_system": "Provide a summary of the relevant information that could help determine if a claim is contradicted or supported by this excerpt. The excerpt may be irrelevant. Do not directly answer if it is contradicted - only summarize relevant information. Respond with the following JSON format:\n\n{{\n \"summary\": \"...\",\n \"relevance_score\": \"...\"\n}}\n\nwhere `summary` is relevant information from excerpt ({summary_length}) and `relevance_score` is the relevance of `summary` to support or contradict the claim (integer out of 10). If any string entry in the JSON has newlines, be sure to escape them. " + }, + "agent": { + "agent_llm": "gpt-4o-2024-08-06", + "agent_type": "ToolSelector", + "agent_system_prompt": "You are a helpful AI assistant.", + "agent_prompt": "Answer question: {question}\n\nSearch for papers, gather evidence, collect papers cited in evidence then re-gather evidence, and answer. Gathering evidence will do nothing if you have not done a new search or collected new papers. If you do not have enough evidence to generate a good answer, you can:\n- Search for more papers (preferred)\n- Collect papers cited by previous evidence (preferred)\n- Gather more evidence using a different phrase\nIf you search for more papers or collect new papers cited by previous evidence, remember to gather evidence again. Once you have five or more pieces of evidence from multiple sources, or you have tried a few times, call {gen_answer_tool_name} tool. The {gen_answer_tool_name} tool output is visible to the user, so you do not need to restate the answer and can simply terminate if the answer looks sufficient. The current status of evidence/papers/cost is {status}", + "search_count": 12, + "wipe_context_on_answer_failure": true, + "timeout": 500.0, + "should_pre_search": false, + "tool_names": null, + "index_concurrency": 30 + } +} diff --git a/paperqa/configs/debug.json b/paperqa/configs/debug.json new file mode 100644 index 00000000..f560bb99 --- /dev/null +++ b/paperqa/configs/debug.json @@ -0,0 +1,15 @@ +{ + "llm": "text-completion-openai/babbage-002", + "summary_llm": "text-completion-openai/babbage-002", + "answer": { + "evidence_k": 2, + "evidence_detailed_citations": false, + "evidence_summary_length": "25 to 50 words", + "answer_max_sources": 2, + "answer_length": "50 to 100 words", + "max_concurrent_requests": 5 + }, + "parsing": { + "use_doc_details": false + } +} diff --git a/paperqa/configs/fast.json b/paperqa/configs/fast.json new file mode 100644 index 00000000..c7b2252b --- /dev/null +++ b/paperqa/configs/fast.json @@ -0,0 +1,13 @@ +{ + "answer": { + "evidence_k": 5, + "evidence_detailed_citations": false, + "evidence_summary_length": "25 to 50 words", + "answer_max_sources": 3, + "answer_length": "50 to 100 words", + "max_concurrent_requests": 5 + }, + "parsing": { + "use_doc_details": false + } +} diff --git a/paperqa/configs/high_quality.json b/paperqa/configs/high_quality.json new file mode 100644 index 00000000..a4160a8d --- /dev/null +++ b/paperqa/configs/high_quality.json @@ -0,0 +1,18 @@ +{ + "answer": { + "evidence_k": 20, + "answer_max_sources": 5, + "max_concurrent_requests": 10 + }, + "parsing": { + "use_doc_details": true, + "chunk_size": 7000, + "overlap": 250 + }, + "prompts": { + "use_json": true + }, + "agent": { + "agent_type": "ToolSelector" + } +} diff --git a/paperqa/configs/wikicrow.json b/paperqa/configs/wikicrow.json new file mode 100644 index 00000000..ccdd1f05 --- /dev/null +++ b/paperqa/configs/wikicrow.json @@ -0,0 +1,57 @@ +{ + "llm": "gpt-4-turbo-2024-04-09", + "llm_config": null, + "summary_llm": "gpt-4-turbo-2024-04-09", + "summary_llm_config": null, + "embedding": "hybrid-text-embedding-3-small", + "embedding_config": null, + "temperature": 0.0, + "batch_size": 1, + "texts_index_mmr_lambda": 1.0, + "index_absolute_directory": false, + "verbosity": 0, + "manifest_file": null, + "answer": { + "evidence_k": 25, + "evidence_detailed_citations": true, + "evidence_retrieval": true, + "evidence_summary_length": "about 300 words", + "evidence_skip_summary": false, + "answer_max_sources": 12, + "answer_length": "about 200 words, but can be longer", + "max_concurrent_requests": 4, + "answer_filter_extra_background": false + }, + "parsing": { + "chunk_size": 7000, + "use_doc_details": true, + "overlap": 1750, + "citation_prompt": "Provide the citation for the following text in MLA Format. Do not write an introductory sentence. If reporting date accessed, the current year is 2024\n\n{text}\n\nCitation:", + "structured_citation_prompt": "Extract the title, authors, and doi as a JSON from this MLA citation. If any field can not be found, return it as null. Use title, authors, and doi as keys, author's value should be a list of authors. {citation}\n\nCitation JSON:", + "disable_doc_valid_check": false, + "chunking_algorithm": "simple_overlap" + }, + "prompts": { + "summary": "Summarize the excerpt below to help answer a question.\n\nExcerpt from {citation}\n\n----\n\n{text}\n\n----\n\nQuestion: {question}\n\nDo not directly answer the question, instead summarize to give evidence to help answer the question. Stay detailed; report specific numbers, equations, or direct quotes (marked with quotation marks). Reply \"Not applicable\" if the excerpt is irrelevant. At the end of your response, provide an integer score from 1-10 on a newline indicating relevance to question. Do not explain your score.\n\nRelevant Information Summary ({summary_length}):", + "qa": "Answer the question below with the context.\n\nContext (with relevance scores):\n\n{context}\n\n----\n\nQuestion: {question}\n\nWrite an answer based on the context. If the context provides insufficient information and the question cannot be directly answered, reply \"I cannot answer.\" For each part of your answer, indicate which sources most support it via citation keys at the end of sentences, like (Example2012Example pages 3-4). Only cite from the context below and only use the valid keys. Write in the style of a Wikipedia article, with concise sentences and coherent paragraphs. The context comes from a variety of sources and is only a summary, so there may inaccuracies or ambiguities. Make sure the gene_names exactly match the gene name in the question before using a context. If quotes are present and relevant, use them in the answer. This answer will go directly onto Wikipedia, so do not add any extraneous information. Do not reference this prompt or your context. Do not include general summary information, it will be provided in an \"Overview\" section. Do not include the section title in your output. Avoid using adverb phrases like \"furthermore\", \"additionally\", and \"moreover\".\n\nAnswer ({answer_length}):", + "select": "Select papers that may help answer the question below. Papers are listed as $KEY: $PAPER_INFO. Return a list of keys, separated by commas. Return \"None\", if no papers are applicable. Choose papers that are relevant, from reputable sources, and timely (if the question requires timely information).\n\nQuestion: {question}\n\nPapers: {papers}\n\nSelected keys:", + "pre": null, + "post": null, + "system": "Answer in a direct and concise tone.", + "use_json": true, + "summary_json": "Excerpt from {citation}\n\n----\n\n{text}\n\n----\n\nQuestion: {question}\n\n", + "summary_json_system": "Provide a summary of the relevant information that could help answer the question based on the excerpt. The excerpt may be irrelevant. Do not directly answer the question - only summarize relevant information. \n\nRespond with the following JSON format:\n\n{{\n \"summary\": \"...\",\n \"relevance_score\": \"...\",\n \"gene_name: \"...\"\n}}\n\nwhere `summary` is relevant information from text - {summary_length}, \n`gene_name` is the gene discussed in the excerpt (may be different than query), and `relevance_score` is the relevance of `summary` to answer the question (integer out of 10)" + }, + "agent": { + "agent_llm": "gpt-4-turbo-2024-04-09", + "agent_type": "ToolSelector", + "agent_system_prompt": "You are a helpful AI assistant.", + "agent_prompt": "Answer question: {question}\n\nSearch for papers, gather evidence, collect papers cited in evidence then re-gather evidence, and answer. Gathering evidence will do nothing if you have not done a new search or collected new papers. If you do not have enough evidence to generate a good answer, you can:\n- Search for more papers (preferred)\n- Collect papers cited by previous evidence (preferred)\n- Gather more evidence using a different phrase\nIf you search for more papers or collect new papers cited by previous evidence, remember to gather evidence again. Once you have five or more pieces of evidence from multiple sources, or you have tried a few times, call {gen_answer_tool_name} tool. The {gen_answer_tool_name} tool output is visible to the user, so you do not need to restate the answer and can simply terminate if the answer looks sufficient. The current status of evidence/papers/cost is {status}", + "search_count": 12, + "wipe_context_on_answer_failure": true, + "timeout": 500.0, + "should_pre_search": false, + "tool_names": null, + "index_concurrency": 30 + } +} diff --git a/paperqa/contrib/zotero.py b/paperqa/contrib/zotero.py index 7bc0f346..c9dd94ff 100644 --- a/paperqa/contrib/zotero.py +++ b/paperqa/contrib/zotero.py @@ -1,17 +1,21 @@ -# This file gets PDF files from the user's Zotero library +"""This module gets PDF files from the user's Zotero library.""" + import logging import os from pathlib import Path -from typing import List, Optional, Union, cast +from typing import cast from pydantic import BaseModel try: from pyzotero import zotero -except ImportError: - raise ImportError("Please install pyzotero: `pip install pyzotero`") # noqa: B904 -from ..paths import PAPERQA_DIR -from ..utils import StrPath, count_pdf_pages +except ImportError as e: + raise ImportError( + "zotero requires the 'zotero' extra for 'pyzotero'. Please:" + " `pip install paper-qa[zotero]`." + ) from e +from paperqa.paths import PAPERQA_DIR +from paperqa.utils import StrPath, count_pdf_pages class ZoteroPaper(BaseModel): @@ -43,9 +47,9 @@ class ZoteroPaper(BaseModel): def __str__(self) -> str: """Return the title of the paper.""" return ( - f'ZoteroPaper(\n key = "{self.key}",\n' - f'title = "{self.title}",\n pdf = "{self.pdf}",\n ' - f'num_pages = {self.num_pages},\n zotero_key = "{self.zotero_key}",\n details = ...\n)' + f'ZoteroPaper(\n key = "{self.key}",\ntitle = "{self.title}",\n pdf =' + f' "{self.pdf}",\n num_pages = {self.num_pages},\n zotero_key =' + f' "{self.zotero_key}",\n details = ...\n)' ) @@ -65,9 +69,9 @@ def __init__( self, *, library_type: str = "user", - library_id: Optional[str] = None, # noqa: FA100 - api_key: Optional[str] = None, # noqa: FA100 - storage: Optional[StrPath] = None, # noqa: FA100 + library_id: str | None = None, + api_key: str | None = None, + storage: StrPath | None = None, **kwargs, ): self.logger = logging.getLogger("ZoteroDB") @@ -81,8 +85,7 @@ def __init__( " from the text 'Your userID for use in API calls is [XXXXXX]'." " Then, set the environment variable ZOTERO_USER_ID to this value." ) - else: # noqa: RET506 - library_id = os.environ["ZOTERO_USER_ID"] + library_id = os.environ["ZOTERO_USER_ID"] if api_key is None: self.logger.info("Attempting to get ZOTERO_API_KEY from `os.environ`...") @@ -93,8 +96,7 @@ def __init__( " with access to your library." " Then, set the environment variable ZOTERO_API_KEY to this value." ) - else: # noqa: RET506 - api_key = os.environ["ZOTERO_API_KEY"] + api_key = os.environ["ZOTERO_API_KEY"] self.logger.info(f"Using library ID: {library_id} with type: {library_type}.") @@ -108,7 +110,7 @@ def __init__( library_type=library_type, library_id=library_id, api_key=api_key, **kwargs ) - def get_pdf(self, item: dict) -> Union[Path, None]: # noqa: FA100 + def get_pdf(self, item: dict) -> Path | None: """Gets a filename for a given Zotero key for a PDF. If the PDF is not found locally, the PDF will be downloaded to a local file at the correct key. @@ -120,7 +122,7 @@ def get_pdf(self, item: dict) -> Union[Path, None]: # noqa: FA100 An item from `pyzotero`. Should have a `key` field, and also have an entry `links->attachment->attachmentType == application/pdf`. """ - if type(item) != dict: # noqa: E721 + if not isinstance(item, dict): raise TypeError("Pass the full item of the paper. The item must be a dict.") pdf_key = _extract_pdf_key(item) @@ -137,17 +139,17 @@ def get_pdf(self, item: dict) -> Union[Path, None]: # noqa: FA100 return pdf_path - def iterate( # noqa: C901, PLR0912 + def iterate( # noqa: PLR0912 self, limit: int = 25, start: int = 0, - q: Optional[str] = None, # noqa: FA100 - qmode: Optional[str] = None, # noqa: FA100 - since: Optional[str] = None, # noqa: FA100 - tag: Optional[str] = None, # noqa: FA100 - sort: Optional[str] = None, # noqa: FA100 - direction: Optional[str] = None, # noqa: FA100 - collection_name: Optional[str] = None, # noqa: FA100 + q: str | None = None, + qmode: str | None = None, + since: str | None = None, + tag: str | None = None, + sort: str | None = None, + direction: str | None = None, + collection_name: str | None = None, ): """Given a search query, this will lazily iterate over papers in a Zotero library, downloading PDFs as needed. @@ -203,15 +205,16 @@ def iterate( # noqa: C901, PLR0912 if direction is not None: query_kwargs["direction"] = direction - if collection_name is not None and len(query_kwargs) > 0: + if collection_name is not None and query_kwargs: raise ValueError( - "You cannot specify a `collection_name` and search query simultaneously!" + "You cannot specify a `collection_name` and search query" + " simultaneously!" ) max_limit = 100 - items: List = [] # noqa: FA100 - pdfs: List[Path] = [] # noqa: FA100 + items: list = [] + pdfs: list[Path] = [] i = 0 actual_i = 0 num_remaining = limit @@ -240,7 +243,7 @@ def iterate( # noqa: C901, PLR0912 _pdfs = [self.get_pdf(item) for item in _items] # Filter: - for item, pdf in zip(_items, _pdfs): + for item, pdf in zip(_items, _pdfs, strict=True): no_pdf = item is None or pdf is None is_duplicate = pdf in pdfs @@ -305,8 +308,7 @@ def _get_collection_id(self, collection_name: str) -> str: def _get_citation_key(item: dict) -> str: if ( - "data" not in item - or "creators" not in item["data"] + "creators" not in item.get("data", {}) or len(item["data"]["creators"]) == 0 or "lastName" not in item["data"]["creators"][0] or "title" not in item["data"] @@ -326,7 +328,7 @@ def _get_citation_key(item: dict) -> str: return f"{last_name}_{short_title}_{date}_{item['key']}".replace(" ", "") -def _extract_pdf_key(item: dict) -> Union[str, None]: # noqa: FA100 +def _extract_pdf_key(item: dict) -> str | None: """Extract the PDF key from a Zotero item.""" if "links" not in item: return None @@ -336,7 +338,7 @@ def _extract_pdf_key(item: dict) -> Union[str, None]: # noqa: FA100 attachments = item["links"]["attachment"] - if type(attachments) != dict: # noqa: E721 + if not isinstance(attachments, dict): # Find first attachment with attachmentType == application/pdf: for attachment in attachments: # TODO: This assumes there's only one PDF attachment. diff --git a/paperqa/core.py b/paperqa/core.py new file mode 100644 index 00000000..231f92b2 --- /dev/null +++ b/paperqa/core.py @@ -0,0 +1,114 @@ +from __future__ import annotations + +import json +import re +from collections.abc import Callable +from typing import Any + +from paperqa.llms import PromptRunner +from paperqa.types import Context, LLMResult, Text +from paperqa.utils import extract_score, strip_citations + + +def llm_parse_json(text: str) -> dict: + """Read LLM output and extract JSON data from it.""" + # fetch from markdown ```json if present + text = text.strip().split("```json")[-1].split("```")[0] + # split anything before the first { + text = "{" + text.split("{", 1)[-1] + # split anything after the last } + text = text.rsplit("}", 1)[0] + "}" + + # escape new lines within strings + def replace_newlines(match: re.Match) -> str: + return match.group(0).replace("\n", "\\n") + + # Match anything between double quotes + # including escaped quotes and other escaped characters. + # https://regex101.com/r/VFcDmB/1 + pattern = r'"(?:[^"\\]|\\.)*"' + text = re.sub(pattern, replace_newlines, text) + + return json.loads(text) + + +async def map_fxn_summary( + text: Text, + question: str, + prompt_runner: PromptRunner | None, + extra_prompt_data: dict[str, str] | None = None, + parser: Callable[[str], dict[str, Any]] | None = None, + callbacks: list[Callable[[str], None]] | None = None, +) -> tuple[Context, LLMResult]: + """Parses the given text and returns a context object with the parser and prompt runner. + + The parser should at least return a dict with `summary`. A `relevant_score` will be used and any + extra fields except `question` will be added to the context object. `question` is stripped + because it can be incorrectly parsed from LLM outputs when parsing them as JSON. + + Args: + text: The text to parse. + question: The question to use for the chain. + prompt_runner: The prompt runner to call - should have question, citation, + summary_length, and text fields. + extra_prompt_data: Optional extra kwargs to pass to the prompt runner's data. + parser: The parser to use for parsing - return empty dict on Failure to fallback to text parsing. + callbacks: LLM callbacks to execute in the prompt runner. + + Returns: + The context object and LLMResult to get info about the LLM execution. + """ + # needed empties for failures/skips + llm_result = LLMResult(model="", date="") + extras: dict[str, Any] = {} + citation = text.name + ": " + text.doc.citation + success = False + + if prompt_runner: + llm_result = await prompt_runner( + {"question": question, "citation": citation, "text": text.text} + | (extra_prompt_data or {}), + callbacks, + "evidence:" + text.name, + ) + context = llm_result.text + result_data = parser(context) if parser else {} + success = bool(result_data) + if success: + try: + context = result_data.pop("summary") + score = ( + result_data.pop("relevance_score") + if "relevance_score" in result_data + else extract_score(context) + ) + # just in case question was present + result_data.pop("question", None) + extras = result_data + except KeyError: + success = False + else: + context = text.text + # If we don't assign scores, just default to 5. + # why 5? Because we filter out 0s in another place + # and 5/10 is the other default I could come up with + score = 5 + success = True + # remove citations that collide with our grounded citations (for the answer LLM) + context = strip_citations(context) + if not success: + score = extract_score(context) + + return ( + Context( + context=context, + text=Text( + text=text.text, + name=text.name, + doc=text.doc.__class__(**text.doc.model_dump(exclude={"embedding"})), + ), + score=score, # pylint: disable=possibly-used-before-assignment + **extras, + ), + llm_result, + ) diff --git a/paperqa/docs.py b/paperqa/docs.py index 214653f0..ad2fc595 100644 --- a/paperqa/docs.py +++ b/paperqa/docs.py @@ -5,68 +5,60 @@ import os import re import tempfile +from collections.abc import Callable from datetime import datetime +from functools import partial from io import BytesIO from pathlib import Path -from typing import Any, BinaryIO, Callable, Coroutine, cast +from typing import Any, BinaryIO, cast from uuid import UUID, uuid4 -from openai import AsyncOpenAI -from pydantic import BaseModel, ConfigDict, Field, model_validator - -try: - import voyageai - - USE_VOYAGE = True -except ImportError: - USE_VOYAGE = False +from pydantic import ( + BaseModel, + ConfigDict, + Field, + ValidationInfo, + field_validator, +) -from .clients import ALL_CLIENTS, DocMetadataClient -from .llms import ( - HybridEmbeddingModel, +from paperqa.clients import DEFAULT_CLIENTS, DocMetadataClient +from paperqa.core import llm_parse_json, map_fxn_summary +from paperqa.llms import ( + EmbeddingModel, LLMModel, NumpyVectorStore, - OpenAIEmbeddingModel, - OpenAILLMModel, + PromptRunner, VectorStore, - VoyageAIEmbeddingModel, - get_score, - is_anyscale_model, - llm_model_factory, - vector_store_factory, ) -from .paths import PAPERQA_DIR -from .readers import read_doc -from .types import ( +from paperqa.paths import PAPERQA_DIR +from paperqa.readers import read_doc +from paperqa.settings import MaybeSettings, get_settings +from paperqa.types import ( Answer, - CallbackFactory, - Context, Doc, DocDetails, DocKey, LLMResult, - PromptCollection, Text, + set_llm_answer_ids, ) -from .utils import ( +from paperqa.utils import ( gather_with_concurrency, get_loop, - llm_read_json, maybe_is_html, maybe_is_pdf, maybe_is_text, md5sum, name_in_text, - strip_citations, ) # this is just to reduce None checks/type checks -async def empty_callback(result: LLMResult): # noqa: ARG001 +async def empty_callback(result: LLMResult) -> None: pass -async def print_callback(result: LLMResult): # noqa: ARG001 +async def print_callback(result: LLMResult) -> None: pass @@ -75,176 +67,29 @@ class Docs(BaseModel): model_config = ConfigDict(extra="forbid") - # ephemeral vars that should not be pickled (_things) - _client: Any | None = None - _embedding_client: Any | None = None id: UUID = Field(default_factory=uuid4) - llm: str = "default" - summary_llm: str | None = None - llm_model: LLMModel = Field( - default=OpenAILLMModel( - config={"model": "gpt-4-0125-preview", "temperature": 0.1} - ) - ) - summary_llm_model: LLMModel | None = Field(default=None, validate_default=True) - embedding: str | None = "default" docs: dict[DocKey, Doc | DocDetails] = {} texts: list[Text] = [] docnames: set[str] = set() texts_index: VectorStore = Field(default_factory=NumpyVectorStore) - docs_index: VectorStore = Field(default_factory=NumpyVectorStore) - name: str = "default" - index_path: Path | None = PAPERQA_DIR / name - batch_size: int = 1 - max_concurrent: int = 4 - deleted_dockeys: set[DocKey] = set() - prompts: PromptCollection = PromptCollection() - jit_texts_index: bool = False - # This is used to strip indirect citations that come up from the summary llm - strip_citations: bool = True - llm_result_callback: Callable[[LLMResult], Coroutine[Any, Any, None]] = Field( - default=empty_callback + name: str = Field(default="default", description="Name of this docs collection") + index_path: Path | None = Field( + default=PAPERQA_DIR, description="Path to save index", validate_default=True ) + deleted_dockeys: set[DocKey] = set() - def __init__(self, **data): - # We do it here because we need to move things to private attributes - embedding_client: Any | None = None - client: Any | None = None - if "embedding_client" in data: - embedding_client = data.pop("embedding_client") - if "client" in data: - client = data.pop("client") - # backwards compatibility - if "doc_index" in data: - data["docs_index"] = data.pop("doc_index") - super().__init__(**data) - self.set_client(client, embedding_client) - - @model_validator(mode="before") + @field_validator("index_path") @classmethod - def setup_alias_models(cls, data: Any) -> Any: - if isinstance(data, dict): - if "llm" in data and data["llm"] != "default": - data["llm_model"] = llm_model_factory(data["llm"]) - if "summary_llm" in data and data["summary_llm"] is not None: - data["summary_llm_model"] = llm_model_factory(data["summary_llm"]) - if "embedding" in data and data["embedding"] != "default": - if "texts_index" not in data: - data["texts_index"] = vector_store_factory(data["embedding"]) - if "docs_index" not in data: - data["docs_index"] = vector_store_factory(data["embedding"]) - return data - - @model_validator(mode="after") - @classmethod - def config_summary_llm_config(cls, data: Any) -> Any: - if isinstance(data, Docs): - # check our default gpt-4/3.5-turbo config - # default check is hard - becauise either llm is set or llm_model is set - if ( - data.summary_llm_model is None - and data.llm == "default" - and isinstance(data.llm_model, OpenAILLMModel) - ): - data.summary_llm_model = OpenAILLMModel( - config={"model": "gpt-4o-mini", "temperature": 0.1} - ) - elif data.summary_llm_model is None: - data.summary_llm_model = data.llm_model - return data + def handle_default(cls, value: Path | None, info: ValidationInfo) -> Path | None: + if value == PAPERQA_DIR: + return PAPERQA_DIR / info.data["name"] + return value - @classmethod - def make_llm_names_consistent(cls, data: Any) -> Any: - if isinstance(data, Docs): - data.llm = data.llm_model.name - if data.llm == "langchain": - # from langchain models - kind of hacky - # langchain models cannot know type until - # it sees client - data.llm_model.infer_llm_type(data._client) - data.llm = data.llm_model.name - if data.summary_llm_model is not None: - if ( - data.summary_llm is None - and data.summary_llm_model is data.llm_model - ): - data.summary_llm = data.llm - if data.summary_llm == "langchain": - # from langchain models - kind of hacky - data.summary_llm_model.infer_llm_type(data._client) - data.summary_llm = data.summary_llm_model.name - data.embedding = data.texts_index.embedding_model.name - return data - - def clear_docs(self): + def clear_docs(self) -> None: self.texts = [] self.docs = {} self.docnames = set() - def __getstate__(self): - # You may wonder why make these private if we're just going - # to be overriding the behavior on setstaet/getstate anyway. - # The reason is that the other serialization methods from Pydantic - - # model_dump - will not drop private attributes. - # So - this getstate/setstate removes private attributes for pickling - # and Pydantic will handle removing private attributes for other - # serialization methods (like model_dump) - state = super().__getstate__() - # remove client from private attributes - del state["__pydantic_private__"]["_client"] - del state["__pydantic_private__"]["_embedding_client"] - return state - - def __setstate__(self, state): - # add client back to private attributes - state["__pydantic_private__"]["_client"] = None - state["__pydantic_private__"]["_embedding_client"] = None - super().__setstate__(state) - - def set_client( - self, - client: Any | None = None, - embedding_client: Any | None = None, - ): - if client is None and isinstance(self.llm_model, OpenAILLMModel): - if is_anyscale_model(self.llm_model.name): - client = AsyncOpenAI( - api_key=os.environ["ANYSCALE_API_KEY"], - base_url=os.environ["ANYSCALE_BASE_URL"], - ) - else: - client = AsyncOpenAI() - self._client = client - if embedding_client is None: - # check if we have an openai embedding model in use - if isinstance(self.texts_index.embedding_model, OpenAIEmbeddingModel) or ( - isinstance(self.texts_index.embedding_model, HybridEmbeddingModel) - and any( - isinstance(m, OpenAIEmbeddingModel) - for m in self.texts_index.embedding_model.models - ) - ): - embedding_client = ( - client if isinstance(client, AsyncOpenAI) else AsyncOpenAI() - ) - elif USE_VOYAGE and ( - isinstance(self.texts_index.embedding_model, VoyageAIEmbeddingModel) - or ( - isinstance(self.texts_index.embedding_model, HybridEmbeddingModel) - and any( - isinstance(m, VoyageAIEmbeddingModel) - for m in self.texts_index.embedding_model.models - ) - ) - ): - embedding_client = ( - client - if isinstance(client, voyageai.AsyncClient) - else voyageai.AsyncClient() - ) - self._embedding_client = embedding_client - Docs.make_llm_names_consistent(self) - def _get_unique_name(self, docname: str) -> str: """Create a unique name given proposed name.""" suffix = "" @@ -260,16 +105,19 @@ def add_file( citation: str | None = None, docname: str | None = None, dockey: DocKey | None = None, - chunk_chars: int = 3000, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, ) -> str | None: - loop = get_loop() - return loop.run_until_complete( + return get_loop().run_until_complete( self.aadd_file( file, citation=citation, docname=docname, dockey=dockey, - chunk_chars=chunk_chars, + settings=settings, + llm_model=llm_model, + embedding_model=embedding_model, ) ) @@ -279,11 +127,12 @@ async def aadd_file( citation: str | None = None, docname: str | None = None, dockey: DocKey | None = None, - chunk_chars: int = 3000, title: str | None = None, doi: str | None = None, authors: list[str] | None = None, - use_doc_details: bool = False, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, **kwargs, ) -> str | None: """Add a document to the collection.""" @@ -302,11 +151,12 @@ async def aadd_file( citation=citation, docname=docname, dockey=dockey, - chunk_chars=chunk_chars, title=title, doi=doi, authors=authors, - use_doc_details=use_doc_details, + settings=settings, + llm_model=llm_model, + embedding_model=embedding_model, **kwargs, ) @@ -316,16 +166,19 @@ def add_url( citation: str | None = None, docname: str | None = None, dockey: DocKey | None = None, - chunk_chars: int = 3000, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, ) -> str | None: - loop = get_loop() - return loop.run_until_complete( + return get_loop().run_until_complete( self.aadd_url( url, citation=citation, docname=docname, dockey=dockey, - chunk_chars=chunk_chars, + settings=settings, + llm_model=llm_model, + embedding_model=embedding_model, ) ) @@ -335,7 +188,9 @@ async def aadd_url( citation: str | None = None, docname: str | None = None, dockey: DocKey | None = None, - chunk_chars: int = 3000, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, ) -> str | None: """Add a document to the collection.""" import urllib.request @@ -348,7 +203,9 @@ async def aadd_url( citation=citation, docname=docname, dockey=dockey, - chunk_chars=chunk_chars, + settings=settings, + llm_model=llm_model, + embedding_model=embedding_model, ) def add( @@ -356,63 +213,69 @@ def add( path: Path, citation: str | None = None, docname: str | None = None, - disable_check: bool = False, dockey: DocKey | None = None, - chunk_chars: int = 3000, title: str | None = None, doi: str | None = None, authors: list[str] | None = None, - use_doc_details: bool = False, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, **kwargs, ) -> str | None: - loop = get_loop() - return loop.run_until_complete( + return get_loop().run_until_complete( self.aadd( path, citation=citation, docname=docname, - disable_check=disable_check, dockey=dockey, - chunk_chars=chunk_chars, title=title, doi=doi, authors=authors, - use_doc_details=use_doc_details, + settings=settings, + llm_model=llm_model, + embedding_model=embedding_model, **kwargs, ) ) - async def aadd( # noqa: C901, PLR0912, PLR0915 + async def aadd( # noqa: PLR0912 self, path: Path, citation: str | None = None, docname: str | None = None, - disable_check: bool = False, dockey: DocKey | None = None, - chunk_chars: int = 3000, title: str | None = None, doi: str | None = None, authors: list[str] | None = None, - use_doc_details: bool = False, + settings: MaybeSettings = None, + llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, **kwargs, ) -> str | None: """Add a document to the collection.""" + all_settings = get_settings(settings) + parse_config = all_settings.parsing if dockey is None: + # md5 sum of file contents (not path!) dockey = md5sum(path) + if llm_model is None: + llm_model = all_settings.get_llm() if citation is None: - # skip system because it's too hesitant to answer - cite_chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.cite, - skip_system=True, + # Peek first chunk + texts = read_doc( + path, + Doc(docname="", citation="", dockey=dockey), # Fake doc + chunk_chars=parse_config.chunk_size, + overlap=parse_config.overlap, ) - # peak first chunk - fake_doc = Doc(docname="", citation="", dockey=dockey) - texts = read_doc(path, fake_doc, chunk_chars=chunk_chars, overlap=100) - if len(texts) == 0: + if not texts: raise ValueError(f"Could not read document {path}. Is it empty?") - chain_result = await cite_chain({"text": texts[0].text}, None) - citation = chain_result.text + result = await llm_model.run_prompt( + prompt=parse_config.citation_prompt, + data={"text": texts[0].text}, + skip_system=True, # skip system because it's too hesitant to answer + ) + citation = result.text if ( len(citation) < 3 # noqa: PLR2004 or "Unknown" in citation @@ -442,33 +305,34 @@ async def aadd( # noqa: C901, PLR0912, PLR0915 doc = Doc(docname=docname, citation=citation, dockey=dockey) # try to extract DOI / title from the citation - if (doi is title is None) and use_doc_details: - structured_cite_chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.structured_cite, + if (doi is title is None) and parse_config.use_doc_details: + result = await llm_model.run_prompt( + prompt=parse_config.structured_citation_prompt, + data={"citation": citation}, skip_system=True, ) - chain_result = await structured_cite_chain({"citation": citation}, None) with contextlib.suppress(json.JSONDecodeError): - citation_json = json.loads(chain_result.text) + clean_text = result.text.strip("`") + if clean_text.startswith("json"): + clean_text = clean_text.replace("json", "", 1) + citation_json = json.loads(clean_text) if citation_title := citation_json.get("title"): title = citation_title if citation_doi := citation_json.get("doi"): doi = citation_doi if citation_author := citation_json.get("authors"): authors = citation_author - # see if we can upgrade to DocDetails # if not, we can progress with a normal Doc # if "overwrite_fields_from_metadata" is used: # will map "docname" to "key", and "dockey" to "doc_id" - if (title or doi) and use_doc_details: + if (title or doi) and parse_config.use_doc_details: if kwargs.get("metadata_client"): metadata_client = kwargs["metadata_client"] else: metadata_client = DocMetadataClient( session=kwargs.pop("session", None), - clients=kwargs.pop("clients", ALL_CLIENTS), + clients=kwargs.pop("clients", DEFAULT_CLIENTS), ) query_kwargs: dict[str, Any] = {} @@ -479,22 +343,30 @@ async def aadd( # noqa: C901, PLR0912, PLR0915 query_kwargs["authors"] = authors if title: query_kwargs["title"] = title - doc = await metadata_client.upgrade_doc_to_doc_details( doc, **(query_kwargs | kwargs) ) - texts = read_doc(path, doc, chunk_chars=chunk_chars, overlap=100) + texts = read_doc( + path, + doc, + chunk_chars=parse_config.chunk_size, + overlap=parse_config.overlap, + ) # loose check to see if document was loaded if ( - len(texts) == 0 + not texts or len(texts[0].text) < 10 # noqa: PLR2004 - or (not disable_check and not maybe_is_text(texts[0].text)) + or ( + not parse_config.disable_doc_valid_check + and not maybe_is_text(texts[0].text) + ) ): raise ValueError( - f"This does not look like a text document: {path}. Path disable_check to ignore this error." + f"This does not look like a text document: {path}. Pass disable_check" + " to ignore this error." ) - if await self.aadd_texts(texts, doc): + if await self.aadd_texts(texts, doc, all_settings, embedding_model): return docname return None @@ -502,11 +374,22 @@ def add_texts( self, texts: list[Text], doc: Doc, + settings: MaybeSettings = None, + embedding_model: EmbeddingModel | None = None, ) -> bool: - loop = get_loop() - return loop.run_until_complete(self.aadd_texts(texts, doc)) + return get_loop().run_until_complete( + self.aadd_texts( + texts, doc, settings=settings, embedding_model=embedding_model + ) + ) - async def aadd_texts(self, texts: list[Text], doc: Doc) -> bool: + async def aadd_texts( + self, + texts: list[Text], + doc: Doc, + settings: MaybeSettings = None, + embedding_model: EmbeddingModel | None = None, + ) -> bool: """ Add chunked texts to the collection. @@ -515,6 +398,11 @@ async def aadd_texts(self, texts: list[Text], doc: Doc) -> bool: Returns: True if the doc was added, otherwise False if already in the collection. """ + all_settings = get_settings(settings) + + if embedding_model is None: + embedding_model = all_settings.get_embedding_model() + if doc.dockey in self.docs: return False if not texts: @@ -523,20 +411,11 @@ async def aadd_texts(self, texts: list[Text], doc: Doc) -> bool: # the texts until we've set up the Doc's embedding, so callers can retry upon # OpenAI rate limit errors text_embeddings: list[list[float]] | None = ( - await self.texts_index.embedding_model.embed_documents( - self._embedding_client, texts=[t.text for t in texts] - ) + await embedding_model.embed_documents(texts=[t.text for t in texts]) if texts[0].embedding is None else None ) - # 2. Set the Doc's embedding to be the Doc's citation embedded - if doc.embedding is None: - doc.embedding = ( - await self.docs_index.embedding_model.embed_documents( - self._embedding_client, texts=[doc.citation] - ) - )[0] - # 3. Now we can set the text embeddings + # 2. Now we can set the text embeddings if text_embeddings is not None: for t, t_embedding in zip(texts, text_embeddings, strict=True): t.embedding = t_embedding @@ -546,10 +425,7 @@ async def aadd_texts(self, texts: list[Text], doc: Doc) -> bool: for t in texts: t.name = t.name.replace(doc.docname, new_docname) doc.docname = new_docname - # 5. Index remaining updates - if not self.jit_texts_index: - self.texts_index.add_texts_and_embeddings(texts) - self.docs_index.add_texts_and_embeddings([doc]) + # 5. We do not embed here, because we do it lazily self.docs[doc.dockey] = doc self.texts += texts self.docnames.add(doc.docname) @@ -575,339 +451,273 @@ def delete( self.deleted_dockeys.add(dockey) self.texts = list(filter(lambda x: x.doc.dockey != dockey, self.texts)) - async def adoc_match( + def _build_texts_index(self) -> None: + texts = [t for t in self.texts if t not in self.texts_index] + self.texts_index.add_texts_and_embeddings(texts) + + async def retrieve_texts( self, query: str, - k: int = 25, - rerank: bool | None = None, - get_callbacks: CallbackFactory = lambda x: None, # noqa: ARG005 - answer: Answer | None = None, # used for tracking tokens - ) -> set[DocKey]: - """Return a list of dockeys that match the query.""" - matches, _ = await self.docs_index.max_marginal_relevance_search( - self._embedding_client, - query, - k=k + len(self.deleted_dockeys), - fetch_k=5 * (k + len(self.deleted_dockeys)), - ) - # filter the matches - matched_docs = [ - m for m in cast(list[Doc], matches) if m.dockey not in self.deleted_dockeys - ] - - if len(matched_docs) == 0: - return set() - # this only works for gpt-4 (in my testing) - with contextlib.suppress(AttributeError): - if ( - rerank is None - and ( - isinstance(self.llm_model, OpenAILLMModel) - and self.llm_model.config["model"].startswith("gpt-4") - ) - or rerank is True - ): - chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.select, - skip_system=True, + k: int, + settings: MaybeSettings = None, + embedding_model: EmbeddingModel | None = None, + ) -> list[Text]: + + settings = get_settings(settings) + if embedding_model is None: + embedding_model = settings.get_embedding_model() + + # TODO: should probably happen elsewhere + self.texts_index.mmr_lambda = settings.texts_index_mmr_lambda + + self._build_texts_index() + _k = k + len(self.deleted_dockeys) + matches: list[Text] = cast( + list[Text], + ( + await self.texts_index.max_marginal_relevance_search( + query, k=_k, fetch_k=2 * _k, embedding_model=embedding_model ) - papers = [f"{d.docname}: {d.citation}" for d in matched_docs] - result = await chain( - {"question": query, "papers": "\n".join(papers)}, - get_callbacks("filter"), - ) - if answer: - result.answer_id = answer.id - result.name = "filter" - await self.llm_result_callback(result) - if answer: - answer.add_tokens(result) - return {d.dockey for d in matched_docs if d.docname in str(result)} - return {d.dockey for d in matched_docs} - - def _build_texts_index(self, keys: set[DocKey] | None = None): - texts = self.texts - if keys is not None and self.jit_texts_index: - # TODO: what is JIT even for?? - if keys is not None: - texts = [t for t in texts if t.doc.dockey in keys] - if len(texts) == 0: - return - self.texts_index.clear() - self.texts_index.add_texts_and_embeddings(texts) - if self.jit_texts_index and keys is None: - # Not sure what else to do here??????? - print( - "Warning: JIT text index without keys " - "requires rebuilding index each time!" - ) - self.texts_index.clear() - self.texts_index.add_texts_and_embeddings(texts) + )[0], + ) + matches = [m for m in matches if m.doc.dockey not in self.deleted_dockeys] + return matches[:k] def get_evidence( self, - answer: Answer, - k: int = 10, - max_sources: int = 5, - get_callbacks: CallbackFactory = lambda x: None, # noqa: ARG005 - detailed_citations: bool = False, - disable_vector_search: bool = False, + query: Answer | str, + exclude_text_filter: set[str] | None = None, + settings: MaybeSettings = None, + callbacks: list[Callable] | None = None, + embedding_model: EmbeddingModel | None = None, + summary_llm_model: LLMModel | None = None, ) -> Answer: return get_loop().run_until_complete( self.aget_evidence( - answer, - k=k, - max_sources=max_sources, - get_callbacks=get_callbacks, - detailed_citations=detailed_citations, - disable_vector_search=disable_vector_search, + query=query, + exclude_text_filter=exclude_text_filter, + settings=settings, + callbacks=callbacks, + embedding_model=embedding_model, + summary_llm_model=summary_llm_model, ) ) - async def aget_evidence( # noqa: C901, PLR0915 + async def aget_evidence( self, - answer: Answer, - k: int = 10, # Number of evidence pieces to retrieve - max_sources: int = 5, # Number of scored contexts to use - get_callbacks: CallbackFactory = lambda x: None, # noqa: ARG005 - detailed_citations: bool = False, - disable_vector_search: bool = False, + query: Answer | str, + exclude_text_filter: set[str] | None = None, + settings: MaybeSettings = None, + callbacks: list[Callable] | None = None, + embedding_model: EmbeddingModel | None = None, + summary_llm_model: LLMModel | None = None, ) -> Answer: - if len(self.docs) == 0 and self.docs_index is None: - # do we have no docs? - return answer # type: ignore[unreachable] - self._build_texts_index(keys=answer.dockey_filter) - _k = k - if answer.dockey_filter is not None: - _k = k * 10 # heuristic - get enough so we can downselect - if disable_vector_search: - matches = self.texts - else: - matches = cast( - list[Text], - ( - await self.texts_index.max_marginal_relevance_search( - self._embedding_client, answer.question, k=_k, fetch_k=5 * _k - ) - )[0], + + evidence_settings = get_settings(settings) + answer_config = evidence_settings.answer + prompt_config = evidence_settings.prompts + + answer = ( + Answer(question=query, config_md5=evidence_settings.md5) + if isinstance(query, str) + else query + ) + + if not self.docs and len(self.texts_index) == 0: + return answer + + if embedding_model is None: + embedding_model = evidence_settings.get_embedding_model() + + if summary_llm_model is None: + summary_llm_model = evidence_settings.get_summary_llm() + + exclude_text_filter = exclude_text_filter or set() + exclude_text_filter |= {c.text.name for c in answer.contexts} + + _k = answer_config.evidence_k + if exclude_text_filter: + _k += len( + exclude_text_filter + ) # heuristic - get enough so we can downselect + + if answer_config.evidence_retrieval: + matches = await self.retrieve_texts( + answer.question, _k, evidence_settings, embedding_model ) - # ok now filter (like ones from adoc_match) - if answer.dockey_filter is not None: - matches = [m for m in matches if m.doc.dockey in answer.dockey_filter] + else: + matches = self.texts - # check if it is deleted - matches = [m for m in matches if m.doc.dockey not in self.deleted_dockeys] + if exclude_text_filter: + matches = [m for m in matches if m.text not in exclude_text_filter] - # check if it is already in answer - cur_names = [c.text.name for c in answer.contexts] - matches = [m for m in matches if m.name not in cur_names] - - # now finally cut down - matches = matches[:k] - - async def process(match): # noqa: C901, PLR0912 - callbacks = get_callbacks("evidence:" + match.name) - citation = match.doc.citation - # needed empties for failures/skips - llm_result = LLMResult(model="") - extras: dict[str, Any] = {} - if detailed_citations: - citation = match.name + ": " + citation - - if self.prompts.skip_summary: - context = match.text - score = 5 + matches = ( + matches[: answer_config.evidence_k] + if answer_config.evidence_retrieval + else matches + ) + + prompt_runner: PromptRunner | None = None + if not answer_config.evidence_skip_summary: + if prompt_config.use_json: + prompt_runner = partial( + summary_llm_model.run_prompt, + prompt_config.summary_json, + system_prompt=prompt_config.summary_json_system, + ) else: - if self.prompts.json_summary: - summary_chain = self.summary_llm_model.make_chain( # type: ignore[union-attr] - client=self._client, - prompt=self.prompts.summary_json, - system_prompt=self.prompts.summary_json_system, - ) - else: - summary_chain = self.summary_llm_model.make_chain( # type: ignore[union-attr] - client=self._client, - prompt=self.prompts.summary, - system_prompt=self.prompts.system, - ) - llm_result = await summary_chain( - { - "question": answer.question, - "citation": citation, - "summary_length": answer.summary_length, - "text": match.text, - }, - callbacks, + prompt_runner = partial( + summary_llm_model.run_prompt, + prompt_config.summary, + system_prompt=prompt_config.system, ) - llm_result.answer_id = answer.id - llm_result.name = "evidence:" + match.name - await self.llm_result_callback(llm_result) - context = llm_result.text - success = True - if self.prompts.summary_json: - try: - result_data = llm_read_json(context) - except json.decoder.JSONDecodeError: - # fallback to string - success = False - else: - success = isinstance(result_data, dict) - if success: - try: - context = result_data.pop("summary") - score = result_data.pop("relevance_score") - result_data.pop("question", None) - extras = result_data - except KeyError: - success = False - # fallback to string (or json mode not enabled) - if not success or not self.prompts.summary_json: - # Process as string - if ( - "not applicable" in context.lower() - or "not relevant" in context.lower() - ): - return None, llm_result - score = get_score(context) - if self.strip_citations: - # remove citations that collide with our grounded citations (for the answer LLM) - context = strip_citations(context) - return ( - Context( - context=context, - text=Text( - text=match.text, - name=match.name, - doc=match.doc.__class__( - **match.doc.model_dump(exclude="embedding") - ), - ), - score=score, - **extras, - ), - llm_result, + + with set_llm_answer_ids(answer.id): + results = await gather_with_concurrency( + answer_config.max_concurrent_requests, + [ + map_fxn_summary( + text=m, + question=answer.question, + prompt_runner=prompt_runner, + extra_prompt_data={ + "summary_length": answer_config.evidence_summary_length, + "citation": f"{m.name}: {m.doc.citation}", + }, + parser=llm_parse_json if prompt_config.use_json else None, + callbacks=callbacks, + ) + for m in matches + ], ) - results = await gather_with_concurrency( - self.max_concurrent, [process(m) for m in matches] - ) - # update token counts - [answer.add_tokens(r[1]) for r in results] + for _, llm_result in results: + answer.add_tokens(llm_result) - # filter out failures, sort by score, limit to max_sources - answer.contexts = sorted( - [c for c, r in results if c is not None] + answer.contexts, - key=lambda x: x.score, - reverse=True, - )[:max_sources] - context_str = "\n\n".join( - [ - f"{c.text.name}: {c.context}" - + "".join([f"\n{k}: {v}" for k, v in (c.model_extra or {}).items()]) - + (f"\nFrom {c.text.doc.citation}" if detailed_citations else "") - for c in answer.contexts - ] - ) - - valid_names = [c.text.name for c in answer.contexts] - context_str += "\n\nValid keys: " + ", ".join(valid_names) - answer.context = context_str + answer.contexts += [r for r, _ in results if r is not None] return answer def query( self, - query: str, - k: int = 10, - max_sources: int = 5, - length_prompt="about 100 words", - answer: Answer | None = None, - key_filter: bool | None = None, - get_callbacks: CallbackFactory = lambda x: None, # noqa: ARG005 + query: Answer | str, + settings: MaybeSettings = None, + callbacks: list[Callable] | None = None, + llm_model: LLMModel | None = None, + summary_llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, ) -> Answer: return get_loop().run_until_complete( self.aquery( query, - k=k, - max_sources=max_sources, - length_prompt=length_prompt, - answer=answer, - key_filter=key_filter, - get_callbacks=get_callbacks, + settings=settings, + callbacks=callbacks, + llm_model=llm_model, + summary_llm_model=summary_llm_model, + embedding_model=embedding_model, ) ) - async def aquery( # noqa: C901, PLR0912, PLR0915 + async def aquery( # noqa: PLR0912 self, - query: str, - k: int = 10, - max_sources: int = 5, - length_prompt: str = "about 100 words", - answer: Answer | None = None, - key_filter: bool | None = None, - get_callbacks: CallbackFactory = lambda x: None, # noqa: ARG005 + query: Answer | str, + settings: MaybeSettings = None, + callbacks: list[Callable] | None = None, + llm_model: LLMModel | None = None, + summary_llm_model: LLMModel | None = None, + embedding_model: EmbeddingModel | None = None, ) -> Answer: - if k < max_sources: - raise ValueError("k should be greater than max_sources") - if answer is None: - answer = Answer(question=query, answer_length=length_prompt) - if len(answer.contexts) == 0: - # this is heuristic - k and len(docs) are not - # comparable - one is chunks and one is docs - if key_filter or (key_filter is None and len(self.docs) > k): - keys = await self.adoc_match( - answer.question, - get_callbacks=get_callbacks, - answer=answer, - ) - if len(keys) > 0: - answer.dockey_filter = keys + + query_settings = get_settings(settings) + answer_config = query_settings.answer + prompt_config = query_settings.prompts + + if llm_model is None: + llm_model = query_settings.get_llm() + if summary_llm_model is None: + summary_llm_model = query_settings.get_summary_llm() + if embedding_model is None: + embedding_model = query_settings.get_embedding_model() + + answer = ( + Answer(question=query, config_md5=query_settings.md5) + if isinstance(query, str) + else query + ) + + contexts = answer.contexts + + if not contexts: answer = await self.aget_evidence( answer, - k=k, - max_sources=max_sources, - get_callbacks=get_callbacks, + callbacks=callbacks, + settings=settings, + embedding_model=embedding_model, + summary_llm_model=summary_llm_model, ) - if self.prompts.pre is not None: - chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.pre, - system_prompt=self.prompts.system, - ) - pre = await chain({"question": answer.question}, get_callbacks("pre")) - pre.name = "pre" - pre.answer_id = answer.id - await self.llm_result_callback(pre) + contexts = answer.contexts + pre_str = None + if prompt_config.pre is not None: + with set_llm_answer_ids(answer.id): + pre = await llm_model.run_prompt( + prompt=prompt_config.pre, + data={"question": answer.question}, + callbacks=callbacks, + name="pre", + system_prompt=prompt_config.system, + ) answer.add_tokens(pre) - answer.context += f"\n\nExtra background information: {pre}" + pre_str = pre.text + + filtered_contexts = sorted( + contexts, + key=lambda x: x.score, + reverse=True, + )[: answer_config.answer_max_sources] + # remove any contexts with a score of 0 + filtered_contexts = [c for c in filtered_contexts if c.score > 0] + + context_str = "\n\n".join( + [ + f"{c.text.name}: {c.context}" + + "".join([f"\n{k}: {v}" for k, v in (c.model_extra or {}).items()]) + + ( + f"\nFrom {c.text.doc.citation}" + if answer_config.evidence_detailed_citations + else "" + ) + for c in filtered_contexts + ] + + ([f"Extra background information: {pre_str}"] if pre_str else []) + ) + + valid_names = [c.text.name for c in filtered_contexts] + context_str += "\n\nValid keys: " + ", ".join(valid_names) + bib = {} - if len(answer.context) < 10: # and not self.memory: # noqa: PLR2004 + if len(context_str) < 10: # noqa: PLR2004 answer_text = ( "I cannot answer this question due to insufficient information." ) else: - qa_chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.qa, - system_prompt=self.prompts.system, - ) - answer_result = await qa_chain( - { - "context": answer.context, - "answer_length": answer.answer_length, - "question": answer.question, - }, - get_callbacks("answer"), - ) - answer_result.name = "answer" - answer_result.answer_id = answer.id - await self.llm_result_callback(answer_result) + with set_llm_answer_ids(answer.id): + answer_result = await llm_model.run_prompt( + prompt=prompt_config.qa, + data={ + "context": context_str, + "answer_length": answer_config.answer_length, + "question": answer.question, + "example_citation": prompt_config.EXAMPLE_CITATION, + }, + callbacks=callbacks, + name="answer", + system_prompt=prompt_config.system, + ) answer_text = answer_result.text answer.add_tokens(answer_result) # it still happens - if "(Example2012Example pages 3-4)" in answer_text: - answer_text = answer_text.replace("(Example2012Example pages 3-4)", "") - for c in answer.contexts: + if prompt_config.EXAMPLE_CITATION in answer_text: + answer_text = answer_text.replace(prompt_config.EXAMPLE_CITATION, "") + for c in filtered_contexts: name = c.text.name citation = c.text.doc.citation # do check for whole key (so we don't catch Callahan2019a with Callahan2019) @@ -916,32 +726,38 @@ async def aquery( # noqa: C901, PLR0912, PLR0915 bib_str = "\n\n".join( [f"{i+1}. ({k}): {c}" for i, (k, c) in enumerate(bib.items())] ) + + if answer_config.answer_filter_extra_background: + answer_text = re.sub( + r"\([Ee]xtra [Bb]ackground [Ii]nformation\)", + "", + answer_text, + ) + formatted_answer = f"Question: {answer.question}\n\n{answer_text}\n" - if len(bib) > 0: + if bib: formatted_answer += f"\nReferences\n\n{bib_str}\n" + + if prompt_config.post is not None: + with set_llm_answer_ids(answer.id): + post = await llm_model.run_prompt( + prompt=prompt_config.post, + data=answer.model_dump(), + callbacks=callbacks, + name="post", + system_prompt=prompt_config.system, + ) + answer_text = post.text + answer.add_tokens(post) + formatted_answer = f"Question: {answer.question}\n\n{post}\n" + if bib: + formatted_answer += f"\nReferences\n\n{bib_str}\n" + + # now at end we modify, so we could have retried earlier answer.answer = answer_text answer.formatted_answer = formatted_answer answer.references = bib_str - - if self.prompts.post is not None: - chain = self.llm_model.make_chain( - client=self._client, - prompt=self.prompts.post, - system_prompt=self.prompts.system, - ) - post = await chain(answer.model_dump(), get_callbacks("post")) - post.name = "post" - post.answer_id = answer.id - await self.llm_result_callback(post) - answer.answer = post.text - answer.add_tokens(post) - answer.formatted_answer = f"Question: {answer.question}\n\n{post}\n" - if len(bib) > 0: - answer.formatted_answer += f"\nReferences\n\n{bib_str}\n" - # if self.memory_model is not None: - # answer.memory = self.memory_model.load_memory_variables(inputs={})["memory"] - # self.memory_model.save_context( - # {"Question": answer.question}, {"Answer": answer.answer} - # ) + answer.contexts = contexts + answer.context = context_str return answer diff --git a/paperqa/llms.py b/paperqa/llms.py index 15f9a319..0ce40ea3 100644 --- a/paperqa/llms.py +++ b/paperqa/llms.py @@ -1,144 +1,50 @@ from __future__ import annotations import asyncio -import os -import re +import contextlib from abc import ABC, abstractmethod -from enum import Enum +from collections.abc import AsyncIterable, Awaitable, Callable, Iterable, Sequence +from enum import StrEnum from inspect import signature -from typing import Any, AsyncGenerator, Callable, Coroutine, Iterable, Sequence, cast +from typing import Any import numpy as np import tiktoken -from openai import AsyncOpenAI +from litellm import Router, aembedding, token_counter from pydantic import BaseModel, ConfigDict, Field, model_validator -from .prompts import default_system_prompt -from .types import Doc, Embeddable, LLMResult, Text -from .utils import batch_iter, flatten, gather_with_concurrency, is_coroutine_callable - -try: - from anthropic import AsyncAnthropic - from anthropic.types import ContentBlockDeltaEvent -except ImportError: - AsyncAnthropic = Any - ContentBlockDeltaEvent = Any - -# only works for python 3.11 -# def guess_model_type(model_name: str) -> str: -# """Guess the model type from the model name for OpenAI models""" -# import openai - -# model_type = get_type_hints( -# openai.types.chat.completion_create_params.CompletionCreateParamsBase -# )["model"] -# model_union = get_args(get_args(model_type)[1]) -# model_arr = list(model_union) -# if model_name in model_arr: -# return "chat" -# return "completion" - -# def is_openai_model(model_name): -# import openai - -# model_type = get_type_hints( -# openai.types.chat.completion_create_params.CompletionCreateParamsBase -# )["model"] -# model_union = get_args(get_args(model_type)[1]) -# model_arr = list(model_union) - -# complete_model_types = get_type_hints( -# openai.types.completion_create_params.CompletionCreateParamsBase -# )["model"] -# complete_model_union = get_args(get_args(complete_model_types)[1]) -# complete_model_arr = list(complete_model_union) - -# return model_name in model_arr or model_name in complete_model_arr - -ANYSCALE_MODEL_PREFIXES: tuple[str, ...] = ( - "meta-llama/Meta-Llama-3-", - "mistralai/Mistral-", - "mistralai/Mixtral-", -) - - -def guess_model_type(model_name: str) -> str: # noqa: PLR0911 - if model_name.startswith("babbage"): - return "completion" - if model_name.startswith("davinci"): - return "completion" - if ( - os.environ.get("ANYSCALE_API_KEY") - and os.environ.get("ANYSCALE_BASE_URL") - and (model_name.startswith(ANYSCALE_MODEL_PREFIXES)) - ): - return "chat" - if "instruct" in model_name: - return "completion" - if model_name.startswith("gpt-4"): - if "base" in model_name: - return "completion" - return "chat" - if model_name.startswith("gpt-3.5"): - return "chat" - return "completion" - - -def is_anyscale_model(model_name: str) -> bool: - # compares prefixes with anyscale models - # https://docs.anyscale.com/endpoints/text-generation/query-a-model/ - return bool( - os.environ.get("ANYSCALE_API_KEY") - and os.environ.get("ANYSCALE_BASE_URL") - and model_name.startswith(ANYSCALE_MODEL_PREFIXES) - ) +from paperqa.prompts import default_system_prompt +from paperqa.types import Embeddable, LLMResult +from paperqa.utils import is_coroutine_callable +PromptRunner = Callable[ + [dict, list[Callable[[str], None]] | None, str | None], + Awaitable[LLMResult], +] -def is_openai_model(model_name: str) -> bool: - return is_anyscale_model(model_name) or model_name.startswith( - ("gpt-", "babbage", "davinci", "ft:gpt-") - ) +def prepare_args(func: Callable, chunk: str, name: str | None) -> tuple[tuple, dict]: + with contextlib.suppress(TypeError): + if "name" in signature(func).parameters: + return (chunk,), {"name": name} + return (chunk,), {} -def process_llm_config( - llm_config: dict, max_token_name: str = "max_tokens" # noqa: S107 -) -> dict: - """Remove model_type and try to set max_tokens.""" - result = {k: v for k, v in llm_config.items() if k != "model_type"} - if max_token_name not in result or result[max_token_name] == -1: - model = llm_config["model"] - # now we guess - we could use tiktoken to count, - # but do have the initiative right now - if model.startswith("gpt-4") or ( - model.startswith("gpt-3.5") and "1106" in model - ): - result[max_token_name] = 3000 - else: - result[max_token_name] = 1500 - return result - -async def embed_documents( - client: AsyncOpenAI, texts: list[str], embedding_model: str, batch_size: int = 16 -) -> list[list[float]]: - """Embed a list of documents with batching.""" - if client is None: - raise ValueError( - "Your client is None - did you forget to set it after pickling?" - ) - N = len(texts) - embeddings = [] - for i in range(0, N, batch_size): - response = await client.embeddings.create( - model=embedding_model, - input=texts[i : i + batch_size], - encoding_format="float", - ) - embeddings.extend([e.embedding for e in response.data]) - return embeddings +async def do_callbacks( + async_callbacks: Iterable[Callable[..., Awaitable]], + sync_callbacks: Iterable[Callable[..., Any]], + chunk: str, + name: str | None, +) -> None: + for f in async_callbacks: + args, kwargs = prepare_args(f, chunk, name) + await f(*args, **kwargs) + for f in sync_callbacks: + args, kwargs = prepare_args(f, chunk, name) + f(*args, **kwargs) -class EmbeddingModes(str, Enum): +class EmbeddingModes(StrEnum): DOCUMENT = "document" QUERY = "query" @@ -150,15 +56,25 @@ def set_mode(self, mode: EmbeddingModes) -> None: """Several embedding models have a 'mode' or prompt which affects output.""" @abstractmethod - async def embed_documents(self, client: Any, texts: list[str]) -> list[list[float]]: + async def embed_documents(self, texts: list[str]) -> list[list[float]]: pass -class OpenAIEmbeddingModel(EmbeddingModel): +class LiteLLMEmbeddingModel(EmbeddingModel): name: str = Field(default="text-embedding-3-small") + embedding_kwargs: dict = Field(default_factory=dict) - async def embed_documents(self, client: Any, texts: list[str]) -> list[list[float]]: - return await embed_documents(cast(AsyncOpenAI, client), texts, self.name) + async def embed_documents( + self, texts: list[str], batch_size: int = 16 + ) -> list[list[float]]: + N = len(texts) + embeddings = [] + for i in range(0, N, batch_size): + response = await aembedding( + self.name, input=texts[i : i + batch_size], **self.embedding_kwargs + ) + embeddings.extend([e["embedding"] for e in response.data]) + return embeddings class SparseEmbeddingModel(EmbeddingModel): @@ -168,7 +84,7 @@ class SparseEmbeddingModel(EmbeddingModel): ndim: int = 256 enc: Any = Field(default_factory=lambda: tiktoken.get_encoding("cl100k_base")) - async def embed_documents(self, client, texts) -> list[list[float]]: # noqa: ARG002 + async def embed_documents(self, texts) -> list[list[float]]: enc_batch = self.enc.encode_ordinary_batch(texts) # now get frequency of each token rel to length return [ @@ -182,399 +98,381 @@ class HybridEmbeddingModel(EmbeddingModel): name: str = "hybrid-embed" models: list[EmbeddingModel] - async def embed_documents(self, client, texts): + async def embed_documents(self, texts): all_embeds = await asyncio.gather( - *[m.embed_documents(client, texts) for m in self.models] + *[m.embed_documents(texts) for m in self.models] ) return np.concatenate(all_embeds, axis=1) -class VoyageAIEmbeddingModel(EmbeddingModel): - """A wrapper around Voyage AI's client lib.""" - - name: str = Field(default="voyage-large-2") - embedding_type: EmbeddingModes = Field(default=EmbeddingModes.DOCUMENT) - batch_size: int = 10 +class Chunk(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + text: str | None + prompt_tokens: int + completion_tokens: int - def set_mode(self, mode: EmbeddingModes): - self.embedding_type = mode - - async def embed_documents(self, client: Any, texts: list[str]) -> list[list[float]]: - if client is None: - raise ValueError( - "Your client is None - did you forget to set it after pickling?" - ) - N = len(texts) - embeddings = [] - for i in range(0, N, self.batch_size): - response = await client.embed( - texts[i : i + self.batch_size], - model=self.name, - input_type=self.embedding_type.value, - ) - embeddings.extend(response.embeddings) - return embeddings + def __str__(self): + return self.text class LLMModel(ABC, BaseModel): - model_config = ConfigDict(extra="forbid") + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) llm_type: str | None = None name: str + llm_result_callback: ( + Callable[[LLMResult], None] | Callable[[LLMResult], Awaitable[None]] | None + ) = Field( + default=None, + description=( + "An async callback that will be executed on each" + " LLMResult (different than callbacks that execute on each chunk)" + ), + exclude=True, + ) + config: dict = Field(default_factory=dict) - async def acomplete(self, client: Any, prompt: str) -> str: + async def acomplete(self, prompt: str) -> Chunk: + """Return the completion as string and the number of tokens in the prompt and completion.""" raise NotImplementedError - async def acomplete_iter(self, client: Any, prompt: str) -> Any: + async def acomplete_iter(self, prompt: str) -> AsyncIterable[Chunk]: # noqa: ARG002 """Return an async generator that yields chunks of the completion. - I cannot get mypy to understand the override, so marked as Any + Only the last tuple will be non-zero. """ raise NotImplementedError + if False: # type: ignore[unreachable] # pylint: disable=using-constant-test + yield # Trick mypy: https://github.com/python/mypy/issues/5070#issuecomment-1050834495 - async def achat(self, client: Any, messages: Iterable[dict[str, str]]) -> str: + async def achat(self, messages: Iterable[dict[str, str]]) -> Chunk: + """Return the completion as string and the number of tokens in the prompt and completion.""" raise NotImplementedError - async def achat_iter(self, client: Any, messages: Iterable[dict[str, str]]) -> Any: + async def achat_iter( + self, messages: Iterable[dict[str, str]] # noqa: ARG002 + ) -> AsyncIterable[Chunk]: """Return an async generator that yields chunks of the completion. - I cannot get mypy to understand the override, so marked as Any + Only the last tuple will be non-zero. """ raise NotImplementedError + if False: # type: ignore[unreachable] # pylint: disable=using-constant-test + yield # Trick mypy: https://github.com/python/mypy/issues/5070#issuecomment-1050834495 - def infer_llm_type(self, client: Any) -> str: # noqa: ARG002 + def infer_llm_type(self) -> str: return "completion" def count_tokens(self, text: str) -> int: return len(text) // 4 # gross approximation - def make_chain( # noqa: C901, PLR0915 + async def run_prompt( self, - client: Any, prompt: str, + data: dict, + callbacks: list[Callable] | None = None, + name: str | None = None, skip_system: bool = False, system_prompt: str = default_system_prompt, - ) -> Callable[ - [dict, list[Callable[[str], None]] | None], Coroutine[Any, Any, LLMResult] - ]: - """Create a function to execute a batch of prompts. + ) -> LLMResult: + if self.llm_type is None: + self.llm_type = self.infer_llm_type() + if self.llm_type == "chat": + return await self._run_chat( + prompt, data, callbacks, name, skip_system, system_prompt + ) + if self.llm_type == "completion": + return await self._run_completion( + prompt, data, callbacks, name, skip_system, system_prompt + ) + raise ValueError(f"Unknown llm_type {self.llm_type!r}.") - This replaces the previous use of langchain for combining prompts and LLMs. + async def _run_chat( + self, + prompt: str, + data: dict, + callbacks: list[Callable] | None = None, + name: str | None = None, + skip_system: bool = False, + system_prompt: str = default_system_prompt, + ) -> LLMResult: + """Run a chat prompt. Args: - client: a ephemeral client to use - prompt: The prompt to use - skip_system: Whether to skip the system prompt - system_prompt: The system prompt to use + prompt: Prompt to use. + data: Keys for the input variables that will be formatted into prompt. + callbacks: Optional functions to call with each chunk of the completion. + name: Optional name for the result. + skip_system: Set True to skip the system prompt. + system_prompt: System prompt to use. Returns: - A function to execute a prompt. Its signature is: - execute(data: dict, callbacks: list[Callable[[str], None]]] | None = None) -> LLMResult - where data is a dict with keys for the input variables that will be formatted into prompt - and callbacks is a list of functions to call with each chunk of the completion. + Result of the chat. """ - # check if it needs to be set - if self.llm_type is None: - self.llm_type = self.infer_llm_type(client) - if self.llm_type == "chat": - system_message_prompt = {"role": "system", "content": system_prompt} - human_message_prompt = {"role": "user", "content": prompt} - chat_prompt = ( + system_message_prompt = {"role": "system", "content": system_prompt} + human_message_prompt = {"role": "user", "content": prompt} + messages = [ + {"role": m["role"], "content": m["content"].format(**data)} + for m in ( [human_message_prompt] if skip_system else [system_message_prompt, human_message_prompt] ) + ] + result = LLMResult( + model=self.name, + name=name, + prompt=messages, + prompt_count=( + sum(self.count_tokens(m["content"]) for m in messages) + + sum(self.count_tokens(m["role"]) for m in messages) + ), + ) - async def execute( - data: dict, - callbacks: list[Callable] | None = None, - ) -> LLMResult: - start_clock = asyncio.get_running_loop().time() - result = LLMResult(model=self.name) - messages = [] - for m in chat_prompt: - messages.append( # noqa: PERF401 - {"role": m["role"], "content": m["content"].format(**data)} - ) - result.prompt = messages - result.prompt_count = sum( - self.count_tokens(m["content"]) for m in messages - ) + sum(self.count_tokens(m["role"]) for m in messages) - - if callbacks is None: - output = await self.achat(client, messages) - else: - sync_callbacks = [ - f for f in callbacks if not is_coroutine_callable(f) - ] - async_callbacks = [f for f in callbacks if is_coroutine_callable(f)] - completion = self.achat_iter(client, messages) - text_result = [] - async for chunk in completion: # type: ignore[attr-defined] - if chunk: - if result.seconds_to_first_token == 0: - result.seconds_to_first_token = ( - asyncio.get_running_loop().time() - start_clock - ) - text_result.append(chunk) - [await f(chunk) for f in async_callbacks] - [f(chunk) for f in sync_callbacks] - output = "".join(text_result) - result.completion_count = self.count_tokens(output) - result.text = output - result.seconds_to_last_token = ( - asyncio.get_running_loop().time() - start_clock - ) - return result - - return execute - elif self.llm_type == "completion": # noqa: RET505 - completion_prompt = ( - prompt if skip_system else system_prompt + "\n\n" + prompt - ) - - async def execute( - data: dict, callbacks: list[Callable] | None = None - ) -> LLMResult: - start_clock = asyncio.get_running_loop().time() - result = LLMResult(model=self.name) - formatted_prompt = completion_prompt.format(**data) - result.prompt_count = self.count_tokens(formatted_prompt) - result.prompt = formatted_prompt - if callbacks is None: - output = await self.acomplete(client, formatted_prompt) - else: - sync_callbacks = [ - f for f in callbacks if not is_coroutine_callable(f) - ] - async_callbacks = [f for f in callbacks if is_coroutine_callable(f)] - - completion = self.acomplete_iter( - client, - formatted_prompt, + start_clock = asyncio.get_running_loop().time() + if callbacks is None: + chunk = await self.achat(messages) + output = chunk.text + else: + sync_callbacks = [f for f in callbacks if not is_coroutine_callable(f)] + async_callbacks = [f for f in callbacks if is_coroutine_callable(f)] + completion = self.achat_iter(messages) + text_result = [] + async for chunk in completion: + if chunk.text: + if result.seconds_to_first_token == 0: + result.seconds_to_first_token = ( + asyncio.get_running_loop().time() - start_clock + ) + text_result.append(chunk.text) + await do_callbacks( + async_callbacks, sync_callbacks, chunk.text, name ) - text_result = [] - async for chunk in completion: # type: ignore[attr-defined] - if chunk: - if result.seconds_to_first_token == 0: - result.seconds_to_first_token = ( - asyncio.get_running_loop().time() - start_clock - ) - text_result.append(chunk) - [await f(chunk) for f in async_callbacks] - [f(chunk) for f in sync_callbacks] - output = "".join(text_result) - result.completion_count = self.count_tokens(output) - result.text = output - result.seconds_to_last_token = ( - asyncio.get_running_loop().time() - start_clock - ) - return result - - return execute - raise ValueError(f"Unknown llm_type: {self.llm_type}") - - -class OpenAILLMModel(LLMModel): - config: dict = Field(default={"model": "gpt-4o-mini", "temperature": 0.1}) - name: str = "gpt-4o-mini" - - def _check_client(self, client: Any) -> AsyncOpenAI: - if client is None: - raise ValueError( - "Your client is None - did you forget to set it after pickling?" - ) - if not isinstance(client, AsyncOpenAI): - raise ValueError( # noqa: TRY004 - f"Your client is not a required AsyncOpenAI client. It is a {type(client)}" - ) - return client + output = "".join(text_result) + usage = chunk.prompt_tokens, chunk.completion_tokens + if sum(usage) > 0: + result.prompt_count, result.completion_count = usage + elif output: + result.completion_count = self.count_tokens(output) + result.text = output or "" + result.seconds_to_last_token = asyncio.get_running_loop().time() - start_clock + if self.llm_result_callback: + if is_coroutine_callable(self.llm_result_callback): + await self.llm_result_callback(result) # type: ignore[misc] + else: + self.llm_result_callback(result) + return result - @model_validator(mode="after") - @classmethod - def guess_llm_type(cls, data: Any) -> Any: - m = cast(OpenAILLMModel, data) - m.llm_type = guess_model_type(m.config["model"]) - return m + async def _run_completion( + self, + prompt: str, + data: dict, + callbacks: Iterable[Callable] | None = None, + name: str | None = None, + skip_system: bool = False, + system_prompt: str = default_system_prompt, + ) -> LLMResult: + """Run a completion prompt. - @model_validator(mode="after") - @classmethod - def set_model_name(cls, data: Any) -> Any: - m = cast(OpenAILLMModel, data) - m.name = m.config["model"] - return m - - async def acomplete(self, client: Any, prompt: str) -> str: - aclient = self._check_client(client) - completion = await aclient.completions.create( - prompt=prompt, **process_llm_config(self.config) - ) - return completion.choices[0].text + Args: + prompt: Prompt to use. + data: Keys for the input variables that will be formatted into prompt. + callbacks: Optional functions to call with each chunk of the completion. + name: Optional name for the result. + skip_system: Set True to skip the system prompt. + system_prompt: System prompt to use. - async def acomplete_iter(self, client: Any, prompt: str) -> Any: - aclient = self._check_client(client) - completion = await aclient.completions.create( - prompt=prompt, **process_llm_config(self.config), stream=True + Returns: + Result of the completion. + """ + formatted_prompt: str = ( + prompt if skip_system else system_prompt + "\n\n" + prompt + ).format(**data) + result = LLMResult( + model=self.name, + name=name, + prompt=formatted_prompt, + prompt_count=self.count_tokens(formatted_prompt), ) - async for chunk in completion: - yield chunk.choices[0].text - async def achat(self, client: Any, messages: Iterable[dict[str, str]]) -> str: - aclient = self._check_client(client) - completion = await aclient.chat.completions.create( - messages=messages, **process_llm_config(self.config) # type: ignore[arg-type] - ) - return completion.choices[0].message.content or "" + start_clock = asyncio.get_running_loop().time() + if callbacks is None: + chunk = await self.acomplete(formatted_prompt) + output = chunk.text + else: + sync_callbacks = [f for f in callbacks if not is_coroutine_callable(f)] + async_callbacks = [f for f in callbacks if is_coroutine_callable(f)] + + completion = self.acomplete_iter(formatted_prompt) + text_result = [] + async for chunk in completion: + if chunk.text: + if result.seconds_to_first_token == 0: + result.seconds_to_first_token = ( + asyncio.get_running_loop().time() - start_clock + ) + text_result.append(chunk.text) + await do_callbacks( + async_callbacks, sync_callbacks, chunk.text, name + ) + output = "".join(text_result) + usage = chunk.prompt_tokens, chunk.completion_tokens + if sum(usage) > 0: + result.prompt_count, result.completion_count = usage + elif output: + result.completion_count = self.count_tokens(output) + result.text = output or "" + result.seconds_to_last_token = asyncio.get_running_loop().time() - start_clock + if self.llm_result_callback: + if is_coroutine_callable(self.llm_result_callback): + await self.llm_result_callback(result) # type: ignore[misc] + else: + self.llm_result_callback(result) + return result + + +DEFAULT_VERTEX_SAFETY_SETTINGS: list[dict[str, str]] = [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "BLOCK_ONLY_HIGH", + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "BLOCK_ONLY_HIGH", + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "BLOCK_ONLY_HIGH", + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "BLOCK_ONLY_HIGH", + }, +] + + +class LiteLLMModel(LLMModel): + """A wrapper around the litellm library. + + `config` should have two high level keys: + `model_list`: stores a list of all model configurations + (see https://docs.litellm.ai/docs/routing) + `router_kwargs`: kwargs for the Router class + + This way users can specify routing strategies, retries, etc. - async def achat_iter(self, client: Any, messages: Iterable[dict[str, str]]) -> Any: - aclient = self._check_client(client) - completion = await aclient.chat.completions.create( - messages=messages, **process_llm_config(self.config), stream=True # type: ignore[arg-type] - ) - async for chunk in cast(AsyncGenerator, completion): - yield chunk.choices[0].delta.content + """ + config: dict = Field(default_factory=dict) + name: str = "gpt-4o-mini" + _router: Router | None = None -class AnthropicLLMModel(LLMModel): - config: dict = Field( - default={"model": "claude-3-sonnet-20240229", "temperature": 0.1} - ) - name: str = "claude-3-sonnet-20240229" + @model_validator(mode="before") + @classmethod + def maybe_set_config_attribute(cls, data: dict[str, Any]) -> dict[str, Any]: + """If a user only gives a name, make a sensible config dict for them.""" + if "name" in data and "config" not in data: + data["config"] = { + "model_list": [ + { + "model_name": data["name"], + "litellm_params": {"model": data["name"]} + | ( + {} + if "gemini" not in data["name"] + else {"safety_settings": DEFAULT_VERTEX_SAFETY_SETTINGS} + ), + } + ], + "router_kwargs": {"num_retries": 3, "retry_after": 5}, + } + # we only support one "model name" for now, here we validate + if ( + "config" in data + and len({m["model_name"] for m in data["config"]["model_list"]}) > 1 + ): + raise ValueError("Only one model name per router is supported for now") + return data - def __init__(self, *args, **kwargs): - if AsyncAnthropic is Any: - raise ImportError("Please install anthropic to use this model") - super().__init__(*args, **kwargs) + def __getstate__(self): + state = self.__dict__.copy() + # Remove the _router attribute as it's not picklable + state["_router"] = None + return state - def _check_client(self, client: Any) -> AsyncAnthropic: - if client is None: - raise ValueError( - "Your client is None - did you forget to set it after pickling?" - ) - if not isinstance(client, AsyncAnthropic): - raise TypeError( - f"Your client is not a required {AsyncAnthropic.__name__} client. It is" - f" a {type(client)}." + def __setstate__(self, state): + self.__dict__.update(state) + + @property + def router(self): + if self._router is None: + self._router = Router( + model_list=self.config["model_list"], + **self.config.get("router_kwargs", {}), ) - return client - - @model_validator(mode="after") - @classmethod - def set_llm_type(cls, data: Any) -> Any: - m = cast(AnthropicLLMModel, data) - m.llm_type = "chat" - return m + return self._router + + async def acomplete(self, prompt: str) -> Chunk: + response = await self.router.atext_completion(model=self.name, prompt=prompt) + return Chunk( + text=response.choices[0].text, + prompt_tokens=response.usage.prompt_tokens, + completion_tokens=response.usage.completion_tokens, + ) - @model_validator(mode="after") - @classmethod - def set_model_name(cls, data: Any) -> Any: - m = cast(AnthropicLLMModel, data) - m.name = m.config["model"] - return m - - async def achat(self, client: Any, messages: Iterable[dict[str, str]]) -> str: - aclient = self._check_client(client) - # filter out system - sys_message = next( - (m["content"] for m in messages if m["role"] == "system"), None + async def acomplete_iter(self, prompt: str) -> AsyncIterable[Chunk]: + completion = await self.router.atext_completion( + model=self.name, + prompt=prompt, + stream=True, + stream_options={"include_usage": True}, ) - # BECAUSE THEY DO NOT USE NONE TO INDICATE SENTINEL - # LIKE ANY SANE PERSON - if sys_message: - completion = await aclient.messages.create( - system=sys_message, - messages=[m for m in messages if m["role"] != "system"], - **process_llm_config(self.config, "max_tokens"), + async for chunk in completion: + yield Chunk( + text=chunk.choices[0].text, prompt_tokens=0, completion_tokens=0 ) - else: - completion = await aclient.messages.create( - messages=[m for m in messages if m["role"] != "system"], - **process_llm_config(self.config, "max_tokens"), + if hasattr(chunk, "usage") and hasattr(chunk.usage, "prompt_tokens"): + yield Chunk( + text=chunk.choices[0].text, prompt_tokens=0, completion_tokens=0 ) - return str(completion.content) or "" - async def achat_iter(self, client: Any, messages: Iterable[dict[str, str]]) -> Any: - aclient = self._check_client(client) - sys_message = next( - (m["content"] for m in messages if m["role"] == "system"), None + async def achat(self, messages: Iterable[dict[str, str]]) -> Chunk: + response = await self.router.acompletion(self.name, messages) + return Chunk( + text=response.choices[0].message.content, + prompt_tokens=response.usage.prompt_tokens, + completion_tokens=response.usage.completion_tokens, ) - if sys_message: - completion = await aclient.messages.create( - stream=True, - system=sys_message, - messages=[m for m in messages if m["role"] != "system"], - **process_llm_config(self.config, "max_tokens"), - ) - else: - completion = await aclient.messages.create( - stream=True, - messages=[m for m in messages if m["role"] != "system"], - **process_llm_config(self.config, "max_tokens"), + + async def achat_iter( + self, messages: Iterable[dict[str, str]] + ) -> AsyncIterable[Chunk]: + completion = await self.router.acompletion( + self.name, messages, stream=True, stream_options={"include_usage": True} + ) + async for chunk in completion: + yield Chunk( + text=chunk.choices[0].delta.content, + prompt_tokens=0, + completion_tokens=0, ) - async for event in completion: - if isinstance(event, ContentBlockDeltaEvent): - yield event.delta.text - # yield event.message.content - - -class LlamaEmbeddingModel(EmbeddingModel): - embedding_model: str = Field(default="llama") - - batch_size: int = Field(default=4) - concurrency: int = Field(default=1) - - async def embed_documents(self, client: Any, texts: list[str]) -> list[list[float]]: - cast(AsyncOpenAI, client) - - async def process(texts: list[str]) -> list[float]: - for i in range(3): # noqa: B007 - # access httpx client directly to avoid type casting - response = await client._client.post( - client.base_url.join("../embedding"), json={"content": texts} - ) - body = response.json() - if len(texts) == 1: - if ( - type(body) != dict # noqa: E721 - or body.get("embedding") is None - ): - continue - return [body["embedding"]] - else: # noqa: RET505 - if type(body) != list or body[0] != "results": # noqa: E721 - continue - return [e["embedding"] for e in body[1]] - raise ValueError("Failed to embed documents - response was ", body) - - return flatten( - await gather_with_concurrency( - self.concurrency, - [process(b) for b in batch_iter(texts, self.batch_size)], + if hasattr(chunk, "usage") and hasattr(chunk.usage, "prompt_tokens"): + yield Chunk( + text=None, + prompt_tokens=chunk.usage.prompt_tokens, + completion_tokens=chunk.usage.completion_tokens, ) - ) - - -class SentenceTransformerEmbeddingModel(EmbeddingModel): - name: str = Field(default="multi-qa-MiniLM-L6-cos-v1") - _model: Any = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - try: - from sentence_transformers import SentenceTransformer - except ImportError as exc: - raise ImportError( - "Please install sentence-transformers to use this model" - ) from exc - self._model = SentenceTransformer(self.name) - - async def embed_documents( - self, client: Any, texts: list[str] # noqa: ARG002 - ) -> list[list[float]]: - from sentence_transformers import SentenceTransformer + def infer_llm_type(self) -> str: + if all( + "text-completion" in m.get("litellm_params", {}).get("model", "") + for m in self.config["model_list"] + ): + return "completion" + return "chat" - return cast(SentenceTransformer, self._model).encode(texts) + def count_tokens(self, text: str) -> int: + return token_counter(model=self.name, text=text) def cosine_similarity(a, b): @@ -585,18 +483,24 @@ def cosine_similarity(a, b): class VectorStore(BaseModel, ABC): """Interface for vector store - very similar to LangChain's VectorStore to be compatible.""" - embedding_model: EmbeddingModel = Field(default=OpenAIEmbeddingModel()) # can be tuned for different tasks mmr_lambda: float = Field(default=0.9) model_config = ConfigDict(extra="forbid") + texts_hashes: set[int] = Field(default_factory=set) + + def __contains__(self, item) -> bool: + return hash(item) in self.texts_hashes + + def __len__(self) -> int: + return len(self.texts_hashes) @abstractmethod def add_texts_and_embeddings(self, texts: Sequence[Embeddable]) -> None: - pass + [self.texts_hashes.add(hash(t)) for t in texts] # type: ignore[func-returns-value] @abstractmethod async def similarity_search( - self, client: Any, query: str, k: int + self, query: str, k: int, embedding_model: EmbeddingModel ) -> tuple[Sequence[Embeddable], list[float]]: pass @@ -605,15 +509,15 @@ def clear(self) -> None: pass async def max_marginal_relevance_search( - self, client: Any, query: str, k: int, fetch_k: int + self, query: str, k: int, fetch_k: int, embedding_model: EmbeddingModel ) -> tuple[Sequence[Embeddable], list[float]]: """Vectorized implementation of Maximal Marginal Relevance (MMR) search. Args: - client: TODOC. query: Query vector. k: Number of results to return. fetch_k: Number of results to fetch from the vector store. + embedding_model: model used to embed the query Returns: List of tuples (doc, score) of length k. @@ -621,7 +525,7 @@ async def max_marginal_relevance_search( if fetch_k < k: raise ValueError("fetch_k must be greater or equal to k") - texts, scores = await self.similarity_search(client, query, fetch_k) + texts, scores = await self.similarity_search(query, fetch_k, embedding_model) if len(texts) <= k or self.mmr_lambda >= 1.0: return texts, scores @@ -663,24 +567,23 @@ def add_texts_and_embeddings( self, texts: Sequence[Embeddable], ) -> None: + super().add_texts_and_embeddings(texts) self.texts.extend(texts) self._embeddings_matrix = np.array([t.embedding for t in self.texts]) async def similarity_search( - self, client: Any, query: str, k: int + self, query: str, k: int, embedding_model: EmbeddingModel ) -> tuple[Sequence[Embeddable], list[float]]: k = min(k, len(self.texts)) if k == 0: return [], [] # this will only affect models that embedding prompts - self.embedding_model.set_mode(EmbeddingModes.QUERY) + embedding_model.set_mode(EmbeddingModes.QUERY) - np_query = np.array( - (await self.embedding_model.embed_documents(client, [query]))[0] - ) + np_query = np.array((await embedding_model.embed_documents([query]))[0]) - self.embedding_model.set_mode(EmbeddingModes.DOCUMENT) + embedding_model.set_mode(EmbeddingModes.DOCUMENT) similarity_scores = cosine_similarity( np_query.reshape(1, -1), self._embeddings_matrix @@ -696,230 +599,17 @@ async def similarity_search( ) -class LangchainLLMModel(LLMModel): - """A wrapper around the wrapper langchain.""" - - config: dict = Field(default={"temperature": 0.1}) - name: str = "langchain" - - def infer_llm_type(self, client: Any) -> str: - from langchain_core.language_models.chat_models import BaseChatModel - - self.name = client.model_name - if isinstance(client, BaseChatModel): - return "chat" - return "completion" - - async def acomplete(self, client: Any, prompt: str) -> str: - return await client.ainvoke(prompt, **self.config) - - async def acomplete_iter(self, client: Any, prompt: str) -> Any: - async for chunk in cast(AsyncGenerator, client.astream(prompt, **self.config)): - yield chunk - - async def achat(self, client: Any, messages: Iterable[dict[str, str]]) -> str: - from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage - - lc_messages: list[BaseMessage] = [] - for m in messages: - if m["role"] == "user": - lc_messages.append(HumanMessage(content=m["content"])) - elif m["role"] == "system": - lc_messages.append(SystemMessage(content=m["content"])) - else: - raise ValueError(f"Unknown role: {m['role']}") - return (await client.ainvoke(lc_messages, **self.config)).content - - async def achat_iter(self, client: Any, messages: Iterable[dict[str, str]]) -> Any: - from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage - - lc_messages: list[BaseMessage] = [] - for m in messages: - if m["role"] == "user": - lc_messages.append(HumanMessage(content=m["content"])) - elif m["role"] == "system": - lc_messages.append(SystemMessage(content=m["content"])) - else: - raise ValueError(f"Unknown role: {m['role']}") - async for chunk in client.astream(lc_messages, **self.config): - yield chunk.content - - -class LangchainEmbeddingModel(EmbeddingModel): - """A wrapper around the wrapper langchain.""" - - name: str = "langchain" - - async def embed_documents(self, client: Any, texts: list[str]) -> list[list[float]]: - return await client.aembed_documents(texts) - - -class LangchainVectorStore(VectorStore): - """A wrapper around the wrapper langchain. - - Note that if this is cleared (e.g., by `Docs` having `jit_texts_index` set to True), - this will calls the `from_texts` class method on the `store`. This means that any non-default - constructor arguments will be lost. You can override the clear method on this class. - """ - - _store_builder: Any | None = None - _store: Any | None = None - # JIT Generics - store the class type (Doc or Text) - class_type: type[Embeddable] = Field(default=Embeddable) - model_config = ConfigDict(extra="forbid") - - def __init__(self, **data): - # we have to separate out store from the rest of the data - # because langchain objects are not serializable - store_builder = None - if "store_builder" in data: - store_builder = LangchainVectorStore.check_store_builder( - data.pop("store_builder") - ) - if "cls" in data and "embedding_model" in data: - # make a little closure - cls = data.pop("cls") - embedding_model = data.pop("embedding_model") - - def candidate(x, y): - return cls.from_embeddings(x, embedding_model, y) - - store_builder = LangchainVectorStore.check_store_builder(candidate) - super().__init__(**data) - self._store_builder = store_builder - - @classmethod - def check_store_builder(cls, builder: Any) -> Any: - # check it is a callable - if not callable(builder): - raise ValueError("store_builder must be callable") # noqa: TRY004 - # check it takes two arguments - # we don't use type hints because it could be - # a partial - sig = signature(builder) - if len(sig.parameters) != 2: # noqa: PLR2004 - raise ValueError("store_builder must take two arguments") - return builder - - def __getstate__(self): - state = super().__getstate__() - # remove non-serializable private attributes - del state["__pydantic_private__"]["_store"] - del state["__pydantic_private__"]["_store_builder"] - return state - - def __setstate__(self, state): - # restore non-serializable private attributes - state["__pydantic_private__"]["_store"] = None - state["__pydantic_private__"]["_store_builder"] = None - super().__setstate__(state) - - def add_texts_and_embeddings(self, texts: Sequence[Embeddable]) -> None: - if self._store_builder is None: - raise ValueError("You must set store_builder before adding texts") - self.class_type = type(texts[0]) - if self.class_type == Text: - vec_store_text_and_embeddings = [ - (x.text, x.embedding) for x in cast(list[Text], texts) - ] - elif self.class_type == Doc: - vec_store_text_and_embeddings = [ - (x.citation, x.embedding) for x in cast(list[Doc], texts) - ] - else: - raise ValueError("Only embeddings of type Text are supported") - if self._store is None: - self._store = self._store_builder( - vec_store_text_and_embeddings, - texts, - ) - if self._store is None or not hasattr(self._store, "add_embeddings"): - raise ValueError("store_builder did not return a valid vectorstore") - self._store.add_embeddings( - vec_store_text_and_embeddings, - metadatas=texts, - ) - - async def similarity_search( - self, client: Any, query: str, k: int # noqa: ARG002 - ) -> tuple[Sequence[Embeddable], list[float]]: - if self._store is None: - return [], [] - results = await self._store.asimilarity_search_with_relevance_scores(query, k=k) - texts, scores = [self.class_type(**r[0].metadata) for r in results], [ - r[1] for r in results - ] - return texts, scores - - def clear(self) -> None: - del self._store # be explicit, because it could be large - self._store = None - - -def get_score(text: str) -> int: - # check for N/A - last_line = text.split("\n")[-1] - if "N/A" in last_line or "n/a" in last_line or "NA" in last_line: - return 0 - score = re.search(r"[sS]core[:is\s]+([0-9]+)", text) - if not score: - score = re.search(r"\(([0-9])\w*\/", text) - if not score: - score = re.search(r"([0-9]+)\w*\/", text) - if score: - s = int(score.group(1)) - if s > 10: # noqa: PLR2004 - s = int(s / 10) # sometimes becomes out of 100 - return s - last_few = text[-15:] - scores = re.findall(r"([0-9]+)", last_few) - if scores: - s = int(scores[-1]) - if s > 10: # noqa: PLR2004 - s = int(s / 10) # sometimes becomes out of 100 - return s - if len(text) < 100: # noqa: PLR2004 - return 1 - return 5 - - -def llm_model_factory(llm: str) -> LLMModel: - if llm != "default": - if is_openai_model(llm): - return OpenAILLMModel(config={"model": llm}) - elif llm.startswith("langchain"): # noqa: RET505 - return LangchainLLMModel() - elif "claude" in llm: - return AnthropicLLMModel(config={"model": llm}) - else: - raise ValueError(f"Could not guess model type for {llm}. ") - return OpenAILLMModel() - - def embedding_model_factory(embedding: str, **kwargs) -> EmbeddingModel: - if embedding == "langchain": - return LangchainEmbeddingModel(**kwargs) - if embedding == "sentence-transformers": - return SentenceTransformerEmbeddingModel(**kwargs) - if embedding.startswith("voyage"): - return VoyageAIEmbeddingModel(name=embedding, **kwargs) + if embedding.startswith("hybrid"): embedding_model_name = "-".join(embedding.split("-")[1:]) - dense_model = ( - OpenAIEmbeddingModel(name=embedding_model_name) - if not embedding_model_name.startswith("voyage") - else VoyageAIEmbeddingModel(name=embedding_model_name, **kwargs) - ) return HybridEmbeddingModel( models=[ - dense_model, + LiteLLMEmbeddingModel(name=embedding_model_name), SparseEmbeddingModel(**kwargs), ] ) if embedding == "sparse": return SparseEmbeddingModel(**kwargs) - return OpenAIEmbeddingModel(name=embedding, **kwargs) - -def vector_store_factory(embedding: str) -> NumpyVectorStore: - return NumpyVectorStore(embedding_model=embedding_model_factory(embedding)) + return LiteLLMEmbeddingModel(name=embedding, embedding_kwargs=kwargs) diff --git a/paperqa/prompts.py b/paperqa/prompts.py index 3782ce17..e4d6e0fa 100644 --- a/paperqa/prompts.py +++ b/paperqa/prompts.py @@ -1,13 +1,12 @@ summary_prompt = ( - "Summarize the excerpt below to help answer a question.\n\n" - "Excerpt from {citation}\n\n----\n\n{text}\n\n----\n\n" - "Question: {question}\n\n" - "Do not directly answer the question, instead summarize to give evidence to help " - "answer the question. Stay detailed; report specific numbers, equations, or " - 'direct quotes (marked with quotation marks). Reply "Not applicable" if the ' - "excerpt is irrelevant. At the end of your response, provide an integer score " - "from 1-10 on a newline indicating relevance to question. Do not explain your score." - "\n\nRelevant Information Summary ({summary_length}):" + "Summarize the excerpt below to help answer a question.\n\nExcerpt from" + " {citation}\n\n----\n\n{text}\n\n----\n\nQuestion: {question}\n\nDo not directly" + " answer the question, instead summarize to give evidence to help answer the" + " question. Stay detailed; report specific numbers, equations, or direct quotes" + ' (marked with quotation marks). Reply "Not applicable" if the excerpt is' + " irrelevant. At the end of your response, provide an integer score from 1-10 on a" + " newline indicating relevance to question. Do not explain your score.\n\nRelevant" + " Information Summary ({summary_length}):" ) summary_json_prompt = ( @@ -19,12 +18,11 @@ "Context (with relevance scores):\n\n{context}\n\n----\n\n" "Question: {question}\n\n" "Write an answer based on the context. " - "If the context provides insufficient information and " - "the question cannot be directly answered, reply " + "If the context provides insufficient information reply " '"I cannot answer."' "For each part of your answer, indicate which sources most support " "it via citation keys at the end of sentences, " - "like (Example2012Example pages 3-4). Only cite from the context " + "like {example_citation}. Only cite from the context " "below and only use the valid keys. Write in the style of a " "Wikipedia article, with concise sentences and coherent paragraphs. " "The context comes from a variety of sources and is only a summary, " diff --git a/paperqa/readers.py b/paperqa/readers.py index a2a2db05..0a4962bf 100644 --- a/paperqa/readers.py +++ b/paperqa/readers.py @@ -4,17 +4,19 @@ from pathlib import Path from typing import Literal, overload -import html2text +import pymupdf import tiktoken +from html2text import __version__ as html2text_version +from html2text import html2text -from .types import ChunkMetadata, Doc, ParsedMetadata, ParsedText, Text -from .version import __version__ as pqa_version +from paperqa.types import ChunkMetadata, Doc, ParsedMetadata, ParsedText, Text +from paperqa.utils import ImpossibleParsingError +from paperqa.version import __version__ as pqa_version -def parse_pdf_fitz_to_pages(path: Path) -> ParsedText: - import fitz +def parse_pdf_to_pages(path: Path) -> ParsedText: - with fitz.open(path) as file: + with pymupdf.open(path) as file: pages: dict[str, str] = {} total_length = 0 @@ -24,29 +26,8 @@ def parse_pdf_fitz_to_pages(path: Path) -> ParsedText: total_length += len(pages[str(i + 1)]) metadata = ParsedMetadata( - parsing_libraries=[f"fitz ({fitz.__doc__})"], - paperqa_version=str(pqa_version), - total_parsed_text_length=total_length, - parse_type="pdf", - ) - return ParsedText(content=pages, metadata=metadata) - - -def parse_pdf_to_pages(path: Path) -> ParsedText: - import pypdf - - with open(path, "rb") as pdfFileObj: - pdfReader = pypdf.PdfReader(pdfFileObj) - pages: dict[str, str] = {} - total_length = 0 - - for i, page in enumerate(pdfReader.pages): - pages[str(i + 1)] = page.extract_text() - total_length += len(pages[str(i + 1)]) - - metadata = ParsedMetadata( - parsing_libraries=[f"pypdf ({pypdf.__version__})"], - paperqa_version=str(pqa_version), + parsing_libraries=[f"pymupdf ({pymupdf.__version__})"], + paperqa_version=pqa_version, total_parsed_text_length=total_length, parse_type="pdf", ) @@ -65,6 +46,11 @@ def chunk_pdf( f"ParsedText.content must be a `dict`, not {type(parsed_text.content)}." ) + if not parsed_text.content: + raise ImpossibleParsingError( + "No text was parsed from the document: either empty or corrupted." + ) + for page_num, page_text in parsed_text.content.items(): split += page_text pages.append(page_num) @@ -82,7 +68,7 @@ def chunk_pdf( split = split[chunk_chars - overlap :] pages = [page_num] - if len(split) > overlap or len(texts) == 0: + if len(split) > overlap or not texts: pg = "-".join([pages[0], pages[-1]]) texts.append( Text(text=split[:chunk_chars], name=f"{doc.docname} pages {pg}", doc=doc) @@ -91,39 +77,43 @@ def chunk_pdf( def parse_text( - path: Path, html: bool = False, split_lines=False, use_tiktoken=True + path: Path, html: bool = False, split_lines: bool = False, use_tiktoken: bool = True ) -> ParsedText: """Simple text splitter, can optionally use tiktoken, parse html, or split into newlines. Args: - path: path to file - html: flag to use html2text library for parsing - split_lines: flag to split lines into a list - use_tiktoken: flag to use tiktoken library to encode text - + path: path to file. + html: flag to use html2text library for parsing. + split_lines: flag to split lines into a list. + use_tiktoken: flag to use tiktoken library to encode text. """ try: - with open(path) as f: - text = [str(line) for line in f] if split_lines else f.read() + with path.open() as f: + text = list(f) if split_lines else f.read() except UnicodeDecodeError: - with open(path, encoding="utf-8", errors="ignore") as f: + with path.open(encoding="utf-8", errors="ignore") as f: text = f.read() + parsing_libraries: list[str] = ["tiktoken (cl100k_base)"] if use_tiktoken else [] if html: - text = html2text.html2text(text) - - metadata = { - "parsing_libraries": ["tiktoken (cl100k_base)"] if use_tiktoken else [], - "paperqa_version": str(pqa_version), - "total_parsed_text_length": ( - len(text) if isinstance(text, str) else sum(len(t) for t in text) + if not isinstance(text, str): + raise NotImplementedError( + "HTML parsing is not yet set up to work with split_lines." + ) + text = html2text(text) + parsing_libraries.append(f"html2text ({html2text_version})") + + return ParsedText( + content=text, + metadata=ParsedMetadata( + parsing_libraries=parsing_libraries, + paperqa_version=pqa_version, + total_parsed_text_length=( + len(text) if isinstance(text, str) else sum(len(t) for t in text) + ), + parse_type="txt" if not html else "html", ), - "parse_type": "txt" if not html else "html", - } - if html: - metadata["parsing_libraries"].append(f"html2text ({html2text.__version__})") # type: ignore[attr-defined] - - return ParsedText(content=text, metadata=ParsedMetadata(**metadata)) + ) def chunk_text( @@ -195,7 +185,7 @@ def chunk_code_text( ) split = split[chunk_chars - overlap :] last_line = i - if len(split) > overlap or len(texts) == 0: + if len(split) > overlap or not texts: texts.append( Text( text=split[:chunk_chars], @@ -214,7 +204,6 @@ def read_doc( include_metadata: Literal[False], chunk_chars: int = ..., overlap: int = ..., - force_pypdf: bool = ..., ) -> list[Text]: ... @@ -226,7 +215,6 @@ def read_doc( include_metadata: Literal[False] = ..., chunk_chars: int = ..., overlap: int = ..., - force_pypdf: bool = ..., ) -> list[Text]: ... @@ -238,7 +226,6 @@ def read_doc( include_metadata: bool = ..., chunk_chars: int = ..., overlap: int = ..., - force_pypdf: bool = ..., ) -> ParsedText: ... @@ -250,7 +237,6 @@ def read_doc( include_metadata: Literal[True], chunk_chars: int = ..., overlap: int = ..., - force_pypdf: bool = ..., ) -> tuple[list[Text], ParsedMetadata]: ... @@ -261,7 +247,6 @@ def read_doc( include_metadata: bool = False, chunk_chars: int = 3000, overlap: int = 100, - force_pypdf: bool = False, ) -> list[Text] | ParsedText | tuple[list[Text], ParsedMetadata]: """Parse a document and split into chunks. @@ -272,7 +257,6 @@ def read_doc( doc: object with document metadata chunk_chars: size of chunks overlap: size of overlap between chunks - force_pypdf: flag to force use of pypdf in parsing parsed_text_only: return parsed text without chunking include_metadata: return a tuple """ @@ -281,20 +265,14 @@ def read_doc( # start with parsing -- users may want to store this separately if str_path.endswith(".pdf"): - if force_pypdf: - parsed_text = parse_pdf_to_pages(path) - else: - try: - parsed_text = parse_pdf_fitz_to_pages(path) - except ImportError: - parsed_text = parse_pdf_to_pages(path) + parsed_text = parse_pdf_to_pages(path) elif str_path.endswith(".txt"): - parsed_text = parse_text(path, html=False, split_lines=False, use_tiktoken=True) + parsed_text = parse_text(path) elif str_path.endswith(".html"): - parsed_text = parse_text(path, html=True, split_lines=False, use_tiktoken=True) + parsed_text = parse_text(path, html=True) else: - parsed_text = parse_text(path, html=False, split_lines=True, use_tiktoken=False) + parsed_text = parse_text(path, split_lines=True, use_tiktoken=False) if parsed_text_only: return parsed_text @@ -309,11 +287,7 @@ def read_doc( ) elif str_path.endswith((".txt", ".html")): chunked_text = chunk_text( - parsed_text, - doc, - chunk_chars=chunk_chars, - overlap=overlap, - use_tiktoken=True, + parsed_text, doc, chunk_chars=chunk_chars, overlap=overlap ) chunk_metadata = ChunkMetadata( chunk_chars=chunk_chars, overlap=overlap, chunk_type="overlap" diff --git a/paperqa/settings.py b/paperqa/settings.py new file mode 100644 index 00000000..7c331a75 --- /dev/null +++ b/paperqa/settings.py @@ -0,0 +1,528 @@ +import importlib.resources +import os +from enum import StrEnum +from pathlib import Path +from typing import Any, ClassVar, assert_never, cast + +from pydantic import BaseModel, ConfigDict, Field, computed_field, field_validator +from pydantic_settings import BaseSettings, CliSettingsSource, SettingsConfigDict + +from paperqa.llms import EmbeddingModel, LiteLLMModel, embedding_model_factory +from paperqa.prompts import ( + citation_prompt, + default_system_prompt, + qa_prompt, + select_paper_prompt, + structured_citation_prompt, + summary_json_prompt, + summary_json_system_prompt, + summary_prompt, +) +from paperqa.utils import hexdigest, pqa_directory +from paperqa.version import __version__ + + +class AnswerSettings(BaseModel): + model_config = ConfigDict(extra="forbid") + + evidence_k: int = Field( + default=10, description="Number of evidence pieces to retrieve" + ) + evidence_detailed_citations: bool = Field( + default=True, description="Whether to include detailed citations in summaries" + ) + evidence_retrieval: bool = Field( + default=True, + description="Whether to use retrieval instead of processing all docs", + ) + evidence_summary_length: str = Field( + default="about 100 words", description="Length of evidence summary" + ) + evidence_skip_summary: bool = Field( + default=False, description="Whether to summarization" + ) + answer_max_sources: int = Field( + default=5, description="Max number of sources to use for an answer" + ) + answer_length: str = Field( + "about 200 words, but can be longer", description="Length of final answer" + ) + max_concurrent_requests: int = Field( + default=4, description="Max concurrent requests to LLMs" + ) + answer_filter_extra_background: bool = Field( + default=False, + description="Whether to cite background information provided by model.", + ) + + +class ParsingOptions(StrEnum): + PAPERQA_DEFAULT = "paperqa_default" + + def available_for_inference(self) -> list["ParsingOptions"]: + return [self.PAPERQA_DEFAULT] # type: ignore[list-item] + + +def _get_parse_type(opt: ParsingOptions, config: "ParsingSettings") -> str: + if opt == ParsingOptions.PAPERQA_DEFAULT: + return config.parser_version_string + assert_never(opt) + + +class ChunkingOptions(StrEnum): + SIMPLE_OVERLAP = "simple_overlap" + + @property + def valid_parsings(self) -> list[ParsingOptions]: + # Note that SIMPLE_OVERLAP must be valid for all by default + # TODO: implement for future parsing options + valid_parsing_dict: dict[str, list[ParsingOptions]] = {} + return valid_parsing_dict.get(self.value, []) # noqa: FURB184 + + +class ParsingSettings(BaseModel): + chunk_size: int = Field(default=3000, description="Number of characters per chunk") + use_doc_details: bool = Field( + default=True, description="Whether to try to get metadata details for a Doc" + ) + overlap: int = Field( + default=100, description="Number of characters to overlap chunks" + ) + citation_prompt: str = Field( + default=citation_prompt, + description="Prompt that tries to create citation from peeking one page", + ) + structured_citation_prompt: str = Field( + default=structured_citation_prompt, + description=( + "Prompt that tries to creates a citation in JSON from peeking one page" + ), + ) + disable_doc_valid_check: bool = Field( + default=False, + description=( + "Whether to disable checking if a document looks like text (was parsed" + " correctly)" + ), + ) + chunking_algorithm: ChunkingOptions = ChunkingOptions.SIMPLE_OVERLAP + model_config = ConfigDict(extra="forbid") + + def chunk_type(self, chunking_selection: ChunkingOptions | None = None) -> str: + """Future chunking implementations (i.e. by section) will get an elif clause here.""" + if chunking_selection is None: + chunking_selection = self.chunking_algorithm + if chunking_selection == ChunkingOptions.SIMPLE_OVERLAP: + return ( + f"{self.parser_version_string}|{chunking_selection.value}" + f"|tokens={self.chunk_size}|overlap={self.overlap}" + ) + assert_never(chunking_selection) + + @property + def parser_version_string(self) -> str: + return f"paperqa-{__version__}" + + def is_chunking_valid_for_parsing(self, parsing: str): + # must map the parsings because they won't include versions by default + return ( + self.chunking_algorithm == ChunkingOptions.SIMPLE_OVERLAP + or parsing + in { # type: ignore[unreachable] + _get_parse_type(p, self) for p in self.chunking_algorithm.valid_parsings + } + ) + + +class _FormatDict(dict): # noqa: FURB189 + """Mock a dictionary and store any missing items.""" + + def __init__(self) -> None: + self.key_set: set[str] = set() + + def __missing__(self, key: str) -> str: + self.key_set.add(key) + return key + + +def get_formatted_variables(s: str) -> set[str]: + """Returns the set of variables implied by the format string.""" + format_dict = _FormatDict() + s.format_map(format_dict) + return format_dict.key_set + + +class PromptSettings(BaseModel): + model_config = ConfigDict(extra="forbid") + + summary: str = summary_prompt + qa: str = qa_prompt + select: str = select_paper_prompt + pre: str | None = Field( + default=None, + description=( + "Opt-in pre-prompt (templated with just the original question) to append" + " information before a qa prompt. For example:" + " 'Summarize all scientific terms in the following question:\n{question}'." + " This pre-prompt can enable injection of question-specific guidance later" + " used by the qa prompt, without changing the qa prompt's template." + ), + ) + post: str | None = None + system: str = default_system_prompt + use_json: bool = False + # Not thrilled about this model, + # but need to split out the system/summary + # to get JSON + summary_json: str = summary_json_prompt + summary_json_system: str = summary_json_system_prompt + EXAMPLE_CITATION: ClassVar[str] = "(Example2012Example pages 3-4)" + + @field_validator("summary") + @classmethod + def check_summary(cls, v: str) -> str: + if not get_formatted_variables(v).issubset( + get_formatted_variables(summary_prompt) + ): + raise ValueError( + "Summary prompt can only have variables:" + f" {get_formatted_variables(summary_prompt)}" + ) + return v + + @field_validator("qa") + @classmethod + def check_qa(cls, v: str) -> str: + if not get_formatted_variables(v).issubset(get_formatted_variables(qa_prompt)): + raise ValueError( + "QA prompt can only have variables:" + f" {get_formatted_variables(qa_prompt)}" + ) + return v + + @field_validator("select") + @classmethod + def check_select(cls, v: str) -> str: + if not get_formatted_variables(v).issubset( + get_formatted_variables(select_paper_prompt) + ): + raise ValueError( + "Select prompt can only have variables:" + f" {get_formatted_variables(select_paper_prompt)}" + ) + return v + + @field_validator("pre") + @classmethod + def check_pre(cls, v: str | None) -> str | None: + if v is not None and get_formatted_variables(v) != {"question"}: + raise ValueError("Pre prompt must have input variables: question") + return v + + @field_validator("post") + @classmethod + def check_post(cls, v: str | None) -> str | None: + if v is not None: + # kind of a hack to get list of attributes in answer + from paperqa.types import Answer + + attrs = set(Answer.model_fields.keys()) + if not get_formatted_variables(v).issubset(attrs): + raise ValueError(f"Post prompt must have input variables: {attrs}") + return v + + +class AgentSettings(BaseModel): + model_config = ConfigDict(extra="forbid") + + agent_llm: str = Field( + default="gpt-4o-2024-08-06", + description="Model to use for agent", + ) + + agent_llm_config: dict | None = Field( + default=None, + description="Optional kwargs for LLM constructor", + ) + + agent_type: str = Field( + default="fake", + description="Type of agent to use", + ) + agent_config: dict[str, Any] | None = Field( + default=None, + description="Optional kwarg for AGENT constructor", + ) + + agent_system_prompt: str | None = Field( + # Matching https://github.com/langchain-ai/langchain/blob/langchain%3D%3D0.2.3/libs/langchain/langchain/agents/openai_functions_agent/base.py#L213-L215 + default="You are a helpful AI assistant.", + description="Optional system prompt message to precede the below agent_prompt.", + ) + + # TODO: make this prompt more minimalist, instead improving tool descriptions so + # how to use them together can be intuited, and exposing them for configuration + agent_prompt: str = ( + "Use the tools to answer the question: {question}\n\nThe {gen_answer_tool_name}" + " tool output is visible to the user, so you do not need to restate the answer" + " and can simply terminate if the answer looks sufficient. The current status" + " of evidence/papers/cost is {status}" + ) + return_paper_metadata: bool = Field( + default=False, + description=( + "Set True to have the search tool include paper title/year information as" + " part of its return." + ), + ) + search_count: int = 8 + wipe_context_on_answer_failure: bool = True + timeout: float = Field( + default=500.0, + description=( + "Matches LangChain AgentExecutor.max_execution_time (seconds), the timeout" + " on agent execution." + ), + ) + should_pre_search: bool = Field( + default=False, + description="If set to true, run the search tool before invoking agent.", + ) + + tool_names: set[str] | None = Field( + default=None, + description=( + "Optional override on the tools to provide the agent. Leaving as the" + " default of None will use a minimal toolset of the paper search, gather" + " evidence, collect cited papers from evidence, and gen answer. If passing" + " tool names (non-default route), at least the gen answer tool must be" + " supplied." + ), + ) + + index_concurrency: int = Field( + default=30, + description="Number of concurrent filesystem reads for indexing", + ) + + @field_validator("tool_names") + @classmethod + def validate_tool_names(cls, v: set[str] | None) -> set[str] | None: + if v is None: + return None + # imported here to avoid circular imports + from paperqa.agents.tools import GenerateAnswer + + answer_tool_name = GenerateAnswer.TOOL_FN_NAME + if answer_tool_name not in v: + raise ValueError( + f"If using an override, must contain at least the {answer_tool_name}." + ) + return v + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(extra="ignore") + + llm: str = Field( + default="gpt-4o-2024-08-06", + description=( + "Default LLM for most things, including answers. Should be 'best' LLM" + ), + ) + llm_config: dict | None = Field( + default=None, + description=( + "LiteLLM Router configuration to pass to LiteLLMModel, must have" + " `model_list` key (corresponding to model_list inputs here:" + " https://docs.litellm.ai/docs/routing), and can optionally include a" + " router_kwargs key with router kwargs as values." + ), + ) + summary_llm: str = Field( + default="gpt-4o-2024-08-06", + description="Default LLM for summaries and parsing citations", + ) + summary_llm_config: dict | None = Field( + default=None, + description=( + "LiteLLM Router configuration to pass to LiteLLMModel, must have" + " `model_list` key (corresponding to model_list inputs here:" + " https://docs.litellm.ai/docs/routing), and can optionally include a" + " router_kwargs key with router kwargs as values." + ), + ) + embedding: str = Field( + "text-embedding-3-small", + description="Default embedding model for texts", + ) + embedding_config: dict | None = Field( + default=None, + description="Extra kwargs to pass to embedding model", + ) + temperature: float = Field(default=0.0, description="Temperature for LLMs") + batch_size: int = Field(default=1, description="Batch size for calling LLMs") + texts_index_mmr_lambda: float = Field( + default=1.0, description="Lambda for MMR in text index" + ) + index_absolute_directory: bool = Field( + default=False, + description="Whether to use the absolute directory for the PQA index", + ) + index_directory: str | os.PathLike | None = Field( + default=pqa_directory("indexes"), + description=( + "Directory to store the PQA generated search index, configuration, and" + " answer indexes." + ), + ) + index_recursively: bool = Field( + default=True, + description="Whether to recurse into subdirectories when indexing sources.", + ) + verbosity: int = Field( + default=0, + description=( + "Integer verbosity level for logging (0-3). 3 = all LLM/Embeddings calls" + " logged" + ), + ) + manifest_file: str | os.PathLike | None = Field( + default=None, + description=( + "Optional manifest CSV, containing columns which are attributes for a" + " DocDetails object. Only 'file_location','doi', and 'title' will be used" + " when indexing." + ), + ) + paper_directory: str | os.PathLike = Field( + default=Path.cwd(), + description=( + "Local directory which contains the papers to be indexed and searched." + ), + ) + + @computed_field # type: ignore[prop-decorator] + @property + def md5(self) -> str: + return hexdigest(self.model_dump_json(exclude={"md5"})) + + answer: AnswerSettings = Field(default_factory=AnswerSettings) + parsing: ParsingSettings = Field(default_factory=ParsingSettings) + prompts: PromptSettings = Field(default_factory=PromptSettings) + agent: AgentSettings = Field(default_factory=AgentSettings) + + def get_index_name(self) -> str: + """Get programmatically generated index name. + + This index is where parsings are stored based on parsing/embedding strategy. + """ + # index name should use an absolute path + # this way two different folders where the + # user locally uses '.' will make different indexes + paper_directory = self.paper_directory + if isinstance(paper_directory, Path): + paper_directory = str(paper_directory.absolute()) + + index_fields = "|".join( + [ + str(paper_directory), + self.embedding, + str(self.parsing.chunk_size), + str(self.parsing.overlap), + self.parsing.chunking_algorithm, + ] + ) + + return f"pqa_index_{hexdigest(index_fields)}" + + @classmethod + def from_name( + cls, config_name: str = "default", cli_source: CliSettingsSource | None = None + ) -> "Settings": + json_path: Path | None = None + + # quick exit for default settings + if config_name == "default": + if not cli_source: + raise NotImplementedError( + f"For config_name {config_name!r}, we require cli_source." + ) + return Settings(_cli_settings_source=cli_source(args=True)) + + # First, try to find the config file in the user's .config directory + user_config_path = pqa_directory("settings") / f"{config_name}.json" + + if user_config_path.exists(): + json_path = user_config_path + + # If not found, fall back to the package's default config + try: + # Use importlib.resources.files() which is recommended for Python 3.9+ + pkg_config_path = ( + importlib.resources.files("paperqa.configs") / f"{config_name}.json" + ) + if pkg_config_path.is_file(): + json_path = cast(Path, pkg_config_path) + except FileNotFoundError as e: + raise FileNotFoundError( + f"No configuration file found for {config_name}" + ) from e + + if json_path: + # we do the ole switcheroo + # json - validate to deserialize knowing the types + # then dump it + # going json.loads directly will not get types correct + tmp = Settings.model_validate_json(json_path.read_text()) + return Settings( + **(tmp.model_dump()), + _cli_settings_source=cli_source(args=True) if cli_source else None, + ) + + raise FileNotFoundError(f"No configuration file found for {config_name}") + + def _default_litellm_router_settings(self, llm: str) -> dict: + """Settings matching "model_list" schema here: https://docs.litellm.ai/docs/routing.""" + return { + "model_list": [ + { + "model_name": llm, + "litellm_params": {"model": llm, "temperature": self.temperature}, + } + ] + } + + def get_llm(self) -> LiteLLMModel: + return LiteLLMModel( + name=self.llm, + config=self.llm_config or self._default_litellm_router_settings(self.llm), + ) + + def get_summary_llm(self) -> LiteLLMModel: + return LiteLLMModel( + name=self.summary_llm, + config=self.summary_llm_config + or self._default_litellm_router_settings(self.summary_llm), + ) + + def get_agent_llm(self) -> LiteLLMModel: + return LiteLLMModel( + name=self.agent.agent_llm, + config=self.agent.agent_llm_config + or self._default_litellm_router_settings(self.agent.agent_llm), + ) + + def get_embedding_model(self) -> EmbeddingModel: + return embedding_model_factory(self.embedding, **(self.embedding_config or {})) + + +MaybeSettings = Settings | str | None + + +def get_settings(config_or_name: MaybeSettings = None) -> Settings: + if isinstance(config_or_name, Settings): + return config_or_name + if config_or_name is None: + return Settings() + return Settings.from_name(config_name=config_or_name) diff --git a/paperqa/types.py b/paperqa/types.py index b0ef0489..9db707a4 100644 --- a/paperqa/types.py +++ b/paperqa/types.py @@ -1,14 +1,20 @@ from __future__ import annotations +import contextvars import logging +import os import re +from collections.abc import Collection +from contextlib import contextmanager from datetime import datetime -from typing import Any, Callable, ClassVar, Collection +from typing import Any, ClassVar from uuid import UUID, uuid4 +import litellm # for cost import tiktoken from pybtex.database import BibliographyData, Entry, Person from pybtex.database.input.bibtex import Parser +from pybtex.scanner import PybtexSyntaxError from pydantic import ( BaseModel, ConfigDict, @@ -18,36 +24,54 @@ model_validator, ) -from .prompts import ( - citation_prompt, - default_system_prompt, - qa_prompt, - select_paper_prompt, - structured_citation_prompt, - summary_json_prompt, - summary_json_system_prompt, - summary_prompt, -) -from .utils import ( +from paperqa.utils import ( create_bibtex_key, encode_id, format_bibtex, get_citenames, ) -from .version import __version__ as pqa_version +from paperqa.version import __version__ as pqa_version # Just for clarity +# also in case one day we want to narrow +# the type DocKey = Any -CallbackFactory = Callable[[str], list[Callable[[str], None]] | None] - logger = logging.getLogger(__name__) +# A context var that will be unique to threads/processes +cvar_answer_id = contextvars.ContextVar[UUID | None]("answer_id", default=None) + + +@contextmanager +def set_llm_answer_ids(answer_id: UUID): + token = cvar_answer_id.set(answer_id) + try: + yield + finally: + cvar_answer_id.reset(token) + class LLMResult(BaseModel): - """A class to hold the result of a LLM completion.""" + """A class to hold the result of a LLM completion. + + To associate a group of LLMResults, you can use the `set_llm_answer_ids` context manager: + + ```python + my_answer_id = uuid4() + with set_llm_answer_ids(my_answer_id): + # code that generates LLMResults + pass + ``` + + and all the LLMResults generated within the context will have the same `answer_id`. + This can be combined with LLMModels `llm_result_callback` to store all LLMResults. + """ id: UUID = Field(default_factory=uuid4) - answer_id: UUID | None = None + answer_id: UUID | None = Field( + default_factory=cvar_answer_id.get, + description="A persistent ID to associate a group of LLMResults", + ) name: str | None = None prompt: str | list[dict] | None = Field( default=None, @@ -65,9 +89,22 @@ class LLMResult(BaseModel): default=0.0, description="Delta time (sec) to last response token's arrival." ) - def __str__(self): + def __str__(self) -> str: return self.text + @computed_field # type: ignore[prop-decorator] + @property + def cost(self) -> float: + """Return the cost of the result in dollars.""" + if self.prompt_count and self.completion_count: + try: + pc = litellm.model_cost[self.model]["input_cost_per_token"] + oc = litellm.model_cost[self.model]["output_cost_per_token"] + return pc * self.prompt_count + oc * self.completion_count + except KeyError: + logger.warning(f"Could not find cost for model {self.model}.") + return 0.0 + class Embeddable(BaseModel): embedding: list[float] | None = Field(default=None, repr=False) @@ -79,7 +116,9 @@ class Doc(Embeddable): dockey: DocKey overwrite_fields_from_metadata: bool = Field( default=True, - description="flag to overwrite fields from metadata when upgrading to a DocDetails", + description=( + "flag to overwrite fields from metadata when upgrading to a DocDetails" + ), ) def __hash__(self) -> int: @@ -91,99 +130,8 @@ class Text(Embeddable): name: str doc: Doc - -# Mock a dictionary and store any missing items -class _FormatDict(dict): - def __init__(self) -> None: - self.key_set: set[str] = set() - - def __missing__(self, key: str) -> str: - self.key_set.add(key) - return key - - -def get_formatted_variables(s: str) -> set[str]: - """Returns the set of variables implied by the format string.""" - format_dict = _FormatDict() - s.format_map(format_dict) - return format_dict.key_set - - -class PromptCollection(BaseModel): - summary: str = summary_prompt - qa: str = qa_prompt - select: str = select_paper_prompt - cite: str = citation_prompt - structured_cite: str = structured_citation_prompt - pre: str | None = Field( - default=None, - description=( - "Opt-in pre-prompt (templated with just the original question) to append" - " information before a qa prompt. For example:" - " 'Summarize all scientific terms in the following question:\n{question}'." - " This pre-prompt can enable injection of question-specific guidance later" - " used by the qa prompt, without changing the qa prompt's template." - ), - ) - post: str | None = None - system: str = default_system_prompt - skip_summary: bool = False - json_summary: bool = False - # Not thrilled about this model, - # but need to split out the system/summary - # to get JSON - summary_json: str = summary_json_prompt - summary_json_system: str = summary_json_system_prompt - - @field_validator("summary") - @classmethod - def check_summary(cls, v: str) -> str: - if not set(get_formatted_variables(v)).issubset( - set(get_formatted_variables(summary_prompt)) - ): - raise ValueError( - f"Summary prompt can only have variables: {get_formatted_variables(summary_prompt)}" - ) - return v - - @field_validator("qa") - @classmethod - def check_qa(cls, v: str) -> str: - if not set(get_formatted_variables(v)).issubset( - set(get_formatted_variables(qa_prompt)) - ): - raise ValueError( - f"QA prompt can only have variables: {get_formatted_variables(qa_prompt)}" - ) - return v - - @field_validator("select") - @classmethod - def check_select(cls, v: str) -> str: - if not set(get_formatted_variables(v)).issubset( - set(get_formatted_variables(select_paper_prompt)) - ): - raise ValueError( - f"Select prompt can only have variables: {get_formatted_variables(select_paper_prompt)}" - ) - return v - - @field_validator("pre") - @classmethod - def check_pre(cls, v: str | None) -> str | None: - if v is not None and set(get_formatted_variables(v)) != {"question"}: - raise ValueError("Pre prompt must have input variables: question") - return v - - @field_validator("post") - @classmethod - def check_post(cls, v: str | None) -> str | None: - if v is not None: - # kind of a hack to get list of attributes in answer - attrs = set(Answer.model_fields.keys()) - if not set(get_formatted_variables(v)).issubset(attrs): - raise ValueError(f"Post prompt must have input variables: {attrs}") - return v + def __hash__(self) -> int: + return hash(self.text) class Context(BaseModel): @@ -195,10 +143,9 @@ class Context(BaseModel): text: Text score: int = 5 - -def __str__(self) -> str: # noqa: N807 - """Return the context as a string.""" - return self.context + def __str__(self) -> str: + """Return the context as a string.""" + return self.context class Answer(BaseModel): @@ -211,14 +158,17 @@ class Answer(BaseModel): contexts: list[Context] = [] references: str = "" formatted_answer: str = "" - dockey_filter: set[DocKey] | None = None - summary_length: str = "about 100 words" - answer_length: str = "about 100 words" - # just for convenience you can override this - cost: float | None = None + cost: float = 0.0 # Map model name to a two-item list of LLM prompt token counts # and LLM completion token counts token_counts: dict[str, list[int]] = Field(default_factory=dict) + config_md5: str | None = Field( + default=None, + frozen=True, + description=( + "MD5 hash of the settings used to generate the answer. Cannot change" + ), + ) model_config = ConfigDict(extra="ignore") def __str__(self) -> str: @@ -232,7 +182,7 @@ def remove_computed(cls, data: Any) -> Any: data.pop("used_contexts", None) return data - @computed_field # type: ignore[misc] + @computed_field # type: ignore[prop-decorator] @property def used_contexts(self) -> set[str]: """Return the used contexts.""" @@ -248,7 +198,7 @@ def get_citation(self, name: str) -> str: raise ValueError(f"Could not find docname {name} in contexts.") from exc return doc.citation - def add_tokens(self, result: LLMResult): + def add_tokens(self, result: LLMResult) -> None: """Update the token counts for the given result.""" if result.model not in self.token_counts: self.token_counts[result.model] = [ @@ -259,6 +209,8 @@ def add_tokens(self, result: LLMResult): self.token_counts[result.model][0] += result.prompt_count self.token_counts[result.model][1] += result.completion_count + self.cost += result.cost + def get_unique_docs_from_contexts(self, score_threshold: int = 0) -> set[Doc]: """Parse contexts for docs with scores above the input threshold.""" return { @@ -266,6 +218,21 @@ def get_unique_docs_from_contexts(self, score_threshold: int = 0) -> set[Doc]: for c in filter(lambda x: x.score >= score_threshold, self.contexts) } + def filter_content_for_user(self) -> None: + """Filter out extra items (inplace) that do not need to be returned to the user.""" + self.contexts = [ + Context( + context=c.context, + score=c.score, + text=Text( + text="", + **c.text.model_dump(exclude={"text", "embedding", "doc"}), + doc=Doc(**c.text.doc.model_dump(exclude={"embedding"})), + ), + ) + for c in self.contexts + ] + class ChunkMetadata(BaseModel): """Metadata for chunking algorithm.""" @@ -353,18 +320,29 @@ class DocDetails(Doc): source_quality: int | None = Field( default=None, - description="Quality of journal/venue of paper. " - " We use None as a sentinel for unset values (like for determining hydration) " - " So, we use -1 means unknown quality and None means it needs to be hydrated.", + description=( + "Quality of journal/venue of paper. We use None as a sentinel for unset" + " values (like for determining hydration) So, we use -1 means unknown" + " quality and None means it needs to be hydrated." + ), + ) + is_retracted: bool | None = Field( + default=None, description="Flag for whether the paper is retracted." ) doi: str | None = None doi_url: str | None = None doc_id: str | None = None + file_location: str | os.PathLike | None = None other: dict[str, Any] = Field( default_factory=dict, description="Other metadata besides the above standardized fields.", ) UNDEFINED_JOURNAL_QUALITY: ClassVar[int] = -1 + DOI_URL_FORMATS: ClassVar[Collection[str]] = { + "https://doi.org/", + "http://dx.doi.org/", + } + AUTHOR_NAMES_TO_REMOVE: ClassVar[Collection[str]] = {"et al", "et al."} @field_validator("key") @classmethod @@ -372,9 +350,13 @@ def clean_key(cls, value: str) -> str: # Replace HTML tags with empty string return re.sub(pattern=r"<\/?\w{1,10}>", repl="", string=value) - @staticmethod - def lowercase_doi_and_populate_doc_id(data: dict[str, Any]) -> dict[str, Any]: + @classmethod + def lowercase_doi_and_populate_doc_id(cls, data: dict[str, Any]) -> dict[str, Any]: if doi := data.get("doi"): + remove_urls = cls.DOI_URL_FORMATS + for url in remove_urls: + if doi.startswith(url): + doi = doi.replace(url, "") data["doi"] = doi.lower() data["doc_id"] = encode_id(doi.lower()) else: @@ -419,6 +401,7 @@ def inject_clean_doi_url_into_data(data: dict[str, Any]) -> dict[str, Any]: if doi and not doi_url: doi_url = "https://doi.org/" + doi + # ensure the modern doi url is used if doi_url: data["doi_url"] = doi_url.replace( "http://dx.doi.org/", "https://doi.org/" @@ -426,9 +409,19 @@ def inject_clean_doi_url_into_data(data: dict[str, Any]) -> dict[str, Any]: return data + @classmethod + def remove_invalid_authors(cls, data: dict[str, Any]) -> dict[str, Any]: + """Capture and cull strange author names.""" + if authors := data.get("authors"): + data["authors"] = [ + a for a in authors if a.lower() not in cls.AUTHOR_NAMES_TO_REMOVE + ] + + return data + @staticmethod def overwrite_docname_dockey_for_compatibility_w_doc( - data: dict[str, Any] + data: dict[str, Any], ) -> dict[str, Any]: """Overwrite fields from metadata if specified.""" overwrite_fields = {"key": "docname", "doc_id": "dockey"} @@ -439,7 +432,7 @@ def overwrite_docname_dockey_for_compatibility_w_doc( return data @classmethod - def populate_bibtex_key_citation( # noqa: C901, PLR0912 + def populate_bibtex_key_citation( # noqa: PLR0912 cls, data: dict[str, Any] ) -> dict[str, Any]: """Add or modify bibtex, key, and citation fields. @@ -477,10 +470,13 @@ def populate_bibtex_key_citation( # noqa: C901, PLR0912 data["other"]["bibtex_source"] = ["self_generated"] else: data["other"] = {"bibtex_source": ["self_generated"]} - - existing_entry = next( - iter(Parser().parse_string(data["bibtex"]).entries.values()) - ) + try: + existing_entry = next( + iter(Parser().parse_string(data["bibtex"]).entries.values()) + ) + except PybtexSyntaxError: + logger.warning(f"Failed to parse bibtex for {data['bibtex']}.") + existing_entry = None entry_data = { "title": data.get("title") or CITATION_FALLBACK_DATA["title"], @@ -505,7 +501,9 @@ def populate_bibtex_key_citation( # noqa: C901, PLR0912 } entry_data = {k: v for k, v in entry_data.items() if v} try: - new_entry = Entry(data.get("bibtex_type", "article"), fields=entry_data) + new_entry = Entry( + data.get("bibtex_type", "article") or "article", fields=entry_data + ) if existing_entry: new_entry = cls.merge_bibtex_entries(existing_entry, new_entry) # add in authors manually into the entry @@ -519,17 +517,23 @@ def populate_bibtex_key_citation( # noqa: C901, PLR0912 if data.get("overwrite_fields_from_metadata", True): data["citation"] = None except Exception: - logger.exception(f"Failed to generate bibtex for {data}") - if not data.get("citation"): + logger.warning( + "Failed to generate bibtex for" + f" {data.get('docname') or data.get('citation')}" + ) + if not data.get("citation") and data.get("bibtex") is not None: data["citation"] = format_bibtex( - data["bibtex"], clean=True, missing_replacements=CITATION_FALLBACK_DATA # type: ignore[arg-type] + data["bibtex"], missing_replacements=CITATION_FALLBACK_DATA # type: ignore[arg-type] ) + elif not data.get("citation"): + data["citation"] = data.get("title") or CITATION_FALLBACK_DATA["title"] return data @model_validator(mode="before") @classmethod def validate_all_fields(cls, data: dict[str, Any]) -> dict[str, Any]: data = cls.lowercase_doi_and_populate_doc_id(data) + data = cls.remove_invalid_authors(data) data = cls.misc_string_cleaning(data) data = cls.inject_clean_doi_url_into_data(data) data = cls.populate_bibtex_key_citation(data) @@ -544,24 +548,30 @@ def __getitem__(self, item: str): @property def formatted_citation(self) -> str: + + if self.is_retracted: + return f"**RETRACTED ARTICLE** Citation: {self.citation} Retrieved from http://retractiondatabase.org/." + if ( self.citation is None # type: ignore[redundant-expr] or self.citation_count is None or self.source_quality is None ): raise ValueError( - "Citation, citationCount, and sourceQuality are not set -- do you need to call `hydrate`?" + "Citation, citationCount, and sourceQuality are not set -- do you need" + " to call `hydrate`?" ) quality = ( SOURCE_QUALITY_MESSAGES[self.source_quality] if self.source_quality >= 0 else None ) + if quality is None: return f"{self.citation} This article has {self.citation_count} citations." return ( - f"{self.citation} This article has {self.citation_count} citations and is from a " - f"{quality}." + f"{self.citation} This article has {self.citation_count} citations and is" + f" from a {quality}." ) OPTIONAL_HYDRATION_FIELDS: ClassVar[Collection[str]] = {"url"} @@ -585,7 +595,7 @@ def repopulate_doc_id_from_doi(self) -> None: if self.doi: self.doc_id = encode_id(self.doi) - def __add__(self, other: DocDetails | int) -> DocDetails: # noqa: C901 + def __add__(self, other: DocDetails | int) -> DocDetails: """Merge two DocDetails objects together.""" # control for usage w. Python's sum() function if isinstance(other, int): @@ -605,7 +615,7 @@ def __add__(self, other: DocDetails | int) -> DocDetails: # noqa: C901 # Merge 'other' dictionaries merged_data[field] = {**self.other, **other.other} # handle the bibtex / sources as special fields - for field_to_combine in ["bibtex_source", "client_source"]: + for field_to_combine in ("bibtex_source", "client_source"): if self.other.get(field_to_combine) and other.other.get( field_to_combine ): @@ -646,7 +656,9 @@ def __add__(self, other: DocDetails | int) -> DocDetails: # noqa: C901 # pre-prints / arXiv versions of papers that are not as up-to-date merged_data[field] = ( other_value - if (other_value is not None and PREFER_OTHER) + if ( + (other_value is not None and other_value != []) and PREFER_OTHER + ) else self_value ) diff --git a/paperqa/utils.py b/paperqa/utils.py index 19f521e0..4e3ce428 100644 --- a/paperqa/utils.py +++ b/paperqa/utils.py @@ -3,22 +3,24 @@ import asyncio import hashlib import inspect -import json import logging +import logging.config import math +import os import re import string -from collections.abc import Iterable +from collections.abc import Collection, Coroutine, Iterable, Iterator from datetime import datetime from functools import reduce from http import HTTPStatus from pathlib import Path -from typing import Any, BinaryIO, Collection, Coroutine, Iterator, Union +from typing import Any, BinaryIO, ClassVar from uuid import UUID import aiohttp import httpx -import pypdf +import litellm +import pymupdf from pybtex.database import Person, parse_string from pybtex.database.input.bibtex import Parser from pybtex.style.formatting import unsrtalpha @@ -34,7 +36,13 @@ logger = logging.getLogger(__name__) -StrPath = Union[str, Path] +StrPath = str | Path + + +class ImpossibleParsingError(Exception): + """Error to throw when a parsing is impossible.""" + + LOG_METHOD_NAME: ClassVar[str] = "warning" def name_in_text(name: str, text: str) -> bool: @@ -44,7 +52,7 @@ def name_in_text(name: str, text: str) -> bool: def maybe_is_text(s: str, thresh: float = 2.5) -> bool: - if len(s) == 0: + if not s: return False # Calculate the entropy of the string entropy = 0.0 @@ -70,7 +78,7 @@ def maybe_is_html(file: BinaryIO) -> bool: def strings_similarity(s1: str, s2: str) -> float: - if len(s1) == 0 or len(s2) == 0: + if not s1 or not s2: return 0 # break the strings into words ss1 = set(s1.split()) @@ -80,23 +88,19 @@ def strings_similarity(s1: str, s2: str) -> float: def count_pdf_pages(file_path: StrPath) -> int: - with open(file_path, "rb") as pdf_file: - try: # try fitz by default - import fitz + with pymupdf.open(file_path) as doc: + return len(doc) - doc = fitz.open(file_path) - num_pages = len(doc) - except ModuleNotFoundError: # pypdf instead - pdf_reader = pypdf.PdfReader(pdf_file) - num_pages = len(pdf_reader.pages) - return num_pages +def hexdigest(data: str | bytes) -> str: + if isinstance(data, str): + return hashlib.md5(data.encode("utf-8")).hexdigest() # noqa: S324 + return hashlib.md5(data).hexdigest() # noqa: S324 -def md5sum(file_path: StrPath) -> str: - import hashlib +def md5sum(file_path: StrPath) -> str: with open(file_path, "rb") as f: - return hashlib.md5(f.read()).hexdigest() # noqa: S324 + return hexdigest(f.read()) async def gather_with_concurrency(n: int, coros: list[Coroutine]) -> list[Any]: @@ -117,6 +121,37 @@ def strip_citations(text: str) -> str: return re.sub(citation_regex, "", text, flags=re.MULTILINE) +def extract_score(text: str) -> int: + # check for N/A + last_line = text.split("\n")[-1] + if "N/A" in last_line or "n/a" in last_line or "NA" in last_line: + return 0 + # check for not applicable, not relevant in summary + if "not applicable" in text.lower() or "not relevant" in text.lower(): + return 0 + + score = re.search(r"[sS]core[:is\s]+([0-9]+)", text) + if not score: + score = re.search(r"\(([0-9])\w*\/", text) + if not score: + score = re.search(r"([0-9]+)\w*\/", text) + if score: + s = int(score.group(1)) + if s > 10: # noqa: PLR2004 + s = int(s / 10) # sometimes becomes out of 100 + return s + last_few = text[-15:] + scores = re.findall(r"([0-9]+)", last_few) + if scores: + s = int(scores[-1]) + if s > 10: # noqa: PLR2004 + s = int(s / 10) # sometimes becomes out of 100 + return s + if len(text) < 100: # noqa: PLR2004 + return 1 + return 5 + + def get_citenames(text: str) -> set[str]: # Combined regex for identifying citations (see unit tests for examples) citation_regex = r"\b[\w\-]+\set\sal\.\s\([0-9]{4}\)|\((?:[^\)]*?[a-zA-Z][^\)]*?[0-9]{4}[^\)]*?)\)" @@ -127,12 +162,12 @@ def get_citenames(text: str) -> set[str]: results.extend(none_results) values = [] for citation in results: - citation = citation.strip("() ") # noqa: PLW2901 + citation = citation.strip("() ") for c in re.split(",|;", citation): if c == "Extra background information": continue # remove leading/trailing spaces - c = c.strip() # noqa: PLW2901 + c = c.strip() values.append(c) return set(values) @@ -151,8 +186,7 @@ def extract_doi(reference: str) -> str: # If DOI is found in the reference, return the DOI link if doi_match: return "https://doi.org/" + doi_match.group() - else: # noqa: RET505 - return "" + return "" def batch_iter(iterable: list, n: int = 1) -> Iterator[list]: @@ -168,19 +202,9 @@ def batch_iter(iterable: list, n: int = 1) -> Iterator[list]: yield iterable[ndx : min(ndx + n, length)] -def flatten(iteratble: list) -> list: - """ - Flatten a list of lists. - - :param l: The list of lists to flatten - :return: A flattened list - """ - return [item for sublist in iteratble for item in sublist] - - def get_loop() -> asyncio.AbstractEventLoop: try: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) @@ -195,28 +219,6 @@ def is_coroutine_callable(obj): return False -def llm_read_json(text: str) -> dict: - """Read LLM output and extract JSON data from it.""" - # fetch from markdown ```json if present - text = text.strip().split("```json")[-1].split("```")[0] - # split anything before the first { - text = "{" + text.split("{", 1)[-1] - # split anything after the last } - text = text.rsplit("}", 1)[0] + "}" - - # escape new lines within strings - def replace_newlines(match: re.Match) -> str: - return match.group(0).replace("\n", "\\n") - - # Match anything between double quotes - # including escaped quotes and other escaped characters. - # https://regex101.com/r/VFcDmB/1 - pattern = r'"(?:[^"\\]|\\.)*"' - text = re.sub(pattern, replace_newlines, text) - - return json.loads(text) - - def encode_id(value: str | bytes | UUID, maxsize: int | None = 16) -> str: """Encode a value (e.g. a DOI) optionally with a max length.""" if isinstance(value, UUID): @@ -279,7 +281,7 @@ def clean_upbibtex(bibtex: str) -> str: return bibtex -def format_bibtex( # noqa: C901 +def format_bibtex( bibtex: str, key: str | None = None, clean: bool = True, @@ -366,16 +368,24 @@ def bibtex_field_extract( return missing_replacements.get(field, "") +UNKNOWN_AUTHOR_KEY: str = "unknownauthors" + + def create_bibtex_key(author: list[str], year: str, title: str) -> str: - FORBIDDEN_KEY_CHARACTERS = {"_", " ", "-", "/", "'", "`", ":"} - author_rep = ( - author[0].split()[-1].casefold() - if "Unknown" not in author[0] - else "unknownauthors" - ) - key = ( - f"{author_rep}{year}{''.join([t.casefold() for t in title.split()[:3]])[:100]}" - ) + FORBIDDEN_KEY_CHARACTERS = {"_", " ", "-", "/", "'", "`", ":", ",", "\n"} + try: + author_rep = ( + author[0].split()[-1].casefold() + if "Unknown" not in author[0] + else UNKNOWN_AUTHOR_KEY + ) + except IndexError: + author_rep = UNKNOWN_AUTHOR_KEY + # we don't want a bibtex-parsing induced line break in the key + # so we cap it to 100+50+4 = 154 characters max + # 50 for the author, 100 for the first three title words, 4 for the year + # the first three title words are just emulating the s2 convention + key = f"{author_rep[:50]}{year}{''.join([t.casefold() for t in title.split()[:3]])[:100]}" return remove_substrings(key, FORBIDDEN_KEY_CHARACTERS) @@ -398,7 +408,7 @@ async def _get_with_retrying( params: dict[str, Any], session: aiohttp.ClientSession, headers: dict[str, str] | None = None, - timeout: float = 10.0, + timeout: float = 10.0, # noqa: ASYNC109 http_exception_mappings: dict[HTTPStatus | int, Exception] | None = None, ) -> dict[str, Any]: """Get from a URL with retrying protection.""" @@ -419,3 +429,51 @@ async def _get_with_retrying( def union_collections_to_ordered_list(collections: Iterable) -> list: return sorted(reduce(lambda x, y: set(x) | set(y), collections)) + + +def pqa_directory(name: str) -> Path: + if pqa_home := os.environ.get("PQA_HOME"): + directory = Path(pqa_home) / ".pqa" / name + else: + directory = Path.home() / ".pqa" / name + + directory.mkdir(parents=True, exist_ok=True) + return directory + + +def setup_default_logs() -> None: + """Configure logs to reasonable defaults.""" + fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + + # Set sane default LiteLLM logging configuration + # SEE: https://docs.litellm.ai/docs/observability/telemetry + litellm.telemetry = False + + logging.config.dictConfig( + { + "version": 1, + "disable_existing_loggers": False, + # Configure a default format and level for all loggers + "formatters": { + "standard": { + "format": fmt, + }, + }, + "handlers": { + "default": { + "level": "INFO", + "formatter": "standard", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + }, + }, + # Lower level for httpx and LiteLLM + "loggers": { + "httpx": {"level": "WARNING"}, + # SEE: https://github.com/BerriAI/litellm/issues/2256 + "LiteLLM": {"level": "WARNING"}, + "LiteLLM Router": {"level": "WARNING"}, + "LiteLLM Proxy": {"level": "WARNING"}, + }, + } + ) diff --git a/pyproject.toml b/pyproject.toml index b8e5bbd6..c4a07a9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools>=64", "setuptools_scm>=8"] [project] authors = [ - {email = "white.d.andrew@gmail.com", name = "Andrew White"}, + {email = "hello@futurehouse.org", name = "FutureHouse technical staff"}, ] # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers = [ @@ -14,37 +14,66 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python", ] dependencies = [ "PyCryptodome", - "html2text", + "aiohttp", # TODO: remove in favor of httpx + "anyio", + "fhaviary[llm]>=0.6", # For info on Message + "html2text", # TODO: evaluate moving to an opt-in dependency + "httpx", + "litellm>=1.44", # to prevent sys.stdout on router creation "numpy", - "openai>=1", "pybtex", + "pydantic-settings", "pydantic~=2.0", - "pypdf", + "pymupdf", + "rich", + "setuptools", # TODO: remove after release of https://bitbucket.org/pybtex-devs/pybtex/pull-requests/46/replace-pkg_resources-with-importlib + "tantivy", + "tenacity", "tiktoken>=0.4.0", ] description = "LLM Chain for answering questions from docs" -dynamic = ["optional-dependencies", "version"] +dynamic = ["version"] keywords = ["question answering"] license = {file = "LICENSE"} maintainers = [ {email = "jamesbraza@gmail.com", name = "James Braza"}, + {email = "michael.skarlinski@gmail.com", name = "Michael Skarlinski"}, {email = "white.d.andrew@gmail.com", name = "Andrew White"}, ] name = "paper-qa" readme = "README.md" -requires-python = ">=3.8" -urls = {repository = "https://github.com/whitead/paper-qa"} +requires-python = ">=3.11" + +[project.optional-dependencies] +ldp = [ + "ldp>=0.4", +] +typing = [ + "types-PyYAML", + "types-setuptools", +] +zotero = [ + "pyzotero", +] + +[project.scripts] +pqa = "paperqa.agents:main" + +[project.urls] +issues = "https://github.com/Future-House/paper-qa/issues" +repository = "https://github.com/Future-House/paper-qa" + +[tool.black] +preview = true [tool.codespell] check-filenames = true check-hidden = true -ignore-words-list = "aadd,ser" +ignore-words-list = "aadd,astroid,ser" [tool.mypy] # Type-checks the interior of functions without type annotations. @@ -59,12 +88,23 @@ enable_error_code = [ "redundant-self", "truthy-bool", "truthy-iterable", + "unimported-reveal", "unreachable", "unused-awaitable", "unused-ignore", ] # Shows a short summary line after error messages. error_summary = false +# A regular expression that matches file names, directory names and paths which mypy +# should ignore while recursively discovering files to check. Use forward slashes (/) as +# directory separators on all platforms. +exclude = [ + "^\\.?venv", # SEE: https://regex101.com/r/0rp5Br/1 +] +# Specifies the OS platform for the target program, for example darwin or win32 +# (meaning OS X or Windows, respectively). The default is the current platform +# as revealed by Python’s sys.platform variable. +platform = "linux" # Comma-separated list of mypy plugins. plugins = ["pydantic.mypy"] # Use visually nicer output in error messages: use soft word wrap, show source @@ -93,12 +133,94 @@ warn_unused_ignores = true ignore_missing_imports = true # Per-module configuration options module = [ - "fitz", + "litellm", # SEE: https://github.com/BerriAI/litellm/issues/825 + "pybtex.*", # SEE: https://bitbucket.org/pybtex-devs/pybtex/issues/141/type-annotations "pyzotero", # SEE: https://github.com/urschrei/pyzotero/issues/110 - "sentence_transformers", # SEE: https://github.com/UKPLab/sentence-transformers/issues/1723 - "voyageai.*", # SEE: https://github.com/voyage-ai/voyageai-python/issues/8 ] +[tool.pylint] + +[tool.pylint.design] +# Maximum number of attributes for a class (see R0902). +max-attributes = 12 + +[tool.pylint.format] +# Maximum number of characters on a single line. +max-line-length = 88 # Match ruff line-length + +[tool.pylint.main] +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs = 0 +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins = [ + "pylint_pydantic", +] + +[tool.pylint.messages_control] +# Disable the message, report, category or checker with the given id(s). +disable = [ + "arguments-differ", # Ops intentionally differ arguments + "attribute-defined-outside-init", # Disagrees with reset pattern + "bare-except", # Rely on ruff E722 for this + "broad-exception-caught", # Don't care to enforce this + "broad-exception-raised", # Rely on ruff TRY002 for this + "cyclic-import", # Let Python blow up + "expression-not-assigned", # Rely on mypy func-returns-value for this + "fixme", # codetags are useful + "function-redefined", # Rely on mypy no-redef for this + "import-outside-toplevel", # Rely on ruff PLC0415 for this + "inconsistent-return-statements", # TODO: remove after pylint>=3.3 for https://github.com/pylint-dev/pylint/pull/9591 + "invalid-name", # Don't care to enforce this + "line-too-long", # Rely on ruff E501 for this + "logging-fstring-interpolation", # f-strings are convenient + "logging-too-many-args", # Rely on ruff PLE1205 for this + "missing-docstring", # Let docformatter and ruff take care of docstrings + "missing-final-newline", # Rely on ruff W292 for this + "no-else-return", # Rely on ruff RET506 for this + "no-member", # Buggy, SEE: https://github.com/pylint-dev/pylint/issues/8138 + "not-callable", # Don't care to enforce this + "protected-access", # Don't care to enforce this + "raise-missing-from", # Rely on ruff B904 for this + "redefined-builtin", # Rely on ruff A002 for this + "super-init-not-called", # Don't care to enforce this + "too-few-public-methods", # Don't care to enforce this + "too-many-ancestors", # Don't care to enforce this + "too-many-arguments", # Don't care to enforce this + "too-many-branches", # Rely on ruff PLR0912 for this + "too-many-instance-attributes", # Don't care to enforce this + "too-many-lines", # Don't care to enforce this + "too-many-locals", # Rely on ruff PLR0914 for this + "too-many-return-statements", # Rely on ruff PLR0911 for this + "too-many-statements", # Rely on ruff PLR0915 for this + "undefined-loop-variable", # Don't care to enforce this + "ungrouped-imports", # Rely on ruff I001 for this + "unidiomatic-typecheck", # Rely on ruff E721 for this + "unreachable", # Rely on mypy unreachable for this + "unspecified-encoding", # Don't care to enforce this + "unsubscriptable-object", # Buggy, SEE: https://github.com/pylint-dev/pylint/issues/3637 + "unsupported-membership-test", # Buggy, SEE: https://github.com/pylint-dev/pylint/issues/3045 + "unused-argument", # Rely on ruff ARG002 for this + "unused-import", # Rely on ruff F401 for this + "unused-variable", # Rely on ruff F841 for this + "wrong-import-order", # Rely on ruff I001 for this + "wrong-import-position", # Rely on ruff E402 for this +] +# Enable the message, report, category or checker with the given id(s). +enable = [ + "useless-suppression", # Print unused `pylint: disable` comments +] + +[tool.pylint.reports] +# Set true to activate the evaluation score. +score = false + +[tool.pylint.similarities] +# Minimum lines number of a similarity. +min-similarity-lines = 12 + [tool.pytest.ini_options] # List of directories that should be searched for tests when no specific directories, # files or test ids are given in the command line when executing pytest from the rootdir @@ -106,15 +228,35 @@ module = [ # pattern. testpaths = ["tests"] +[tool.refurb] +enable_all = true +ignore = [ + "FURB101", # FURB101, FURB103, FURB141, FURB144, FURB146, FURB147, FURB150, FURB155: no need for pathlib + "FURB103", + "FURB141", + "FURB144", + "FURB146", + "FURB147", + "FURB150", + "FURB155", +] + [tool.ruff] # Line length to use when enforcing long-lines violations (like `E501`). line-length = 120 # Enable application of unsafe fixes. unsafe-fixes = true +[tool.ruff.format] +# Enable reformatting of code snippets in docstrings. +docstring-code-format = true +# Enable preview style formatting. +preview = true + [tool.ruff.lint] explicit-preview-rules = true extend-select = [ + "C420", "FURB110", "FURB113", "FURB116", @@ -137,7 +279,6 @@ extend-select = [ "PLR6201", "PLW0108", "RUF022", - "RUF025", ] # List of rule codes that are unsupported by Ruff, but should be preserved when # (e.g.) validating # noqa directives. Useful for retaining # noqa directives @@ -148,6 +289,7 @@ external = [ ignore = [ "ANN", # Don't care to enforce typing "BLE001", # Don't care to enforce blind exception catching + "C901", # we can be complex "COM812", # Trailing comma with black leads to wasting lines "D100", # D100, D101, D102, D103, D104, D105, D106, D107: don't always need docstrings "D101", @@ -172,9 +314,12 @@ ignore = [ "FLY002", # Can be less readable "G004", # f-strings are convenient "INP001", # Can use namespace packages + "ISC001", # For ruff format compatibility "N803", # Want to use 'N', or 'L', "N806", # Want to use 'N', or 'L', "PLR0913", + "PLR0915", # we can write lots of code + "PLW2901", # Allow modifying loop variables "PTH", # Overly pedantic "S311", # Ok to use python random "SLF001", # Overly pedantic @@ -184,13 +329,13 @@ ignore = [ "TCH003", "TD002", # Don't care for TODO author "TD003", # Don't care for TODO links - "TID252", # Allow relative imports for packaging "TRY003", # Overly pedantic ] preview = true select = ["ALL"] unfixable = [ "B007", # While debugging, unused loop variables can be useful + "B905", # Default fix is zip(strict=False), but that can hide bugs "ERA001", # While debugging, temporarily commenting code can be useful "F401", # While debugging, unused imports can be useful "F841", # While debugging, unused locals can be useful @@ -200,9 +345,12 @@ unfixable = [ mypy-init-return = true [tool.ruff.lint.per-file-ignores] -"tests/*.py" = [ +"**/tests/*.py" = [ + "N802", # Tests function names can match class names "PLR2004", # Tests can have magic values "S101", # Tests can have assertions + "S301", # can test pickle + "S310", ] [tool.ruff.lint.pycodestyle] @@ -215,8 +363,8 @@ max-doc-length = 120 # Match line-length # defaults when analyzing docstring sections. convention = "google" -[tool.setuptools.dynamic.optional-dependencies.dev] -file = ["dev-requirements.txt"] +[tool.setuptools.package-data] +paperqa = ["configs/**json"] [tool.setuptools.packages.find] include = ["paperqa*"] @@ -230,3 +378,26 @@ in_place = true spaces_before_inline_comment = 2 # Match Python PEP 8 spaces_indent_inline_array = 4 # Match Python PEP 8 trailing_comma_inline_array = true + +[tool.uv] +dev-dependencies = [ + "build", # TODO: remove after https://github.com/astral-sh/uv/issues/6278 + "ipython>=8", # Pin to keep recent + "mypy>=1.8", # Pin for mutable-override + "paper-qa[ldp,typing]", + "pre-commit~=3.4", # Pin to keep recent + "pydantic~=2.0", + "pylint-pydantic", + "pytest-asyncio", + "pytest-recording", + "pytest-rerunfailures", + "pytest-subtests", + "pytest-sugar", + "pytest-timer[colorama]", + "pytest-xdist", + "pytest>=8", # Pin to keep recent + "python-dotenv", + "pyzotero", + "refurb>=2", # Pin to keep recent + "requests", +] diff --git a/tests/cassettes/test_author_matching.yaml b/tests/cassettes/test_author_matching.yaml index cedcb2d6..05d2fa6a 100644 --- a/tests/cassettes/test_author_matching.yaml +++ b/tests/cassettes/test_author_matching.yaml @@ -7,7 +7,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":3960,"items":[{"DOI":"10.4337\/cilj.2023.02.08","author":[{"ORCID":"http:\/\/orcid.org\/0000-0002-2467-681X","authenticated-orcid":true,"given":"Jack + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":3975,"items":[{"DOI":"10.4337\/cilj.2023.02.08","author":[{"ORCID":"http:\/\/orcid.org\/0000-0002-2467-681X","authenticated-orcid":true,"given":"Jack Wright","family":"Nelson","sequence":"first","affiliation":[{"name":"Doctoral Candidate, McGill University Faculty of Law, Montreal, QC, Canada"},{"name":"Adjunct Research Fellow, National University of Singapore Faculty of Law, Singapore"}]}],"title":["Large @@ -25,7 +25,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:44 GMT + - Wed, 04 Sep 2024 22:52:23 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -61,7 +61,7 @@ interactions: "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", "name": - "P. Schwaller"}], "matchScore": 175.55591}]} + "P. Schwaller"}], "matchScore": 179.37169}]} ' headers: @@ -74,27 +74,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:50 GMT + - Wed, 04 Sep 2024 22:52:24 GMT Via: - - 1.1 c4199de5b59b067ce72a20c751022aa8.cloudfront.net (CloudFront) + - 1.1 8eabaca8b591c36a72bf060174c30de0.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - Zo-Bv6RkIySq8Lz1wlU5-IaRvStXYgufZEiP7e1tWVfWwmL0ZiSabA== + - zk6ySahGbIb5ZQ_IJZpNhucv3LuNoOKuSBlYD6DVVIjnSt9fYcHIKA== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcSzEPWPHcEVVA= + - dmiqPHWGPHcEpuw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "684" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:50 GMT + - Wed, 04 Sep 2024 22:52:24 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 03ac53c0-5c35-4f92-a60c-ffdf5d560c25 + - 3e3deb85-645d-4e10-94a7-e7e67aac87d9 status: code: 200 message: OK @@ -114,7 +114,7 @@ interactions: "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", "name": - "P. Schwaller"}], "matchScore": 181.37788}]} + "P. Schwaller"}], "matchScore": 181.10751}]} ' headers: @@ -127,27 +127,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:55 GMT + - Wed, 04 Sep 2024 22:52:24 GMT Via: - - 1.1 c4199de5b59b067ce72a20c751022aa8.cloudfront.net (CloudFront) + - 1.1 8eabaca8b591c36a72bf060174c30de0.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - 2ovc6Rq0WSyAYC2WUaxZyDiqpR_CryzYi2MJSFY-OOnyK4K8r7AB4Q== + - nLQc8pB58xiKWZbyxRSqVHTmwL1fmoST5SNR1sKkJlpbNV3_xT25pw== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcTvH8mvHcEmhw= + - dmiqVFg6vHcEMxg= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "684" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:55 GMT + - Wed, 04 Sep 2024 22:52:24 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 8a79a994-f3a3-4210-bb3f-e701fe35b69c + - 85e6e834-1518-4853-b0b4-604651cca2a6 status: code: 200 message: OK @@ -172,7 +172,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:34:55 GMT + - Wed, 04 Sep 2024 22:52:25 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_bad_dois.yaml b/tests/cassettes/test_bad_dois.yaml index 47c22c94..97f6f7e4 100644 --- a/tests/cassettes/test_bad_dois.yaml +++ b/tests/cassettes/test_bad_dois.yaml @@ -20,7 +20,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:24 GMT + - Wed, 04 Sep 2024 22:52:20 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -60,27 +60,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:27 GMT + - Wed, 04 Sep 2024 22:52:20 GMT Via: - - 1.1 4ce044af637284f41cd11c7043e8eaaa.cloudfront.net (CloudFront) + - 1.1 a0fdc0fd69b93d134cd1b726f68c77b4.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - Pj6xus9b-JNlnpYo1jLekS_3APln9I04bTZOxIT2DhpsFLgZma7cPg== + - XgnKCkJtzQZfS44zQE6e0QiqJMBkFlTsL2YH4gwBWHSpbFktgNNmrw== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Error from cloudfront x-amz-apigw-id: - - cdcPmEmUPHcESHw= + - dmipsGswPHcEWeQ= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "34" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:27 GMT + - Wed, 04 Sep 2024 22:52:20 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 07203930-ceca-432e-a916-d83cb392223a + - 226308d6-ed58-4623-9be5-2d87f80501e2 status: code: 404 message: Not Found diff --git a/tests/cassettes/test_bad_titles.yaml b/tests/cassettes/test_bad_titles.yaml index 26398200..578c9812 100644 --- a/tests/cassettes/test_bad_titles.yaml +++ b/tests/cassettes/test_bad_titles.yaml @@ -20,7 +20,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:05 GMT + - Wed, 04 Sep 2024 22:52:18 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -60,30 +60,89 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:07 GMT + - Wed, 04 Sep 2024 22:52:18 GMT Via: - - 1.1 b3169f8fae0104e39a0a9728b6537e08.cloudfront.net (CloudFront) + - 1.1 d12f243c0eac340525d6f4e735c01b64.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - njac2dCY2mYDBr9kpqMw07vocYKj5bqWdB3gjSS35BKQnXDemHVPdw== + - 3DWgEuMADlSeiLMhufObr-vygiJPeAWhCoFVgOUvmVrEwbLPbilLAQ== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Error from cloudfront x-amz-apigw-id: - - cdcMuFLwPHcEjHQ= + - dmipYEcqvHcEfEw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "34" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:07 GMT + - Wed, 04 Sep 2024 22:52:18 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - c9ee1896-0b67-4677-b6ed-dd3c7c274fd1 + - 41badda1-f747-4d97-bf9a-198de87c5eca status: code: 404 message: Not Found + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": + {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, + "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", + "title": "Effect of native oxide layers on copper thin-film tensile properties: + A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": + 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": + null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": + {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, + "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = + {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n + pages = {235306},\n title = {Effect of native oxide layers on copper thin-film + tensile properties: A reactive molecular dynamics study},\n volume = {118},\n + year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael + Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": + 208.01811}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1109" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:19 GMT + Via: + - 1.1 d12f243c0eac340525d6f4e735c01b64.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - 7POquAziX-3ilWrhAo6vJ7PubfSasYLaHfgIUsEfVoP5qSdVsBj6SQ== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipeH_lvHcETFA= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1109" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:19 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 992527e5-3812-4041-81c2-aeb61d4d6980 + status: + code: 200 + message: OK - request: body: null headers: {} @@ -92,9 +151,9 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":10275318,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":10323850,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office - of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide + of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"],"id":[{"id":"10.13039\/100000006","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide layers are likely to be present on metallic nano-structures due to either environmental exposure during use, or high temperature processing techniques such as annealing. It is well known that nano-structured metals have vastly @@ -186,7 +245,7 @@ interactions: Sci. Eng. A"},{"key":"2023062402360541600_c55","doi-asserted-by":"publisher","first-page":"057129","DOI":"10.1063\/1.4880241","volume":"4","year":"2014","journal-title":"AIP Adv."},{"key":"2023062402360541600_c56","doi-asserted-by":"publisher","first-page":"94","DOI":"10.1016\/j.susc.2014.10.017","volume":"633","year":"2015","journal-title":"Surf. Sci."},{"key":"2023062402360541600_c57","doi-asserted-by":"publisher","first-page":"710","DOI":"10.1016\/j.pmatsci.2010.04.001","volume":"55","year":"2010","journal-title":"Prog. - Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":49.39564,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":49.360317,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -200,11 +259,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "3928" + - "3946" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:08 GMT + - Wed, 04 Sep 2024 22:52:19 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -251,7 +310,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:34:09 GMT + - Wed, 04 Sep 2024 22:52:19 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -271,63 +330,4 @@ interactions: status: code: 200 message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": - {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, - "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", - "title": "Effect of native oxide layers on copper thin-film tensile properties: - A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": - 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": - null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": - {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, - "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = - {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n - pages = {235306},\n title = {Effect of native oxide layers on copper thin-film - tensile properties: A reactive molecular dynamics study},\n volume = {118},\n - year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael - Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": - 208.29527}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1109" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 18:34:23 GMT - Via: - - 1.1 b3169f8fae0104e39a0a9728b6537e08.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - DsETikbOnDFtYsX-08CvPI_WjGnGoJnPTVHA97MsDkpnNNTns7nDmA== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdcNCFJZvHcEnsA= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1109" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:23 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - c85f8443-7411-47f0-ba6f-2a023b2b2b7c - status: - code: 200 - message: OK version: 1 diff --git a/tests/cassettes/test_bulk_doi_search.yaml b/tests/cassettes/test_bulk_doi_search.yaml index 925b6fee..39e75290 100644 --- a/tests/cassettes/test_bulk_doi_search.yaml +++ b/tests/cassettes/test_bulk_doi_search.yaml @@ -3,54 +3,57 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2?mailto=test@example.com + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1063/1.4938384?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T04:17:17Z","timestamp":1687580237047},"reference-count":1,"publisher":"Springer - Science and Business Media LLC","issue":"1","license":[{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"},{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Reactions - Weekly"],"DOI":"10.1007\/s40278-023-41815-2","type":"journal-article","created":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T18:42:29Z","timestamp":1687545749000},"page":"145-145","source":"Crossref","is-referenced-by-count":0,"title":["Convalescent-anti-sars-cov-2-plasma\/immune-globulin"],"prefix":"10.1007","volume":"1962","member":"297","published-online":{"date-parts":[[2023,6,24]]},"reference":[{"key":"41815_CR1","doi-asserted-by":"crossref","unstructured":"Delgado-Fernandez - M, et al. Treatment of COVID-19 with convalescent plasma in patients with - humoral immunodeficiency - Three consecutive cases and review of the literature. - Enfermedades Infecciosas Y Microbiologia Clinica 40\n: 507-516, No. 9, Nov - 2022. Available from: URL: \nhttps:\/\/seimc.org\/","DOI":"10.1016\/j.eimce.2021.01.009"}],"container-title":["Reactions - Weekly"],"original-title":[],"language":"en","link":[{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/article\/10.1007\/s40278-023-41815-2\/fulltext.html","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T19:23:18Z","timestamp":1687548198000},"score":1,"resource":{"primary":{"URL":"https:\/\/link.springer.com\/10.1007\/s40278-023-41815-2"}},"subtitle":["Fever - and mild decrease in baseline oxygen saturation following off-label use: 3 - case reports"],"short-title":[],"issued":{"date-parts":[[2023,6,24]]},"references-count":1,"journal-issue":{"issue":"1","published-online":{"date-parts":[[2023,6]]}},"alternative-id":["41815"],"URL":"http:\/\/dx.doi.org\/10.1007\/s40278-023-41815-2","relation":{},"ISSN":["1179-2051"],"issn-type":[{"value":"1179-2051","type":"electronic"}],"subject":[],"published":{"date-parts":[[2023,6,24]]}}}' + '{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": + {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, + "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", + "title": "Effect of native oxide layers on copper thin-film tensile properties: + A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": + 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": + null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": + {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, + "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = + {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n + pages = {235306},\n title = {Effect of native oxide layers on copper thin-film + tensile properties: A reactive molecular dynamics study},\n volume = {118},\n + year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael + Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}]} + + ' headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close - Content-Encoding: - - gzip + - keep-alive Content-Length: - - "1163" + - "1072" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - Wed, 04 Sep 2024 22:52:15 GMT + Via: + - 1.1 ba51bb9649a17fe3932d945cc355c922.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - a9t0f3UmRFrgjuXEwQmQ2dSmPkVsBqV66Az3CQ2Jl23XehMZ66wiQg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmio4GqvvHcER1A= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1072" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:15 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 571358b9-6bea-49c4-ba87-d9897de4ad71 status: code: 200 message: OK @@ -58,91 +61,62 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1101%2F2024.04.01.587366?mailto=test@example.com + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1038/s42256-024-00832-8?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"institution":[{"name":"bioRxiv"}],"indexed":{"date-parts":[[2024,4,5]],"date-time":"2024-04-05T00:42:23Z","timestamp":1712277743507},"posted":{"date-parts":[[2024,4,1]]},"group-title":"Genomics","reference-count":50,"publisher":"Cold - Spring Harbor Laboratory","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"accepted":{"date-parts":[[2024,4,1]]},"abstract":"ABSTRACT<\/jats:title>Understanding - the effects of rare genetic variants remains challenging, both in coding and - non-coding regions. While multiplexed assays of variant effect (MAVEs) have - enabled scalable functional assessment of variants, established MAVEs are - limited by either exogenous expression of variants or constraints of genome - editing. Here, we introduce a pooled prime editing (PE) platform in haploid - human cells to scalably assay variants in their endogenous context. We first - optimized delivery of variants to HAP1 cells, defining optimal pegRNA designs - and establishing a co-selection strategy for improved efficiency. We characterize - our platform in the context of negative selection by testing over 7,500 pegRNAs - targetingSMARCB1<\/jats:italic>for editing activity and observing - depletion of highly active pegRNAs installing loss-of-function variants. We - next assess variants inMLH1<\/jats:italic>via 6-thioguanine selection, - assaying 65.3% of all possible SNVs in a 200-bp region spanning exon 10 and - distinguishing LoF variants with high accuracy. Lastly, we assay 362 non-codingMLH1<\/jats:italic>variants - across a 60 kb region in a single experiment, identifying pathogenic variants - acting via multiple mechanisms with high specificity. Our analyses detail - how filtering for highly active pegRNAs can facilitate both positive and negative - selection screens. Accordingly, our platform promises to enable highly scalable - functional assessment of human variants.<\/jats:p>","DOI":"10.1101\/2024.04.01.587366","type":"posted-content","created":{"date-parts":[[2024,4,2]],"date-time":"2024-04-02T02:05:17Z","timestamp":1712023517000},"source":"Crossref","is-referenced-by-count":0,"title":["High-throughput - screening of human genetic variants by pooled prime editing"],"prefix":"10.1101","author":[{"given":"Michael","family":"Herger","sequence":"first","affiliation":[]},{"given":"Christina - M.","family":"Kajba","sequence":"additional","affiliation":[]},{"given":"Megan","family":"Buckley","sequence":"additional","affiliation":[]},{"given":"Ana","family":"Cunha","sequence":"additional","affiliation":[]},{"given":"Molly","family":"Strom","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-7767-8608","authenticated-orcid":false,"given":"Gregory - M.","family":"Findlay","sequence":"additional","affiliation":[]}],"member":"246","reference":[{"key":"2024040415500652000_2024.04.01.587366v1.1","doi-asserted-by":"publisher","DOI":"10.1038\/gim.2015.30"},{"key":"2024040415500652000_2024.04.01.587366v1.2","doi-asserted-by":"crossref","first-page":"116","DOI":"10.1016\/j.cels.2017.11.003","article-title":"Quantitative - Missense Variant Effect Prediction Using Large-Scale Mutagenesis Data","volume":"6","year":"2018","journal-title":"Cell - Syst"},{"key":"2024040415500652000_2024.04.01.587366v1.3","doi-asserted-by":"publisher","DOI":"10.1126\/science.abi8207"},{"key":"2024040415500652000_2024.04.01.587366v1.4","doi-asserted-by":"publisher","DOI":"10.1016\/J.CELL.2018.12.015"},{"key":"2024040415500652000_2024.04.01.587366v1.5","doi-asserted-by":"crossref","first-page":"eabn8153","DOI":"10.1126\/science.abn8197","article-title":"The - landscape of tolerated genetic variation in humans and primates","volume":"380","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.6","doi-asserted-by":"crossref","first-page":"eadg7492","DOI":"10.1126\/science.adg7492","article-title":"Accurate - proteome-wide missense variant effect prediction with AlphaMissense","volume":"381","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.7","doi-asserted-by":"publisher","DOI":"10.1093\/nar\/gkv1222"},{"key":"2024040415500652000_2024.04.01.587366v1.8","doi-asserted-by":"crossref","first-page":"1381","DOI":"10.1038\/s41436-021-01172-3","article-title":"ACMG - SF v3.0 list for reporting of secondary findings in clinical exome and genome - sequencing: a policy statement of the American College of Medical Genetics - and Genomics (ACMG)","volume":"23","year":"2021","journal-title":"Genet. Med"},{"key":"2024040415500652000_2024.04.01.587366v1.9","doi-asserted-by":"publisher","DOI":"10.1038\/nprot.2016.135"},{"key":"2024040415500652000_2024.04.01.587366v1.10","doi-asserted-by":"publisher","DOI":"10.1093\/hmg\/ddab219"},{"key":"2024040415500652000_2024.04.01.587366v1.11","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-11526-w"},{"key":"2024040415500652000_2024.04.01.587366v1.12","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-022-02839-z"},{"key":"2024040415500652000_2024.04.01.587366v1.13","doi-asserted-by":"crossref","first-page":"2248","DOI":"10.1016\/j.ajhg.2021.11.001","article-title":"Closing - the gap: Systematic integration of multiplexed functional data resolves variants - of uncertain significance in BRCA1, TP53, and PTEN","volume":"108","year":"2021","journal-title":"Am. - J. Hum. Genet."},{"key":"2024040415500652000_2024.04.01.587366v1.14","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-018-0461-z"},{"key":"2024040415500652000_2024.04.01.587366v1.15","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.10.015"},{"key":"2024040415500652000_2024.04.01.587366v1.16","doi-asserted-by":"crossref","first-page":"7702","DOI":"10.1038\/s41467-023-43041-4","article-title":"Saturation - genome editing of DDX3X clarifies pathogenicity of germline and somatic variation","volume":"14","year":"2023","journal-title":"Nat. - Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.17","doi-asserted-by":"publisher","DOI":"10.1038\/nature13695"},{"key":"2024040415500652000_2024.04.01.587366v1.18","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.012"},{"key":"2024040415500652000_2024.04.01.587366v1.19","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.041"},{"key":"2024040415500652000_2024.04.01.587366v1.20","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-019-1711-4"},{"key":"2024040415500652000_2024.04.01.587366v1.21","doi-asserted-by":"crossref","first-page":"288","DOI":"10.1016\/j.ccell.2022.12.009","article-title":"Base - editing screens map mutations affecting interferon-\u03b3 signaling in cancer","volume":"41","year":"2023","journal-title":"Cancer - Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.22","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.09.018"},{"key":"2024040415500652000_2024.04.01.587366v1.23","doi-asserted-by":"crossref","first-page":"402","DOI":"10.1038\/s41587-021-01039-7","article-title":"Engineered - pegRNAs improve prime editing efficiency","volume":"40","year":"2022","journal-title":"Nat. - Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.24","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01201-1"},{"key":"2024040415500652000_2024.04.01.587366v1.25","doi-asserted-by":"publisher","DOI":"10.1016\/j.molcel.2023.11.021"},{"key":"2024040415500652000_2024.04.01.587366v1.26","doi-asserted-by":"publisher","DOI":"10.1038\/nature10348"},{"key":"2024040415500652000_2024.04.01.587366v1.27","doi-asserted-by":"publisher","DOI":"10.1126\/science.1247005"},{"key":"2024040415500652000_2024.04.01.587366v1.28","doi-asserted-by":"crossref","first-page":"1151","DOI":"10.1038\/s41587-022-01613-7","article-title":"Predicting - prime editing efficiency and product purity by deep learning","volume":"41","year":"2023","journal-title":"Nat. - Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.29","doi-asserted-by":"crossref","first-page":"2256","DOI":"10.1016\/j.cell.2023.03.034","article-title":"Prediction - of efficiencies for diverse prime editing systems in multiple cell types","volume":"186","year":"2023","journal-title":"Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.30","doi-asserted-by":"publisher","DOI":"10.1101\/2022.10.26.513842"},{"key":"2024040415500652000_2024.04.01.587366v1.31","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01172-3"},{"key":"2024040415500652000_2024.04.01.587366v1.32","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-020-0677-y"},{"key":"2024040415500652000_2024.04.01.587366v1.33","doi-asserted-by":"publisher","DOI":"10.1016\/j.tibtech.2018.07.017"},{"key":"2024040415500652000_2024.04.01.587366v1.34","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-020-20810-z"},{"key":"2024040415500652000_2024.04.01.587366v1.35","doi-asserted-by":"crossref","first-page":"5909","DOI":"10.1038\/s41467-022-33669-z","article-title":"Marker-free - co-selection for successive rounds of prime editing in human cells","volume":"13","year":"2022","journal-title":"Nat. - Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.36","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2013.12.001"},{"key":"2024040415500652000_2024.04.01.587366v1.37","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-018-02974-x"},{"key":"2024040415500652000_2024.04.01.587366v1.38","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-021-27838-9"},{"key":"2024040415500652000_2024.04.01.587366v1.39","doi-asserted-by":"publisher","DOI":"10.1126\/science.1225829"},{"key":"2024040415500652000_2024.04.01.587366v1.40","doi-asserted-by":"publisher","DOI":"10.3390\/cancers14153645"},{"key":"2024040415500652000_2024.04.01.587366v1.41","doi-asserted-by":"publisher","DOI":"10.1126\/science.aac7557"},{"key":"2024040415500652000_2024.04.01.587366v1.42","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-10849-y"},{"key":"2024040415500652000_2024.04.01.587366v1.43","doi-asserted-by":"publisher","DOI":"10.1038\/nbt.3437"},{"key":"2024040415500652000_2024.04.01.587366v1.44","doi-asserted-by":"publisher","DOI":"10.1136\/jmg.2007.056499"},{"key":"2024040415500652000_2024.04.01.587366v1.45","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.12.003"},{"key":"2024040415500652000_2024.04.01.587366v1.46","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-019-0032-3"},{"key":"2024040415500652000_2024.04.01.587366v1.47","doi-asserted-by":"publisher","DOI":"10.1038\/nmeth.3047"},{"key":"2024040415500652000_2024.04.01.587366v1.48","doi-asserted-by":"crossref","first-page":"96","DOI":"10.1089\/hgtb.2017.198","article-title":"Determination - of Lentiviral Infectious Titer by a Novel Droplet Digital PCR Method","volume":"29","year":"2018","journal-title":"Hum. - Gene Ther. Methods"},{"key":"2024040415500652000_2024.04.01.587366v1.49","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-020-02091-3"},{"key":"2024040415500652000_2024.04.01.587366v1.50","doi-asserted-by":"publisher","DOI":"10.1186\/s13073-021-00835-9"}],"container-title":[],"original-title":[],"link":[{"URL":"https:\/\/syndication.highwire.org\/content\/doi\/10.1101\/2024.04.01.587366","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,4,4]],"date-time":"2024-04-04T22:50:19Z","timestamp":1712271019000},"score":1,"resource":{"primary":{"URL":"http:\/\/biorxiv.org\/lookup\/doi\/10.1101\/2024.04.01.587366"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2024,4,1]]},"references-count":50,"URL":"http:\/\/dx.doi.org\/10.1101\/2024.04.01.587366","relation":{},"subject":[],"published":{"date-parts":[[2024,4,1]]},"subtype":"preprint"}}' + '{"paperId": "354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", "externalIds": + {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": + "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": + "38799228"}, "url": "https://www.semanticscholar.org/paper/354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", + "title": "Augmenting large language models with chemistry tools", "venue": + "Nat. Mac. Intell.", "year": 2023, "citationCount": 191, "influentialCitationCount": + 12, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.nature.com/articles/s42256-024-00832-8.pdf", + "status": "HYBRID"}, "publicationTypes": ["JournalArticle"], "publicationDate": + "2023-04-11", "journal": {"name": "Nature Machine Intelligence", "pages": + "525 - 535", "volume": "6"}, "citationStyles": {"bibtex": "@Article{Bran2023AugmentingLL,\n + author = {Andr\u00e9s M Bran and Sam Cox and Oliver Schilter and Carlo Baldassari + and Andrew D. White and P. Schwaller},\n booktitle = {Nat. Mac. Intell.},\n + journal = {Nature Machine Intelligence},\n pages = {525 - 535},\n title = + {Augmenting large language models with chemistry tools},\n volume = {6},\n + year = {2023}\n}\n"}, "authors": [{"authorId": "2216007369", "name": "Andr\u00e9s + M Bran"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", + "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, + {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", + "name": "P. Schwaller"}]} + + ' headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close - Content-Encoding: - - gzip + - keep-alive Content-Length: - - "3214" + - "1514" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - Wed, 04 Sep 2024 22:52:15 GMT + Via: + - 1.1 a2165b66922b78c24eb18ccc5d845334.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - s4Dypo5xhbfTsSPGutzMc-ygqcN6s5ihdMcooKRfFnzh05grOm71_Q== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmio4ER_vHcEF8w= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1514" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:15 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - a59c2318-659b-40dd-8692-67e3a4db231d status: code: 200 message: OK @@ -150,139 +124,62 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1063%2F1.4938384?mailto=test@example.com + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1023/a:1007154515475?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP - Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office - of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"short-container-title":[],"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide - layers are likely to be present on metallic nano-structures due to either - environmental exposure during use, or high temperature processing techniques - such as annealing. It is well known that nano-structured metals have vastly - different mechanical properties from bulk metals; however, difficulties in - modeling the transition between metallic and ionic bonding have prevented - the computational investigation of the effects of oxide surface layers. Newly - developed charge-optimized many body [Liang et al., Mater. Sci. Eng., R 74, - 255 (2013)] potentials are used to perform fully reactive molecular dynamics - simulations which elucidate the effects that metal-oxide layers have on the - mechanical properties of a copper thin-film. Simulated tensile tests are performed - on thin-films while using different strain-rates, temperatures, and oxide - thicknesses to evaluate changes in yield stress, modulus, and failure mechanisms. - Findings indicate that copper-thin film mechanical properties are strongly - affected by native oxide layers. The formed oxide layers have an amorphous - structure with lower Cu-O bond-densities than bulk CuO, and a mixture of Cu2O - and CuO charge character. It is found that oxidation will cause modifications - to the strain response of the elastic modulii, producing a stiffened modulii - at low temperatures (&lt;75\u2009K) and low strain values (&lt;5%), - and a softened modulii at higher temperatures. While under strain, structural - reorganization within the oxide layers facilitates brittle yielding through - nucleation of defects across the oxide\/metal interface. The oxide-free copper - thin-film yielding mechanism is found to be a tensile-axis reorientation and - grain creation. The oxide layers change the observed yielding mechanism, allowing - for the inner copper thin-film to sustain an FCC-to-BCC transition during - yielding. The mechanical properties are fit to a thermodynamic model based - on classical nucleation theory. The fit implies that the oxidation of the - films reduces the activation volume for yielding.<\/jats:p>","DOI":"10.1063\/1.4938384","type":"journal-article","created":{"date-parts":[[2015,12,22]],"date-time":"2015-12-22T00:59:18Z","timestamp":1450745958000},"update-policy":"http:\/\/dx.doi.org\/10.1063\/aip-crossmark-policy-page","source":"Crossref","is-referenced-by-count":8,"title":["Effect - of native oxide layers on copper thin-film tensile properties: A reactive - molecular dynamics study"],"prefix":"10.1063","volume":"118","author":[{"given":"Michael - D.","family":"Skarlinski","sequence":"first","affiliation":[{"name":"University - of Rochester 1 Materials Science Program, , Rochester, New York 14627, USA"}]},{"given":"David - J.","family":"Quesnel","sequence":"additional","affiliation":[{"name":"University - of Rochester 1 Materials Science Program, , Rochester, New York 14627, USA"},{"name":"University - of Rochester 2 Department of Mechanical Engineering, , Rochester, New York - 14627, USA"}]}],"member":"317","published-online":{"date-parts":[[2015,12,21]]},"reference":[{"key":"2023062402360541600_c1","doi-asserted-by":"publisher","first-page":"10973","DOI":"10.1021\/nn504883m","volume":"8","year":"2014","journal-title":"ACS - Nano"},{"key":"2023062402360541600_c2","volume-title":"Ultrathin Metal Transparent - Electrodes for the Optoelectronics Industry","year":"2013"},{"key":"2023062402360541600_c3","doi-asserted-by":"publisher","first-page":"2224","DOI":"10.1039\/b718768h","volume":"37","year":"2008","journal-title":"Chem. - Soc. Rev."},{"key":"2023062402360541600_c4","doi-asserted-by":"publisher","first-page":"3011","DOI":"10.1002\/adma.200501767","volume":"17","year":"2005","journal-title":"Adv. - Mater."},{"key":"2023062402360541600_c5","doi-asserted-by":"publisher","first-page":"4816","DOI":"10.1016\/j.actamat.2008.05.044","volume":"56","year":"2008","journal-title":"Acta - Mater."},{"key":"2023062402360541600_c6","doi-asserted-by":"publisher","first-page":"76","DOI":"10.1016\/j.commatsci.2014.02.014","volume":"87","year":"2014","journal-title":"Comput. - Mater. Sci."},{"key":"2023062402360541600_c7","doi-asserted-by":"publisher","first-page":"3032","DOI":"10.1016\/j.commatsci.2011.05.023","volume":"50","year":"2011","journal-title":"Comput. - Mater. Sci."},{"key":"2023062402360541600_c8","doi-asserted-by":"publisher","first-page":"319","DOI":"10.1016\/j.commatsci.2010.08.021","volume":"50","year":"2010","journal-title":"Comput. - Mater. Sci."},{"key":"2023062402360541600_c9","doi-asserted-by":"publisher","first-page":"140","DOI":"10.1016\/j.commatsci.2012.08.044","volume":"67","year":"2013","journal-title":"Comput. - Mater. Sci."},{"key":"2023062402360541600_c10","doi-asserted-by":"publisher","first-page":"093515","DOI":"10.1063\/1.3120916","volume":"105","year":"2009","journal-title":"J. - Appl. Phys."},{"key":"2023062402360541600_c11","doi-asserted-by":"publisher","first-page":"3151","DOI":"10.1021\/nl201233u","volume":"11","year":"2011","journal-title":"Nano - Lett."},{"key":"2023062402360541600_c12","doi-asserted-by":"publisher","first-page":"3048","DOI":"10.1021\/nl9015107","volume":"9","year":"2009","journal-title":"Nano - Lett."},{"key":"2023062402360541600_c13","doi-asserted-by":"publisher","first-page":"2318","DOI":"10.1016\/j.actamat.2008.01.027","volume":"56","year":"2008","journal-title":"Acta - Mater."},{"key":"2023062402360541600_c14","doi-asserted-by":"publisher","first-page":"241403","DOI":"10.1103\/PhysRevB.71.241403","volume":"71","year":"2005","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c15","doi-asserted-by":"publisher","first-page":"195429","DOI":"10.1103\/PhysRevB.77.195429","volume":"77","year":"2008","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c16","doi-asserted-by":"publisher","first-page":"3277","DOI":"10.1039\/c2jm13682a","volume":"22","year":"2012","journal-title":"J. - Mater. Chem."},{"key":"2023062402360541600_c17","doi-asserted-by":"publisher","first-page":"075413","DOI":"10.1103\/PhysRevB.70.075413","volume":"70","year":"2004","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c18","doi-asserted-by":"publisher","first-page":"163112","DOI":"10.1063\/1.2723654","volume":"90","year":"2007","journal-title":"Appl. - Phys. Lett."},{"key":"2023062402360541600_c19","doi-asserted-by":"publisher","first-page":"144","DOI":"10.1038\/ncomms1149","volume":"1","year":"2010","journal-title":"Nat. - Commun."},{"key":"2023062402360541600_c20","doi-asserted-by":"publisher","first-page":"085408","DOI":"10.1103\/PhysRevB.75.085408","volume":"75","year":"2007","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c21","doi-asserted-by":"publisher","first-page":"025502","DOI":"10.1103\/PhysRevLett.100.025502","volume":"100","year":"2008","journal-title":"Phys. - Rev. Lett."},{"key":"2023062402360541600_c22","doi-asserted-by":"publisher","first-page":"33","DOI":"10.1016\/j.ijplas.2013.04.002","volume":"52","year":"2014","journal-title":"Int. - J. Plast."},{"key":"2023062402360541600_c23","doi-asserted-by":"publisher","first-page":"035020","DOI":"10.1088\/2053-1591\/1\/3\/035020","volume":"1","year":"2014","journal-title":"Mater. - Res. Express"},{"key":"2023062402360541600_c24","doi-asserted-by":"publisher","first-page":"670","DOI":"10.1016\/j.jcrysgro.2005.11.111","volume":"289","year":"2006","journal-title":"J. - Cryst. Growth"},{"key":"2023062402360541600_c25","doi-asserted-by":"publisher","first-page":"62","DOI":"10.1016\/j.cplett.2004.10.005","volume":"399","year":"2004","journal-title":"Chem. - Phys. Lett."},{"key":"2023062402360541600_c26","doi-asserted-by":"publisher","first-page":"4040","DOI":"10.1016\/j.tsf.2007.12.159","volume":"516","year":"2008","journal-title":"Thin - Solid Films"},{"key":"2023062402360541600_c27","doi-asserted-by":"publisher","first-page":"085311","DOI":"10.1103\/PhysRevB.75.085311","volume":"75","year":"2007","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c28","doi-asserted-by":"publisher","first-page":"11996","DOI":"10.1103\/PhysRevB.50.11996","volume":"50","year":"1994","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c29","doi-asserted-by":"publisher","first-page":"4866","DOI":"10.1103\/PhysRevLett.82.4866","volume":"82","year":"1999","journal-title":"Phys. - Rev. Lett."},{"key":"2023062402360541600_c30","doi-asserted-by":"publisher","first-page":"9396","DOI":"10.1021\/jp004368u","volume":"105","year":"2001","journal-title":"J. - Phys. Chem. A."},{"key":"2023062402360541600_c31","doi-asserted-by":"publisher","first-page":"195408","DOI":"10.1103\/PhysRevB.78.195408","volume":"78","year":"2008","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c32","doi-asserted-by":"publisher","first-page":"123517","DOI":"10.1063\/1.2938022","volume":"103","year":"2008","journal-title":"J. - Appl. Phys."},{"key":"2023062402360541600_c33","doi-asserted-by":"publisher","first-page":"4073","DOI":"10.1080\/14786435.2011.598881","volume":"91","year":"2011","journal-title":"Philos. - Mag."},{"key":"2023062402360541600_c34","doi-asserted-by":"publisher","first-page":"051912","DOI":"10.1063\/1.4790181","volume":"102","year":"2013","journal-title":"Appl. - Phys. Lett."},{"key":"2023062402360541600_c35","doi-asserted-by":"publisher","first-page":"3959","DOI":"10.1038\/ncomms4959","volume":"5","year":"2014","journal-title":"Nat. - Commun."},{"key":"2023062402360541600_c36","doi-asserted-by":"publisher","first-page":"1","DOI":"10.1006\/jcph.1995.1039","volume":"117","year":"1995","journal-title":"J. - Comput. Phys."},{"key":"2023062402360541600_c37","doi-asserted-by":"publisher","first-page":"125308","DOI":"10.1103\/PhysRevB.84.125308","volume":"84","year":"2011","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c38","doi-asserted-by":"publisher","first-page":"255","DOI":"10.1016\/j.mser.2013.07.001","volume":"74","year":"2013","journal-title":"Mater. - Sci. Eng., R"},{"key":"2023062402360541600_c39","doi-asserted-by":"publisher","first-page":"6141","DOI":"10.1063\/1.468398","volume":"101","year":"1994","journal-title":"J. - Chem. Phys."},{"key":"2023062402360541600_c40","doi-asserted-by":"publisher","first-page":"98","DOI":"10.1103\/PhysRev.159.98","volume":"159","year":"1967","journal-title":"Phys. - Rev."},{"key":"2023062402360541600_c41","doi-asserted-by":"publisher","first-page":"109","DOI":"10.1146\/annurev-matsci-071312-121610","volume":"43","year":"2013","journal-title":"Annu. - Rev. Mater. Res."},{"key":"2023062402360541600_c42","doi-asserted-by":"publisher","first-page":"4177","DOI":"10.1063\/1.467468","volume":"101","year":"1994","journal-title":"J. - Chem. Phys."},{"key":"2023062402360541600_c43","first-page":"35","volume":"3","year":"1969","journal-title":"ESAIM-Math. - Model. Num."},{"key":"2023062402360541600_c44","doi-asserted-by":"publisher","first-page":"11085","DOI":"10.1103\/PhysRevB.58.11085","volume":"58","year":"1998","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c45","doi-asserted-by":"publisher","first-page":"045021","DOI":"10.1088\/0965-0393\/20\/4\/045021","volume":"20","year":"2012","journal-title":"Modell. - Simul. Mater. Sci. Eng."},{"key":"2023062402360541600_c46","doi-asserted-by":"publisher","first-page":"015012","DOI":"10.1088\/0965-0393\/18\/1\/015012","volume":"18","year":"2010","journal-title":"Modell. - Simul. Mater. Sci. Eng."},{"key":"2023062402360541600_c47","doi-asserted-by":"publisher","first-page":"605","DOI":"10.1007\/s11669-005-0005-8","volume":"26","year":"2005","journal-title":"J. - Phase Equilib. Diffus."},{"key":"2023062402360541600_c48","doi-asserted-by":"publisher","first-page":"386","DOI":"10.1016\/j.electacta.2015.03.221","volume":"179","year":"2015","journal-title":"Electrochim. - Acta"},{"key":"2023062402360541600_c49","doi-asserted-by":"publisher","first-page":"1876","DOI":"10.1016\/j.actamat.2007.12.043","volume":"56","year":"2008","journal-title":"Acta - Mater."},{"key":"2023062402360541600_c50","doi-asserted-by":"publisher","first-page":"2237","DOI":"10.1016\/S0020-7403(01)00043-1","volume":"43","year":"2001","journal-title":"Int. - J. Mech. Sci."},{"key":"2023062402360541600_c51","doi-asserted-by":"publisher","first-page":"1723","DOI":"10.1080\/14786430802206482","volume":"88","year":"2008","journal-title":"Philos. - Mag."},{"key":"2023062402360541600_c52","doi-asserted-by":"publisher","first-page":"224106","DOI":"10.1103\/PhysRevB.63.224106","volume":"63","year":"2001","journal-title":"Phys. - Rev. B"},{"key":"2023062402360541600_c53","doi-asserted-by":"publisher","first-page":"136","DOI":"10.1080\/09500830802684114","volume":"89","year":"2009","journal-title":"Philos. - Mag. Lett."},{"key":"2023062402360541600_c54","doi-asserted-by":"publisher","first-page":"238","DOI":"10.1016\/S0921-5093(02)00708-6","volume":"350","year":"2003","journal-title":"Mater. - Sci. Eng. A"},{"key":"2023062402360541600_c55","doi-asserted-by":"publisher","first-page":"057129","DOI":"10.1063\/1.4880241","volume":"4","year":"2014","journal-title":"AIP - Adv."},{"key":"2023062402360541600_c56","doi-asserted-by":"publisher","first-page":"94","DOI":"10.1016\/j.susc.2014.10.017","volume":"633","year":"2015","journal-title":"Surf. - Sci."},{"key":"2023062402360541600_c57","doi-asserted-by":"publisher","first-page":"710","DOI":"10.1016\/j.pmatsci.2010.04.001","volume":"55","year":"2010","journal-title":"Prog. - Mater. Sci."}],"container-title":["Journal of Applied Physics"],"original-title":[],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":1,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","relation":{},"ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"subject":[],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}}' + '{"paperId": "19807da5b11f3e641535cb72e465001b49b48ee5", "externalIds": + {"MAG": "1554322594", "DOI": "10.1023/A:1007154515475", "CorpusId": 22646521, + "PubMed": "11330823"}, "url": "https://www.semanticscholar.org/paper/19807da5b11f3e641535cb72e465001b49b48ee5", + "title": "An essential role of active site arginine residue in iodide binding + and histidine residue in electron transfer for iodide oxidation by horseradish + peroxidase", "venue": "Molecular and Cellular Biochemistry", "year": 2001, + "citationCount": 7, "influentialCitationCount": 0, "isOpenAccess": false, + "openAccessPdf": null, "publicationTypes": ["JournalArticle", "Study"], "publicationDate": + "2001-02-01", "journal": {"name": "Molecular and Cellular Biochemistry", "pages": + "1-11", "volume": "218"}, "citationStyles": {"bibtex": "@Article{Adak2001AnER,\n + author = {S. Adak and D. Bandyopadhyay and U. Bandyopadhyay and R. Banerjee},\n + booktitle = {Molecular and Cellular Biochemistry},\n journal = {Molecular + and Cellular Biochemistry},\n pages = {1-11},\n title = {An essential role + of active site arginine residue in iodide binding and histidine residue in + electron transfer for iodide oxidation by horseradish peroxidase},\n volume + = {218},\n year = {2001}\n}\n"}, "authors": [{"authorId": "1940081", "name": + "S. Adak"}, {"authorId": "1701389", "name": "D. Bandyopadhyay"}, {"authorId": + "5343877", "name": "U. Bandyopadhyay"}, {"authorId": "32656528", "name": "R. + Banerjee"}]} + + ' headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close - Content-Encoding: - - gzip + - keep-alive Content-Length: - - "3893" + - "1446" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - Wed, 04 Sep 2024 22:52:15 GMT + Via: + - 1.1 bd414f5f75d6893558dff609c5ff1fe6.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - _jZ8-GIU76NSYYUGEIT8FDXOc5OWHG9G1J5AbBdg3QBC72VTaraspA== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmio5GjPvHcEsfA= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1446" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:15 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - edfb27e0-9e20-48bc-a89e-a1ce7920889b status: code: 200 message: OK @@ -290,20 +187,25 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1101/2024.04.01.587366?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": - {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "url": "https://www.semanticscholar.org/paper/e0d2719e49ad216f98ed640864cdacd1c20f53e6", - "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions - weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, - "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], - "publicationDate": "2023-06-01", "journal": {"name": "Reactions Weekly", "pages": - "145 - 145", "volume": "1962"}, "citationStyles": {"bibtex": "@Article{None,\n - booktitle = {Reactions weekly},\n journal = {Reactions Weekly},\n pages = - {145 - 145},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n - volume = {1962},\n year = {2023}\n}\n"}, "authors": []} + '{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": + {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", + "title": "High-throughput screening of human genetic variants by pooled prime + editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": + 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", + "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n + author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana + Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n + journal = {bioRxiv},\n title = {High-throughput screening of human genetic + variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": + "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": + "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, + {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", + "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}]} ' headers: @@ -312,31 +214,31 @@ interactions: Connection: - keep-alive Content-Length: - - "837" + - "1325" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Via: - - 1.1 c4199de5b59b067ce72a20c751022aa8.cloudfront.net (CloudFront) + - 1.1 bc4ea6bb0c34991c678d2ee30fe9418e.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - vkRAQr-Zq5vWLWrjXiVx7z4K3te3TJM0InHdjIZb-VghBQGsU9rv7Q== + - n0uSlYJ9h92Y93gE6VNxU8axBoW_4tqqmpYlDj9Q_pzxUAMM2ZQnfg== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdeu2GMevHcEkXg= + - dmio5EEcPHcENJw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - - "837" + - "1325" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 7a93e795-7d0a-41e9-ad54-d3ac263a5ef2 + - e3be8137-17a5-4afa-bf19-78ebbabaa664 status: code: 200 message: OK @@ -344,29 +246,81 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1023/a:1007154515475?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.48550/arxiv.2312.07559?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"paperId": "19807da5b11f3e641535cb72e465001b49b48ee5", "externalIds": - {"MAG": "1554322594", "DOI": "10.1023/A:1007154515475", "CorpusId": 22646521, - "PubMed": "11330823"}, "url": "https://www.semanticscholar.org/paper/19807da5b11f3e641535cb72e465001b49b48ee5", - "title": "An essential role of active site arginine residue in iodide binding - and histidine residue in electron transfer for iodide oxidation by horseradish - peroxidase", "venue": "Molecular and Cellular Biochemistry", "year": 2001, - "citationCount": 7, "influentialCitationCount": 0, "isOpenAccess": false, - "openAccessPdf": null, "publicationTypes": ["JournalArticle", "Study"], "publicationDate": - "2001-02-01", "journal": {"name": "Molecular and Cellular Biochemistry", "pages": - "1-11", "volume": "218"}, "citationStyles": {"bibtex": "@Article{Adak2001AnER,\n - author = {S. Adak and D. Bandyopadhyay and U. Bandyopadhyay and R. Banerjee},\n - booktitle = {Molecular and Cellular Biochemistry},\n journal = {Molecular - and Cellular Biochemistry},\n pages = {1-11},\n title = {An essential role - of active site arginine residue in iodide binding and histidine residue in - electron transfer for iodide oxidation by horseradish peroxidase},\n volume - = {218},\n year = {2001}\n}\n"}, "authors": [{"authorId": "1940081", "name": - "S. Adak"}, {"authorId": "1701389", "name": "D. Bandyopadhyay"}, {"authorId": - "5343877", "name": "U. Bandyopadhyay"}, {"authorId": "32656528", "name": "R. - Banerjee"}]} + '{"paperId": "7e55d8701785818776323b4147cb13354c820469", "externalIds": + {"ArXiv": "2312.07559", "DBLP": "journals/corr/abs-2312-07559", "DOI": "10.48550/arXiv.2312.07559", + "CorpusId": 266191420}, "url": "https://www.semanticscholar.org/paper/7e55d8701785818776323b4147cb13354c820469", + "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", + "venue": "arXiv.org", "year": 2023, "citationCount": 23, "influentialCitationCount": + 1, "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], + "publicationDate": "2023-12-08", "journal": {"name": "ArXiv", "volume": "abs/2312.07559"}, + "citationStyles": {"bibtex": "@Article{L''ala2023PaperQARG,\n author = {Jakub + L''ala and Odhran O''Donoghue and Aleksandar Shtedritski and Sam Cox and Samuel + G. Rodriques and Andrew D. White},\n booktitle = {arXiv.org},\n journal = + {ArXiv},\n title = {PaperQA: Retrieval-Augmented Generative Agent for Scientific + Research},\n volume = {abs/2312.07559},\n year = {2023}\n}\n"}, "authors": + [{"authorId": "2219926382", "name": "Jakub L''ala"}, {"authorId": "2258961056", + "name": "Odhran O''Donoghue"}, {"authorId": "2258961451", "name": "Aleksandar + Shtedritski"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": + "2258964497", "name": "Samuel G. Rodriques"}, {"authorId": "2273941271", "name": + "Andrew D. White"}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1349" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:15 GMT + Via: + - 1.1 cc58556a6e846289f4d3105969536e4c.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - o6spqsPA3fdC2BrgNChKLUaoK22cXaikXe3B9M67Nx7U5O6Dn5PcgA== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmio5FP7vHcEVVg= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1349" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:15 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - a017d70b-b4bb-4e96-b474-1069c5d494b3 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": + {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "url": "https://www.semanticscholar.org/paper/e0d2719e49ad216f98ed640864cdacd1c20f53e6", + "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions + weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, + "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], + "publicationDate": "2023-06-01", "journal": {"name": "Reactions Weekly", "pages": + "145 - 145", "volume": "1962"}, "citationStyles": {"bibtex": "@Article{None,\n + booktitle = {Reactions weekly},\n journal = {Reactions Weekly},\n pages = + {145 - 145},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n + volume = {1962},\n year = {2023}\n}\n"}, "authors": []} ' headers: @@ -375,31 +329,31 @@ interactions: Connection: - keep-alive Content-Length: - - "1446" + - "837" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Via: - - 1.1 b3169f8fae0104e39a0a9728b6537e08.cloudfront.net (CloudFront) + - 1.1 c3d007e42510cc2bd48d2a205774e488.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - X5UPx1oFndrqHMUFoLp-g2sI75H3GXmEpcX2mb87BRgu-Xlw8HW_Rg== + - 1RAeHC7HhZJE9DtiTJoT4b7mYz0Bvcz_VoAECnMgTlXklPykWxc8Ug== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdeu2HrFvHcEpQw= + - dmio5GTsPHcEqgw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - - "1446" + - "837" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 78db29ca-52b3-422e-80e6-21c6d863691b + - cd9b85cf-4664-46ba-a63c-2688e86aca39 status: code: 200 message: OK @@ -407,14 +361,105 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475?mailto=test@example.com + uri: https://api.crossref.org/works/10.1063%2F1.4938384?mailto=test@example.com response: body: string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,1,21]],"date-time":"2024-01-21T17:53:48Z","timestamp":1705859628791},"reference-count":0,"publisher":"Springer - Science and Business Media LLC","issue":"1\/2","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"published-print":{"date-parts":[[2001]]},"DOI":"10.1023\/a:1007154515475","type":"journal-article","created":{"date-parts":[[2002,12,22]],"date-time":"2002-12-22T09:07:15Z","timestamp":1040548035000},"page":"1-11","source":"Crossref","is-referenced-by-count":6,"title":[],"prefix":"10.1007","volume":"218","author":[{"given":"Subrata","family":"Adak","sequence":"first","affiliation":[]},{"given":"Debashis","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Uday","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Ranajit - K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"297","container-title":["Molecular - and Cellular Biochemistry"],"original-title":[],"deposited":{"date-parts":[[2012,12,27]],"date-time":"2012-12-27T23:10:34Z","timestamp":1356649834000},"score":1,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1023\/A:1007154515475"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2001]]},"references-count":0,"journal-issue":{"issue":"1\/2"},"alternative-id":["271450"],"URL":"http:\/\/dx.doi.org\/10.1023\/a:1007154515475","relation":{},"ISSN":["0300-8177"],"issn-type":[{"value":"0300-8177","type":"print"}],"subject":[],"published":{"date-parts":[[2001]]}}}' + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP + Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office + of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"],"id":[{"id":"10.13039\/100000006","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"short-container-title":[],"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide + layers are likely to be present on metallic nano-structures due to either + environmental exposure during use, or high temperature processing techniques + such as annealing. It is well known that nano-structured metals have vastly + different mechanical properties from bulk metals; however, difficulties in + modeling the transition between metallic and ionic bonding have prevented + the computational investigation of the effects of oxide surface layers. Newly + developed charge-optimized many body [Liang et al., Mater. Sci. Eng., R 74, + 255 (2013)] potentials are used to perform fully reactive molecular dynamics + simulations which elucidate the effects that metal-oxide layers have on the + mechanical properties of a copper thin-film. Simulated tensile tests are performed + on thin-films while using different strain-rates, temperatures, and oxide + thicknesses to evaluate changes in yield stress, modulus, and failure mechanisms. + Findings indicate that copper-thin film mechanical properties are strongly + affected by native oxide layers. The formed oxide layers have an amorphous + structure with lower Cu-O bond-densities than bulk CuO, and a mixture of Cu2O + and CuO charge character. It is found that oxidation will cause modifications + to the strain response of the elastic modulii, producing a stiffened modulii + at low temperatures (&lt;75\u2009K) and low strain values (&lt;5%), + and a softened modulii at higher temperatures. While under strain, structural + reorganization within the oxide layers facilitates brittle yielding through + nucleation of defects across the oxide\/metal interface. The oxide-free copper + thin-film yielding mechanism is found to be a tensile-axis reorientation and + grain creation. The oxide layers change the observed yielding mechanism, allowing + for the inner copper thin-film to sustain an FCC-to-BCC transition during + yielding. The mechanical properties are fit to a thermodynamic model based + on classical nucleation theory. The fit implies that the oxidation of the + films reduces the activation volume for yielding.<\/jats:p>","DOI":"10.1063\/1.4938384","type":"journal-article","created":{"date-parts":[[2015,12,22]],"date-time":"2015-12-22T00:59:18Z","timestamp":1450745958000},"update-policy":"http:\/\/dx.doi.org\/10.1063\/aip-crossmark-policy-page","source":"Crossref","is-referenced-by-count":8,"title":["Effect + of native oxide layers on copper thin-film tensile properties: A reactive + molecular dynamics study"],"prefix":"10.1063","volume":"118","author":[{"given":"Michael + D.","family":"Skarlinski","sequence":"first","affiliation":[{"name":"University + of Rochester 1 Materials Science Program, , Rochester, New York 14627, USA"}]},{"given":"David + J.","family":"Quesnel","sequence":"additional","affiliation":[{"name":"University + of Rochester 1 Materials Science Program, , Rochester, New York 14627, USA"},{"name":"University + of Rochester 2 Department of Mechanical Engineering, , Rochester, New York + 14627, USA"}]}],"member":"317","published-online":{"date-parts":[[2015,12,21]]},"reference":[{"key":"2023062402360541600_c1","doi-asserted-by":"publisher","first-page":"10973","DOI":"10.1021\/nn504883m","volume":"8","year":"2014","journal-title":"ACS + Nano"},{"key":"2023062402360541600_c2","volume-title":"Ultrathin Metal Transparent + Electrodes for the Optoelectronics Industry","year":"2013"},{"key":"2023062402360541600_c3","doi-asserted-by":"publisher","first-page":"2224","DOI":"10.1039\/b718768h","volume":"37","year":"2008","journal-title":"Chem. + Soc. Rev."},{"key":"2023062402360541600_c4","doi-asserted-by":"publisher","first-page":"3011","DOI":"10.1002\/adma.200501767","volume":"17","year":"2005","journal-title":"Adv. + Mater."},{"key":"2023062402360541600_c5","doi-asserted-by":"publisher","first-page":"4816","DOI":"10.1016\/j.actamat.2008.05.044","volume":"56","year":"2008","journal-title":"Acta + Mater."},{"key":"2023062402360541600_c6","doi-asserted-by":"publisher","first-page":"76","DOI":"10.1016\/j.commatsci.2014.02.014","volume":"87","year":"2014","journal-title":"Comput. + Mater. Sci."},{"key":"2023062402360541600_c7","doi-asserted-by":"publisher","first-page":"3032","DOI":"10.1016\/j.commatsci.2011.05.023","volume":"50","year":"2011","journal-title":"Comput. + Mater. Sci."},{"key":"2023062402360541600_c8","doi-asserted-by":"publisher","first-page":"319","DOI":"10.1016\/j.commatsci.2010.08.021","volume":"50","year":"2010","journal-title":"Comput. + Mater. Sci."},{"key":"2023062402360541600_c9","doi-asserted-by":"publisher","first-page":"140","DOI":"10.1016\/j.commatsci.2012.08.044","volume":"67","year":"2013","journal-title":"Comput. + Mater. Sci."},{"key":"2023062402360541600_c10","doi-asserted-by":"publisher","first-page":"093515","DOI":"10.1063\/1.3120916","volume":"105","year":"2009","journal-title":"J. + Appl. Phys."},{"key":"2023062402360541600_c11","doi-asserted-by":"publisher","first-page":"3151","DOI":"10.1021\/nl201233u","volume":"11","year":"2011","journal-title":"Nano + Lett."},{"key":"2023062402360541600_c12","doi-asserted-by":"publisher","first-page":"3048","DOI":"10.1021\/nl9015107","volume":"9","year":"2009","journal-title":"Nano + Lett."},{"key":"2023062402360541600_c13","doi-asserted-by":"publisher","first-page":"2318","DOI":"10.1016\/j.actamat.2008.01.027","volume":"56","year":"2008","journal-title":"Acta + Mater."},{"key":"2023062402360541600_c14","doi-asserted-by":"publisher","first-page":"241403","DOI":"10.1103\/PhysRevB.71.241403","volume":"71","year":"2005","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c15","doi-asserted-by":"publisher","first-page":"195429","DOI":"10.1103\/PhysRevB.77.195429","volume":"77","year":"2008","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c16","doi-asserted-by":"publisher","first-page":"3277","DOI":"10.1039\/c2jm13682a","volume":"22","year":"2012","journal-title":"J. + Mater. Chem."},{"key":"2023062402360541600_c17","doi-asserted-by":"publisher","first-page":"075413","DOI":"10.1103\/PhysRevB.70.075413","volume":"70","year":"2004","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c18","doi-asserted-by":"publisher","first-page":"163112","DOI":"10.1063\/1.2723654","volume":"90","year":"2007","journal-title":"Appl. + Phys. Lett."},{"key":"2023062402360541600_c19","doi-asserted-by":"publisher","first-page":"144","DOI":"10.1038\/ncomms1149","volume":"1","year":"2010","journal-title":"Nat. + Commun."},{"key":"2023062402360541600_c20","doi-asserted-by":"publisher","first-page":"085408","DOI":"10.1103\/PhysRevB.75.085408","volume":"75","year":"2007","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c21","doi-asserted-by":"publisher","first-page":"025502","DOI":"10.1103\/PhysRevLett.100.025502","volume":"100","year":"2008","journal-title":"Phys. + Rev. Lett."},{"key":"2023062402360541600_c22","doi-asserted-by":"publisher","first-page":"33","DOI":"10.1016\/j.ijplas.2013.04.002","volume":"52","year":"2014","journal-title":"Int. + J. Plast."},{"key":"2023062402360541600_c23","doi-asserted-by":"publisher","first-page":"035020","DOI":"10.1088\/2053-1591\/1\/3\/035020","volume":"1","year":"2014","journal-title":"Mater. + Res. Express"},{"key":"2023062402360541600_c24","doi-asserted-by":"publisher","first-page":"670","DOI":"10.1016\/j.jcrysgro.2005.11.111","volume":"289","year":"2006","journal-title":"J. + Cryst. Growth"},{"key":"2023062402360541600_c25","doi-asserted-by":"publisher","first-page":"62","DOI":"10.1016\/j.cplett.2004.10.005","volume":"399","year":"2004","journal-title":"Chem. + Phys. Lett."},{"key":"2023062402360541600_c26","doi-asserted-by":"publisher","first-page":"4040","DOI":"10.1016\/j.tsf.2007.12.159","volume":"516","year":"2008","journal-title":"Thin + Solid Films"},{"key":"2023062402360541600_c27","doi-asserted-by":"publisher","first-page":"085311","DOI":"10.1103\/PhysRevB.75.085311","volume":"75","year":"2007","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c28","doi-asserted-by":"publisher","first-page":"11996","DOI":"10.1103\/PhysRevB.50.11996","volume":"50","year":"1994","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c29","doi-asserted-by":"publisher","first-page":"4866","DOI":"10.1103\/PhysRevLett.82.4866","volume":"82","year":"1999","journal-title":"Phys. + Rev. Lett."},{"key":"2023062402360541600_c30","doi-asserted-by":"publisher","first-page":"9396","DOI":"10.1021\/jp004368u","volume":"105","year":"2001","journal-title":"J. + Phys. Chem. A."},{"key":"2023062402360541600_c31","doi-asserted-by":"publisher","first-page":"195408","DOI":"10.1103\/PhysRevB.78.195408","volume":"78","year":"2008","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c32","doi-asserted-by":"publisher","first-page":"123517","DOI":"10.1063\/1.2938022","volume":"103","year":"2008","journal-title":"J. + Appl. Phys."},{"key":"2023062402360541600_c33","doi-asserted-by":"publisher","first-page":"4073","DOI":"10.1080\/14786435.2011.598881","volume":"91","year":"2011","journal-title":"Philos. + Mag."},{"key":"2023062402360541600_c34","doi-asserted-by":"publisher","first-page":"051912","DOI":"10.1063\/1.4790181","volume":"102","year":"2013","journal-title":"Appl. + Phys. Lett."},{"key":"2023062402360541600_c35","doi-asserted-by":"publisher","first-page":"3959","DOI":"10.1038\/ncomms4959","volume":"5","year":"2014","journal-title":"Nat. + Commun."},{"key":"2023062402360541600_c36","doi-asserted-by":"publisher","first-page":"1","DOI":"10.1006\/jcph.1995.1039","volume":"117","year":"1995","journal-title":"J. + Comput. Phys."},{"key":"2023062402360541600_c37","doi-asserted-by":"publisher","first-page":"125308","DOI":"10.1103\/PhysRevB.84.125308","volume":"84","year":"2011","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c38","doi-asserted-by":"publisher","first-page":"255","DOI":"10.1016\/j.mser.2013.07.001","volume":"74","year":"2013","journal-title":"Mater. + Sci. Eng., R"},{"key":"2023062402360541600_c39","doi-asserted-by":"publisher","first-page":"6141","DOI":"10.1063\/1.468398","volume":"101","year":"1994","journal-title":"J. + Chem. Phys."},{"key":"2023062402360541600_c40","doi-asserted-by":"publisher","first-page":"98","DOI":"10.1103\/PhysRev.159.98","volume":"159","year":"1967","journal-title":"Phys. + Rev."},{"key":"2023062402360541600_c41","doi-asserted-by":"publisher","first-page":"109","DOI":"10.1146\/annurev-matsci-071312-121610","volume":"43","year":"2013","journal-title":"Annu. + Rev. Mater. Res."},{"key":"2023062402360541600_c42","doi-asserted-by":"publisher","first-page":"4177","DOI":"10.1063\/1.467468","volume":"101","year":"1994","journal-title":"J. + Chem. Phys."},{"key":"2023062402360541600_c43","first-page":"35","volume":"3","year":"1969","journal-title":"ESAIM-Math. + Model. Num."},{"key":"2023062402360541600_c44","doi-asserted-by":"publisher","first-page":"11085","DOI":"10.1103\/PhysRevB.58.11085","volume":"58","year":"1998","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c45","doi-asserted-by":"publisher","first-page":"045021","DOI":"10.1088\/0965-0393\/20\/4\/045021","volume":"20","year":"2012","journal-title":"Modell. + Simul. Mater. Sci. Eng."},{"key":"2023062402360541600_c46","doi-asserted-by":"publisher","first-page":"015012","DOI":"10.1088\/0965-0393\/18\/1\/015012","volume":"18","year":"2010","journal-title":"Modell. + Simul. Mater. Sci. Eng."},{"key":"2023062402360541600_c47","doi-asserted-by":"publisher","first-page":"605","DOI":"10.1007\/s11669-005-0005-8","volume":"26","year":"2005","journal-title":"J. + Phase Equilib. Diffus."},{"key":"2023062402360541600_c48","doi-asserted-by":"publisher","first-page":"386","DOI":"10.1016\/j.electacta.2015.03.221","volume":"179","year":"2015","journal-title":"Electrochim. + Acta"},{"key":"2023062402360541600_c49","doi-asserted-by":"publisher","first-page":"1876","DOI":"10.1016\/j.actamat.2007.12.043","volume":"56","year":"2008","journal-title":"Acta + Mater."},{"key":"2023062402360541600_c50","doi-asserted-by":"publisher","first-page":"2237","DOI":"10.1016\/S0020-7403(01)00043-1","volume":"43","year":"2001","journal-title":"Int. + J. Mech. Sci."},{"key":"2023062402360541600_c51","doi-asserted-by":"publisher","first-page":"1723","DOI":"10.1080\/14786430802206482","volume":"88","year":"2008","journal-title":"Philos. + Mag."},{"key":"2023062402360541600_c52","doi-asserted-by":"publisher","first-page":"224106","DOI":"10.1103\/PhysRevB.63.224106","volume":"63","year":"2001","journal-title":"Phys. + Rev. B"},{"key":"2023062402360541600_c53","doi-asserted-by":"publisher","first-page":"136","DOI":"10.1080\/09500830802684114","volume":"89","year":"2009","journal-title":"Philos. + Mag. Lett."},{"key":"2023062402360541600_c54","doi-asserted-by":"publisher","first-page":"238","DOI":"10.1016\/S0921-5093(02)00708-6","volume":"350","year":"2003","journal-title":"Mater. + Sci. Eng. A"},{"key":"2023062402360541600_c55","doi-asserted-by":"publisher","first-page":"057129","DOI":"10.1063\/1.4880241","volume":"4","year":"2014","journal-title":"AIP + Adv."},{"key":"2023062402360541600_c56","doi-asserted-by":"publisher","first-page":"94","DOI":"10.1016\/j.susc.2014.10.017","volume":"633","year":"2015","journal-title":"Surf. + Sci."},{"key":"2023062402360541600_c57","doi-asserted-by":"publisher","first-page":"710","DOI":"10.1016\/j.pmatsci.2010.04.001","volume":"55","year":"2010","journal-title":"Prog. + Mater. Sci."}],"container-title":["Journal of Applied Physics"],"original-title":[],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":1,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","relation":{},"ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"subject":[],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -428,11 +473,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "762" + - "3912" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -452,47 +497,6 @@ interactions: status: code: 200 message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.crossref.org/works/10.48550%2Farxiv.2312.07559?mailto=test@example.com - response: - body: - string: Resource not found. - headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control - Access-Control-Allow-Origin: - - "*" - Access-Control-Expose-Headers: - - Link - Connection: - - close - Content-Type: - - text/plain - Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Server: - - Jetty(9.4.40.v20210413) - Transfer-Encoding: - - chunked - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" - status: - code: 404 - message: Not Found - request: body: null headers: {} @@ -501,10 +505,10 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,8,9]],"date-time":"2024-08-09T17:10:06Z","timestamp":1723223406992},"reference-count":103,"publisher":"Springer + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,9,4]],"date-time":"2024-09-04T17:39:08Z","timestamp":1725471548702},"reference-count":103,"publisher":"Springer Science and Business Media LLC","issue":"5","license":[{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"},{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"}],"funder":[{"DOI":"10.13039\/501100001711","name":"Schweizerischer - Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"]},{"DOI":"10.13039\/100000001","name":"National - Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat + Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"],"id":[{"id":"10.13039\/501100001711","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100000001","name":"National + Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"],"id":[{"id":"10.13039\/100000001","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat Mach Intell"],"abstract":"Abstract<\/jats:title>Large language models (LLMs) have shown strong performance in tasks across domains but struggle with chemistry-related problems. These models also lack access @@ -518,7 +522,7 @@ interactions: both LLM and expert assessments, demonstrates ChemCrow\u2019s effectiveness in automating a diverse set of chemical tasks. Our work not only aids expert chemists and lowers barriers for non-experts but also fosters scientific advancement - by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":12,"title":["Augmenting + by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":17,"title":["Augmenting large language models with chemistry tools"],"prefix":"10.1038","volume":"6","author":[{"given":"Andres","family":"M. Bran","sequence":"first","affiliation":[]},{"given":"Sam","family":"Cox","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-0310-0851","authenticated-orcid":false,"given":"Oliver","family":"Schilter","sequence":"additional","affiliation":[]},{"given":"Carlo","family":"Baldassari","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-6647-3965","authenticated-orcid":false,"given":"Andrew D.","family":"White","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-3046-6576","authenticated-orcid":false,"given":"Philippe","family":"Schwaller","sequence":"additional","affiliation":[]}],"member":"297","published-online":{"date-parts":[[2024,5,8]]},"reference":[{"key":"832_CR1","unstructured":"Devlin, @@ -828,11 +832,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "10492" + - "10521" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -856,121 +860,48 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1101/2024.04.01.587366?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": - {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", - "title": "High-throughput screening of human genetic variants by pooled prime - editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": - 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", - "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", - "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n - author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana - Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n - journal = {bioRxiv},\n title = {High-throughput screening of human genetic - variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": - "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": - "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, - {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", - "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1325" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Via: - - 1.1 ce05e2e2ef149c875905ee7ff636fb28.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - d8Cjr7OBXCAfHiQtpRekALCtsurVudBbRM8R2uxLmvDVjgDGbczsKw== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdeu3EH7vHcEPkg= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1325" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - d5d2965d-91e3-4722-8ec0-4efe859a1a3f - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1038/s42256-024-00832-8?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475?mailto=test@example.com response: body: string: - '{"paperId": "354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", "externalIds": - {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": - "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": - "38799228"}, "url": "https://www.semanticscholar.org/paper/354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", - "title": "Augmenting large language models with chemistry tools", "venue": - "Nat. Mac. Intell.", "year": 2023, "citationCount": 187, "influentialCitationCount": - 12, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.nature.com/articles/s42256-024-00832-8.pdf", - "status": "HYBRID"}, "publicationTypes": ["JournalArticle"], "publicationDate": - "2023-04-11", "journal": {"name": "Nature Machine Intelligence", "pages": - "525 - 535", "volume": "6"}, "citationStyles": {"bibtex": "@Article{Bran2023AugmentingLL,\n - author = {Andr\u00e9s M Bran and Sam Cox and Oliver Schilter and Carlo Baldassari - and Andrew D. White and P. Schwaller},\n booktitle = {Nat. Mac. Intell.},\n - journal = {Nature Machine Intelligence},\n pages = {525 - 535},\n title = - {Augmenting large language models with chemistry tools},\n volume = {6},\n - year = {2023}\n}\n"}, "authors": [{"authorId": "2216007369", "name": "Andr\u00e9s - M Bran"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", - "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, - {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", - "name": "P. Schwaller"}]} - - ' + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,1,21]],"date-time":"2024-01-21T17:53:48Z","timestamp":1705859628791},"reference-count":0,"publisher":"Springer + Science and Business Media LLC","issue":"1\/2","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"published-print":{"date-parts":[[2001]]},"DOI":"10.1023\/a:1007154515475","type":"journal-article","created":{"date-parts":[[2002,12,22]],"date-time":"2002-12-22T09:07:15Z","timestamp":1040548035000},"page":"1-11","source":"Crossref","is-referenced-by-count":6,"title":[],"prefix":"10.1007","volume":"218","author":[{"given":"Subrata","family":"Adak","sequence":"first","affiliation":[]},{"given":"Debashis","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Uday","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Ranajit + K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"297","container-title":["Molecular + and Cellular Biochemistry"],"original-title":[],"deposited":{"date-parts":[[2012,12,27]],"date-time":"2012-12-27T23:10:34Z","timestamp":1356649834000},"score":1,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1023\/A:1007154515475"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2001]]},"references-count":0,"journal-issue":{"issue":"1\/2"},"alternative-id":["271450"],"URL":"http:\/\/dx.doi.org\/10.1023\/a:1007154515475","relation":{},"ISSN":["0300-8177"],"issn-type":[{"value":"0300-8177","type":"print"}],"subject":[],"published":{"date-parts":[[2001]]}}}' headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" + Access-Control-Expose-Headers: + - Link Connection: - - keep-alive + - close + Content-Encoding: + - gzip Content-Length: - - "1514" + - "762" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Via: - - 1.1 d1dad7d3c339d87d553c26a84c9ca5d2.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - uPV2-_0S38dZvskg5-c5pdgk2jZ57CTPlGzzway7BXaFLO4DI0DEGg== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdeu3F-1vHcEYkQ= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1514" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - ecd486db-8011-4899-9dd3-25d0e7a92be5 + - Wed, 04 Sep 2024 22:52:15 GMT + Server: + - Jetty(9.4.40.v20210413) + Vary: + - Accept-Encoding + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: code: 200 message: OK @@ -978,57 +909,54 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1063/1.4938384?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2?mailto=test@example.com response: body: string: - '{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": - {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, - "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", - "title": "Effect of native oxide layers on copper thin-film tensile properties: - A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": - 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": - null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": - {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, - "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = - {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n - pages = {235306},\n title = {Effect of native oxide layers on copper thin-film - tensile properties: A reactive molecular dynamics study},\n volume = {118},\n - year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael - Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}]} - - ' + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T04:17:17Z","timestamp":1687580237047},"reference-count":1,"publisher":"Springer + Science and Business Media LLC","issue":"1","license":[{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"},{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Reactions + Weekly"],"DOI":"10.1007\/s40278-023-41815-2","type":"journal-article","created":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T18:42:29Z","timestamp":1687545749000},"page":"145-145","source":"Crossref","is-referenced-by-count":0,"title":["Convalescent-anti-sars-cov-2-plasma\/immune-globulin"],"prefix":"10.1007","volume":"1962","member":"297","published-online":{"date-parts":[[2023,6,24]]},"reference":[{"key":"41815_CR1","doi-asserted-by":"crossref","unstructured":"Delgado-Fernandez + M, et al. Treatment of COVID-19 with convalescent plasma in patients with + humoral immunodeficiency - Three consecutive cases and review of the literature. + Enfermedades Infecciosas Y Microbiologia Clinica 40\n: 507-516, No. 9, Nov + 2022. Available from: URL: \nhttps:\/\/seimc.org\/","DOI":"10.1016\/j.eimce.2021.01.009"}],"container-title":["Reactions + Weekly"],"original-title":[],"language":"en","link":[{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/article\/10.1007\/s40278-023-41815-2\/fulltext.html","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T19:23:18Z","timestamp":1687548198000},"score":1,"resource":{"primary":{"URL":"https:\/\/link.springer.com\/10.1007\/s40278-023-41815-2"}},"subtitle":["Fever + and mild decrease in baseline oxygen saturation following off-label use: 3 + case reports"],"short-title":[],"issued":{"date-parts":[[2023,6,24]]},"references-count":1,"journal-issue":{"issue":"1","published-online":{"date-parts":[[2023,6]]}},"alternative-id":["41815"],"URL":"http:\/\/dx.doi.org\/10.1007\/s40278-023-41815-2","relation":{},"ISSN":["1179-2051"],"issn-type":[{"value":"1179-2051","type":"electronic"}],"subject":[],"published":{"date-parts":[[2023,6,24]]}}}' headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" + Access-Control-Expose-Headers: + - Link Connection: - - keep-alive + - close + Content-Encoding: + - gzip Content-Length: - - "1072" + - "1163" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Via: - - 1.1 e20259e84d7d881ed453b1f0e4f9a4c6.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - B5pmRydoSvmUVJ8YRzy6ojfZceADVh1gH0ApTjvaKUlR3DjfJN_cxA== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdeu3FzePHcEU2A= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1072" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - a506e5d8-f172-44bc-b1e7-b663130ccecf + - Wed, 04 Sep 2024 22:52:15 GMT + Server: + - Jetty(9.4.40.v20210413) + Vary: + - Accept-Encoding + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: code: 200 message: OK @@ -1036,76 +964,98 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.48550/arxiv.2312.07559?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.crossref.org/works/10.48550%2Farxiv.2312.07559?mailto=test@example.com response: body: - string: - '{"paperId": "7e55d8701785818776323b4147cb13354c820469", "externalIds": - {"ArXiv": "2312.07559", "DBLP": "journals/corr/abs-2312-07559", "DOI": "10.48550/arXiv.2312.07559", - "CorpusId": 266191420}, "url": "https://www.semanticscholar.org/paper/7e55d8701785818776323b4147cb13354c820469", - "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", - "venue": "arXiv.org", "year": 2023, "citationCount": 21, "influentialCitationCount": - 1, "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], - "publicationDate": "2023-12-08", "journal": {"name": "ArXiv", "volume": "abs/2312.07559"}, - "citationStyles": {"bibtex": "@Article{L''ala2023PaperQARG,\n author = {Jakub - L''ala and Odhran O''Donoghue and Aleksandar Shtedritski and Sam Cox and Samuel - G. Rodriques and Andrew D. White},\n booktitle = {arXiv.org},\n journal = - {ArXiv},\n title = {PaperQA: Retrieval-Augmented Generative Agent for Scientific - Research},\n volume = {abs/2312.07559},\n year = {2023}\n}\n"}, "authors": - [{"authorId": "2219926382", "name": "Jakub L''ala"}, {"authorId": "2258961056", - "name": "Odhran O''Donoghue"}, {"authorId": "2258961451", "name": "Aleksandar - Shtedritski"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": - "2258964497", "name": "Samuel G. Rodriques"}, {"authorId": "2273941271", "name": - "Andrew D. White"}]} - - ' + string: Resource not found. headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" + Access-Control-Expose-Headers: + - Link Connection: - - keep-alive - Content-Length: - - "1349" + - close Content-Type: - - application/json + - text/plain Date: - - Tue, 13 Aug 2024 18:51:23 GMT - Via: - - 1.1 27dc27c157f4b42ae253527f76742be4.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - IZn2EOJJ9cH-zelczpr-n3MhYTsn-5Voc574TZ4MjgvpdtrkGI5vgg== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdeu3FVlPHcEncA= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1349" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:23 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - cee2debe-4fbd-478a-a033-a085eab5a7da + - Wed, 04 Sep 2024 22:52:15 GMT + Server: + - Jetty(9.4.40.v20210413) + Transfer-Encoding: + - chunked + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: - code: 200 - message: OK + code: 404 + message: Not Found - request: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475/transform/application/x-bibtex + uri: https://api.crossref.org/works/10.1101%2F2024.04.01.587366?mailto=test@example.com response: body: string: - " @article{Adak_2001, volume={218}, ISSN={0300-8177}, url={http://dx.doi.org/10.1023/a:1007154515475}, - DOI={10.1023/a:1007154515475}, number={1/2}, journal={Molecular and Cellular - Biochemistry}, publisher={Springer Science and Business Media LLC}, author={Adak, - Subrata and Bandyopadhyay, Debashis and Bandyopadhyay, Uday and Banerjee, - Ranajit K.}, year={2001}, pages={1\u201311} }\n" + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"institution":[{"name":"bioRxiv"}],"indexed":{"date-parts":[[2024,4,5]],"date-time":"2024-04-05T00:42:23Z","timestamp":1712277743507},"posted":{"date-parts":[[2024,4,1]]},"group-title":"Genomics","reference-count":50,"publisher":"Cold + Spring Harbor Laboratory","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"accepted":{"date-parts":[[2024,4,1]]},"abstract":"ABSTRACT<\/jats:title>Understanding + the effects of rare genetic variants remains challenging, both in coding and + non-coding regions. While multiplexed assays of variant effect (MAVEs) have + enabled scalable functional assessment of variants, established MAVEs are + limited by either exogenous expression of variants or constraints of genome + editing. Here, we introduce a pooled prime editing (PE) platform in haploid + human cells to scalably assay variants in their endogenous context. We first + optimized delivery of variants to HAP1 cells, defining optimal pegRNA designs + and establishing a co-selection strategy for improved efficiency. We characterize + our platform in the context of negative selection by testing over 7,500 pegRNAs + targetingSMARCB1<\/jats:italic>for editing activity and observing + depletion of highly active pegRNAs installing loss-of-function variants. We + next assess variants inMLH1<\/jats:italic>via 6-thioguanine selection, + assaying 65.3% of all possible SNVs in a 200-bp region spanning exon 10 and + distinguishing LoF variants with high accuracy. Lastly, we assay 362 non-codingMLH1<\/jats:italic>variants + across a 60 kb region in a single experiment, identifying pathogenic variants + acting via multiple mechanisms with high specificity. Our analyses detail + how filtering for highly active pegRNAs can facilitate both positive and negative + selection screens. Accordingly, our platform promises to enable highly scalable + functional assessment of human variants.<\/jats:p>","DOI":"10.1101\/2024.04.01.587366","type":"posted-content","created":{"date-parts":[[2024,4,2]],"date-time":"2024-04-02T02:05:17Z","timestamp":1712023517000},"source":"Crossref","is-referenced-by-count":0,"title":["High-throughput + screening of human genetic variants by pooled prime editing"],"prefix":"10.1101","author":[{"given":"Michael","family":"Herger","sequence":"first","affiliation":[]},{"given":"Christina + M.","family":"Kajba","sequence":"additional","affiliation":[]},{"given":"Megan","family":"Buckley","sequence":"additional","affiliation":[]},{"given":"Ana","family":"Cunha","sequence":"additional","affiliation":[]},{"given":"Molly","family":"Strom","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-7767-8608","authenticated-orcid":false,"given":"Gregory + M.","family":"Findlay","sequence":"additional","affiliation":[]}],"member":"246","reference":[{"key":"2024040415500652000_2024.04.01.587366v1.1","doi-asserted-by":"publisher","DOI":"10.1038\/gim.2015.30"},{"key":"2024040415500652000_2024.04.01.587366v1.2","doi-asserted-by":"crossref","first-page":"116","DOI":"10.1016\/j.cels.2017.11.003","article-title":"Quantitative + Missense Variant Effect Prediction Using Large-Scale Mutagenesis Data","volume":"6","year":"2018","journal-title":"Cell + Syst"},{"key":"2024040415500652000_2024.04.01.587366v1.3","doi-asserted-by":"publisher","DOI":"10.1126\/science.abi8207"},{"key":"2024040415500652000_2024.04.01.587366v1.4","doi-asserted-by":"publisher","DOI":"10.1016\/J.CELL.2018.12.015"},{"key":"2024040415500652000_2024.04.01.587366v1.5","doi-asserted-by":"crossref","first-page":"eabn8153","DOI":"10.1126\/science.abn8197","article-title":"The + landscape of tolerated genetic variation in humans and primates","volume":"380","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.6","doi-asserted-by":"crossref","first-page":"eadg7492","DOI":"10.1126\/science.adg7492","article-title":"Accurate + proteome-wide missense variant effect prediction with AlphaMissense","volume":"381","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.7","doi-asserted-by":"publisher","DOI":"10.1093\/nar\/gkv1222"},{"key":"2024040415500652000_2024.04.01.587366v1.8","doi-asserted-by":"crossref","first-page":"1381","DOI":"10.1038\/s41436-021-01172-3","article-title":"ACMG + SF v3.0 list for reporting of secondary findings in clinical exome and genome + sequencing: a policy statement of the American College of Medical Genetics + and Genomics (ACMG)","volume":"23","year":"2021","journal-title":"Genet. Med"},{"key":"2024040415500652000_2024.04.01.587366v1.9","doi-asserted-by":"publisher","DOI":"10.1038\/nprot.2016.135"},{"key":"2024040415500652000_2024.04.01.587366v1.10","doi-asserted-by":"publisher","DOI":"10.1093\/hmg\/ddab219"},{"key":"2024040415500652000_2024.04.01.587366v1.11","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-11526-w"},{"key":"2024040415500652000_2024.04.01.587366v1.12","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-022-02839-z"},{"key":"2024040415500652000_2024.04.01.587366v1.13","doi-asserted-by":"crossref","first-page":"2248","DOI":"10.1016\/j.ajhg.2021.11.001","article-title":"Closing + the gap: Systematic integration of multiplexed functional data resolves variants + of uncertain significance in BRCA1, TP53, and PTEN","volume":"108","year":"2021","journal-title":"Am. + J. Hum. Genet."},{"key":"2024040415500652000_2024.04.01.587366v1.14","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-018-0461-z"},{"key":"2024040415500652000_2024.04.01.587366v1.15","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.10.015"},{"key":"2024040415500652000_2024.04.01.587366v1.16","doi-asserted-by":"crossref","first-page":"7702","DOI":"10.1038\/s41467-023-43041-4","article-title":"Saturation + genome editing of DDX3X clarifies pathogenicity of germline and somatic variation","volume":"14","year":"2023","journal-title":"Nat. + Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.17","doi-asserted-by":"publisher","DOI":"10.1038\/nature13695"},{"key":"2024040415500652000_2024.04.01.587366v1.18","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.012"},{"key":"2024040415500652000_2024.04.01.587366v1.19","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.041"},{"key":"2024040415500652000_2024.04.01.587366v1.20","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-019-1711-4"},{"key":"2024040415500652000_2024.04.01.587366v1.21","doi-asserted-by":"crossref","first-page":"288","DOI":"10.1016\/j.ccell.2022.12.009","article-title":"Base + editing screens map mutations affecting interferon-\u03b3 signaling in cancer","volume":"41","year":"2023","journal-title":"Cancer + Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.22","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.09.018"},{"key":"2024040415500652000_2024.04.01.587366v1.23","doi-asserted-by":"crossref","first-page":"402","DOI":"10.1038\/s41587-021-01039-7","article-title":"Engineered + pegRNAs improve prime editing efficiency","volume":"40","year":"2022","journal-title":"Nat. + Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.24","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01201-1"},{"key":"2024040415500652000_2024.04.01.587366v1.25","doi-asserted-by":"publisher","DOI":"10.1016\/j.molcel.2023.11.021"},{"key":"2024040415500652000_2024.04.01.587366v1.26","doi-asserted-by":"publisher","DOI":"10.1038\/nature10348"},{"key":"2024040415500652000_2024.04.01.587366v1.27","doi-asserted-by":"publisher","DOI":"10.1126\/science.1247005"},{"key":"2024040415500652000_2024.04.01.587366v1.28","doi-asserted-by":"crossref","first-page":"1151","DOI":"10.1038\/s41587-022-01613-7","article-title":"Predicting + prime editing efficiency and product purity by deep learning","volume":"41","year":"2023","journal-title":"Nat. + Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.29","doi-asserted-by":"crossref","first-page":"2256","DOI":"10.1016\/j.cell.2023.03.034","article-title":"Prediction + of efficiencies for diverse prime editing systems in multiple cell types","volume":"186","year":"2023","journal-title":"Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.30","doi-asserted-by":"publisher","DOI":"10.1101\/2022.10.26.513842"},{"key":"2024040415500652000_2024.04.01.587366v1.31","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01172-3"},{"key":"2024040415500652000_2024.04.01.587366v1.32","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-020-0677-y"},{"key":"2024040415500652000_2024.04.01.587366v1.33","doi-asserted-by":"publisher","DOI":"10.1016\/j.tibtech.2018.07.017"},{"key":"2024040415500652000_2024.04.01.587366v1.34","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-020-20810-z"},{"key":"2024040415500652000_2024.04.01.587366v1.35","doi-asserted-by":"crossref","first-page":"5909","DOI":"10.1038\/s41467-022-33669-z","article-title":"Marker-free + co-selection for successive rounds of prime editing in human cells","volume":"13","year":"2022","journal-title":"Nat. + Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.36","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2013.12.001"},{"key":"2024040415500652000_2024.04.01.587366v1.37","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-018-02974-x"},{"key":"2024040415500652000_2024.04.01.587366v1.38","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-021-27838-9"},{"key":"2024040415500652000_2024.04.01.587366v1.39","doi-asserted-by":"publisher","DOI":"10.1126\/science.1225829"},{"key":"2024040415500652000_2024.04.01.587366v1.40","doi-asserted-by":"publisher","DOI":"10.3390\/cancers14153645"},{"key":"2024040415500652000_2024.04.01.587366v1.41","doi-asserted-by":"publisher","DOI":"10.1126\/science.aac7557"},{"key":"2024040415500652000_2024.04.01.587366v1.42","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-10849-y"},{"key":"2024040415500652000_2024.04.01.587366v1.43","doi-asserted-by":"publisher","DOI":"10.1038\/nbt.3437"},{"key":"2024040415500652000_2024.04.01.587366v1.44","doi-asserted-by":"publisher","DOI":"10.1136\/jmg.2007.056499"},{"key":"2024040415500652000_2024.04.01.587366v1.45","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.12.003"},{"key":"2024040415500652000_2024.04.01.587366v1.46","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-019-0032-3"},{"key":"2024040415500652000_2024.04.01.587366v1.47","doi-asserted-by":"publisher","DOI":"10.1038\/nmeth.3047"},{"key":"2024040415500652000_2024.04.01.587366v1.48","doi-asserted-by":"crossref","first-page":"96","DOI":"10.1089\/hgtb.2017.198","article-title":"Determination + of Lentiviral Infectious Titer by a Novel Droplet Digital PCR Method","volume":"29","year":"2018","journal-title":"Hum. + Gene Ther. Methods"},{"key":"2024040415500652000_2024.04.01.587366v1.49","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-020-02091-3"},{"key":"2024040415500652000_2024.04.01.587366v1.50","doi-asserted-by":"publisher","DOI":"10.1186\/s13073-021-00835-9"}],"container-title":[],"original-title":[],"link":[{"URL":"https:\/\/syndication.highwire.org\/content\/doi\/10.1101\/2024.04.01.587366","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,4,4]],"date-time":"2024-04-04T22:50:19Z","timestamp":1712271019000},"score":1,"resource":{"primary":{"URL":"http:\/\/biorxiv.org\/lookup\/doi\/10.1101\/2024.04.01.587366"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2024,4,1]]},"references-count":50,"URL":"http:\/\/dx.doi.org\/10.1101\/2024.04.01.587366","relation":{},"subject":[],"published":{"date-parts":[[2024,4,1]]},"subtype":"preprint"}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -1116,12 +1066,18 @@ interactions: - Link Connection: - close + Content-Encoding: + - gzip + Content-Length: + - "3214" + Content-Type: + - application/json Date: - - Tue, 13 Aug 2024 18:51:24 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) - Transfer-Encoding: - - chunked + Vary: + - Accept-Encoding permissions-policy: - interest-cohort=() x-api-pool: @@ -1163,7 +1119,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:51:24 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -1210,7 +1166,51 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:51:24 GMT + - Wed, 04 Sep 2024 22:52:15 GMT + Server: + - Jetty(9.4.40.v20210413) + Transfer-Encoding: + - chunked + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475/transform/application/x-bibtex + response: + body: + string: + " @article{Adak_2001, volume={218}, ISSN={0300-8177}, url={http://dx.doi.org/10.1023/a:1007154515475}, + DOI={10.1023/a:1007154515475}, number={1/2}, journal={Molecular and Cellular + Biochemistry}, publisher={Springer Science and Business Media LLC}, author={Adak, + Subrata and Bandyopadhyay, Debashis and Bandyopadhyay, Uday and Banerjee, + Ranajit K.}, year={2001}, pages={1\u201311} }\n" + headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Link + Connection: + - close + Date: + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -1253,7 +1253,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:51:24 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -1299,7 +1299,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:51:24 GMT + - Wed, 04 Sep 2024 22:52:15 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_bulk_title_search.yaml b/tests/cassettes/test_bulk_title_search.yaml index 7a26243b..781d2988 100644 --- a/tests/cassettes/test_bulk_title_search.yaml +++ b/tests/cassettes/test_bulk_title_search.yaml @@ -1,4 +1,64 @@ interactions: + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=High-throughput+screening+of+human+genetic+variants+by+pooled+prime+editing&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"data": [{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": + {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", + "title": "High-throughput screening of human genetic variants by pooled prime + editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": + 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", + "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n + author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana + Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n + journal = {bioRxiv},\n title = {High-throughput screening of human genetic + variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": + "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": + "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, + {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", + "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}], + "matchScore": 252.53987}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1362" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 dee6f6319b26c06091b49860450185bc.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - 5b_YmO3yxkrgxxYMTHYZnrL0XfwwngTTfGwp3K1-6fwv73ExNPziOg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipDHnkvHcEJow= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1362" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - b2b30b39-0717-4939-a177-8a2fc1e1ad5a + status: + code: 200 + message: OK - request: body: null headers: {} @@ -7,11 +67,11 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2004346,"items":[{"indexed":{"date-parts":[[2024,3,8]],"date-time":"2024-03-08T00:26:06Z","timestamp":1709857566241},"reference-count":48,"publisher":"Oxford + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2016375,"items":[{"indexed":{"date-parts":[[2024,3,8]],"date-time":"2024-03-08T00:26:06Z","timestamp":1709857566241},"reference-count":48,"publisher":"Oxford University Press (OUP)","issue":"1","license":[{"start":{"date-parts":[[2024,2,21]],"date-time":"2024-02-21T00:00:00Z","timestamp":1708473600000},"content-version":"vor","delay-in-days":48,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0\/"}],"funder":[{"DOI":"10.13039\/100000092","name":"National - Library of Medicine","doi-asserted-by":"publisher","award":["R01LM009886","R01LM014344"]},{"DOI":"10.13039\/100006108","name":"National - Center for Advancing Translational Sciences","doi-asserted-by":"publisher","award":["UL1TR001873"]},{"DOI":"10.13039\/100000002","name":"National - Institutes of Health","doi-asserted-by":"publisher"}],"content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2024,1,4]]},"abstract":"Abstract<\/jats:title>\n \n Objective<\/jats:title>\n To + Library of Medicine","doi-asserted-by":"publisher","award":["R01LM009886","R01LM014344"],"id":[{"id":"10.13039\/100000092","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100006108","name":"National + Center for Advancing Translational Sciences","doi-asserted-by":"publisher","award":["UL1TR001873"],"id":[{"id":"10.13039\/100006108","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100000002","name":"National + Institutes of Health","doi-asserted-by":"publisher","id":[{"id":"10.13039\/100000002","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2024,1,4]]},"abstract":"Abstract<\/jats:title>\n \n Objective<\/jats:title>\n To automate scientific claim verification using PubMed abstracts.<\/jats:p>\n <\/jats:sec>\n \n Materials and Methods<\/jats:title>\n We developed CliVER, an end-to-end scientific Claim VERification system that leverages retrieval-augmented @@ -94,7 +154,7 @@ interactions: gain-based evaluation of IR techniques","volume":"20","author":"J\u00e4rvelin","year":"2002","journal-title":"ACM Trans Inf Syst"},{"key":"2024030720490192400_ooae021-B46","volume-title":"Evidence-Based Practice in Nursing & Healthcare: A Guide to Best Practice","author":"Melnyk","year":"2022"},{"key":"2024030720490192400_ooae021-B47","first-page":"206","author":"Gupta","year":"2017"},{"key":"2024030720490192400_ooae021-B48","first-page":"1","author":"Park","year":"2012"}],"container-title":["JAMIA - Open"],"language":"en","link":[{"URL":"https:\/\/academic.oup.com\/jamiaopen\/advance-article-pdf\/doi\/10.1093\/jamiaopen\/ooae021\/56732770\/ooae021.pdf","content-type":"application\/pdf","content-version":"am","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,3,7]],"date-time":"2024-03-07T20:49:23Z","timestamp":1709844563000},"score":28.254375,"resource":{"primary":{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article\/doi\/10.1093\/jamiaopen\/ooae021\/7612234"}},"issued":{"date-parts":[[2024,1,4]]},"references-count":48,"journal-issue":{"issue":"1","published-print":{"date-parts":[[2024,1,4]]}},"URL":"http:\/\/dx.doi.org\/10.1093\/jamiaopen\/ooae021","ISSN":["2574-2531"],"issn-type":[{"value":"2574-2531","type":"electronic"}],"published-other":{"date-parts":[[2024,4,1]]},"published":{"date-parts":[[2024,1,4]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + Open"],"language":"en","link":[{"URL":"https:\/\/academic.oup.com\/jamiaopen\/advance-article-pdf\/doi\/10.1093\/jamiaopen\/ooae021\/56732770\/ooae021.pdf","content-type":"application\/pdf","content-version":"am","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,3,7]],"date-time":"2024-03-07T20:49:23Z","timestamp":1709844563000},"score":28.217403,"resource":{"primary":{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article\/doi\/10.1093\/jamiaopen\/ooae021\/7612234"}},"issued":{"date-parts":[[2024,1,4]]},"references-count":48,"journal-issue":{"issue":"1","published-print":{"date-parts":[[2024,1,4]]}},"URL":"http:\/\/dx.doi.org\/10.1093\/jamiaopen\/ooae021","ISSN":["2574-2531"],"issn-type":[{"value":"2574-2531","type":"electronic"}],"published-other":{"date-parts":[[2024,4,1]]},"published":{"date-parts":[[2024,1,4]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -108,11 +168,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "4472" + - "4502" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:10 GMT + - Wed, 04 Sep 2024 22:52:16 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -136,54 +196,62 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works?mailto=test@example.com&query.title=Convalescent-anti-sars-cov-2-plasma/immune-globulin&rows=1 + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Augmenting+large+language+models+with+chemistry+tools&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":1477701,"items":[{"indexed":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T04:17:17Z","timestamp":1687580237047},"reference-count":1,"publisher":"Springer - Science and Business Media LLC","issue":"1","license":[{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"},{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Reactions - Weekly"],"DOI":"10.1007\/s40278-023-41815-2","type":"journal-article","created":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T18:42:29Z","timestamp":1687545749000},"page":"145-145","source":"Crossref","is-referenced-by-count":0,"title":["Convalescent-anti-sars-cov-2-plasma\/immune-globulin"],"prefix":"10.1007","volume":"1962","member":"297","published-online":{"date-parts":[[2023,6,24]]},"reference":[{"key":"41815_CR1","doi-asserted-by":"crossref","unstructured":"Delgado-Fernandez - M, et al. Treatment of COVID-19 with convalescent plasma in patients with - humoral immunodeficiency - Three consecutive cases and review of the literature. - Enfermedades Infecciosas Y Microbiologia Clinica 40\n: 507-516, No. 9, Nov - 2022. Available from: URL: \nhttps:\/\/seimc.org\/","DOI":"10.1016\/j.eimce.2021.01.009"}],"container-title":["Reactions - Weekly"],"language":"en","link":[{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/article\/10.1007\/s40278-023-41815-2\/fulltext.html","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T19:23:18Z","timestamp":1687548198000},"score":57.402653,"resource":{"primary":{"URL":"https:\/\/link.springer.com\/10.1007\/s40278-023-41815-2"}},"subtitle":["Fever - and mild decrease in baseline oxygen saturation following off-label use: 3 - case reports"],"issued":{"date-parts":[[2023,6,24]]},"references-count":1,"journal-issue":{"issue":"1","published-online":{"date-parts":[[2023,6]]}},"alternative-id":["41815"],"URL":"http:\/\/dx.doi.org\/10.1007\/s40278-023-41815-2","ISSN":["1179-2051"],"issn-type":[{"value":"1179-2051","type":"electronic"}],"published":{"date-parts":[[2023,6,24]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + '{"data": [{"paperId": "354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", "externalIds": + {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": + "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": + "38799228"}, "url": "https://www.semanticscholar.org/paper/354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", + "title": "Augmenting large language models with chemistry tools", "venue": + "Nat. Mac. Intell.", "year": 2023, "citationCount": 191, "influentialCitationCount": + 12, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.nature.com/articles/s42256-024-00832-8.pdf", + "status": "HYBRID"}, "publicationTypes": ["JournalArticle"], "publicationDate": + "2023-04-11", "journal": {"name": "Nature Machine Intelligence", "pages": + "525 - 535", "volume": "6"}, "citationStyles": {"bibtex": "@Article{Bran2023AugmentingLL,\n + author = {Andr\u00e9s M Bran and Sam Cox and Oliver Schilter and Carlo Baldassari + and Andrew D. White and P. Schwaller},\n booktitle = {Nat. Mac. Intell.},\n + journal = {Nature Machine Intelligence},\n pages = {525 - 535},\n title = + {Augmenting large language models with chemistry tools},\n volume = {6},\n + year = {2023}\n}\n"}, "authors": [{"authorId": "2216007369", "name": "Andr\u00e9s + M Bran"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", + "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, + {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", + "name": "P. Schwaller"}], "matchScore": 179.37169}]} + + ' headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close - Content-Encoding: - - gzip + - keep-alive Content-Length: - - "1207" + - "1551" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:10 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 ac433885d6f49db81bf694a6c6b6bea0.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - 8RWYfQ3K0fHIsdnKFbs2dMEqFXbc9GJGrH0PUd0eYkZeZWGuz9Q-oA== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipCF7OPHcEv5Q= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1551" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 3b7ac010-c3d6-40f5-bd40-810ac0a276bd status: code: 200 message: OK @@ -195,10 +263,10 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283975,"items":[{"indexed":{"date-parts":[[2024,8,13]],"date-time":"2024-08-13T19:31:35Z","timestamp":1723577495194},"reference-count":103,"publisher":"Springer + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"indexed":{"date-parts":[[2024,9,4]],"date-time":"2024-09-04T17:39:08Z","timestamp":1725471548702},"reference-count":103,"publisher":"Springer Science and Business Media LLC","issue":"5","license":[{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"},{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"}],"funder":[{"DOI":"10.13039\/501100001711","name":"Schweizerischer - Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"]},{"DOI":"10.13039\/100000001","name":"National - Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat + Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"],"id":[{"id":"10.13039\/501100001711","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100000001","name":"National + Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"],"id":[{"id":"10.13039\/100000001","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat Mach Intell"],"abstract":"Abstract<\/jats:title>Large language models (LLMs) have shown strong performance in tasks across domains but struggle with chemistry-related problems. These models also lack access @@ -212,7 +280,7 @@ interactions: both LLM and expert assessments, demonstrates ChemCrow\u2019s effectiveness in automating a diverse set of chemical tasks. Our work not only aids expert chemists and lowers barriers for non-experts but also fosters scientific advancement - by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":13,"title":["Augmenting + by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":17,"title":["Augmenting large language models with chemistry tools"],"prefix":"10.1038","volume":"6","author":[{"given":"Andres","family":"M. Bran","sequence":"first","affiliation":[]},{"given":"Sam","family":"Cox","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-0310-0851","authenticated-orcid":false,"given":"Oliver","family":"Schilter","sequence":"additional","affiliation":[]},{"given":"Carlo","family":"Baldassari","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-6647-3965","authenticated-orcid":false,"given":"Andrew D.","family":"White","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-3046-6576","authenticated-orcid":false,"given":"Philippe","family":"Schwaller","sequence":"additional","affiliation":[]}],"member":"297","published-online":{"date-parts":[[2024,5,8]]},"reference":[{"key":"832_CR1","unstructured":"Devlin, @@ -501,7 +569,7 @@ interactions: (2024).","DOI":"10.5281\/zenodo.10884645"},{"key":"832_CR103","doi-asserted-by":"publisher","unstructured":"Bran, A., Cox, S., White, A. & Schwaller, P. ur-whitelab\/chemcrow-public: v0.3.24. Zenodo https:\/\/doi.org\/10.5281\/zenodo.10884639 (2024).","DOI":"10.5281\/zenodo.10884639"}],"container-title":["Nature - Machine Intelligence"],"language":"en","link":[{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,5,23]],"date-time":"2024-05-23T23:03:31Z","timestamp":1716505411000},"score":46.768044,"resource":{"primary":{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8"}},"issued":{"date-parts":[[2024,5,8]]},"references-count":103,"journal-issue":{"issue":"5","published-online":{"date-parts":[[2024,5]]}},"alternative-id":["832"],"URL":"http:\/\/dx.doi.org\/10.1038\/s42256-024-00832-8","ISSN":["2522-5839"],"issn-type":[{"value":"2522-5839","type":"electronic"}],"published":{"date-parts":[[2024,5,8]]},"assertion":[{"value":"13 + Machine Intelligence"],"language":"en","link":[{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,5,23]],"date-time":"2024-05-23T23:03:31Z","timestamp":1716505411000},"score":46.73176,"resource":{"primary":{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8"}},"issued":{"date-parts":[[2024,5,8]]},"references-count":103,"journal-issue":{"issue":"5","published-online":{"date-parts":[[2024,5]]}},"alternative-id":["832"],"URL":"http:\/\/dx.doi.org\/10.1038\/s42256-024-00832-8","ISSN":["2522-5839"],"issn-type":[{"value":"2522-5839","type":"electronic"}],"published":{"date-parts":[[2024,5,8]]},"assertion":[{"value":"13 September 2023","order":1,"name":"received","label":"Received","group":{"name":"ArticleHistory","label":"Article History"}},{"value":"27 March 2024","order":2,"name":"accepted","label":"Accepted","group":{"name":"ArticleHistory","label":"Article History"}},{"value":"8 May 2024","order":3,"name":"first_online","label":"First @@ -522,11 +590,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "10547" + - "10573" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:10 GMT + - Wed, 04 Sep 2024 22:52:16 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -550,91 +618,52 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works?mailto=test@example.com&query.title=High-throughput+screening+of+human+genetic+variants+by+pooled+prime+editing&rows=1 + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Convalescent-anti-sars-cov-2-plasma/immune-globulin&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":5138747,"items":[{"institution":[{"name":"bioRxiv"}],"indexed":{"date-parts":[[2024,4,5]],"date-time":"2024-04-05T00:42:23Z","timestamp":1712277743507},"posted":{"date-parts":[[2024,4,1]]},"group-title":"Genomics","reference-count":50,"publisher":"Cold - Spring Harbor Laboratory","content-domain":{"domain":[],"crossmark-restriction":false},"accepted":{"date-parts":[[2024,4,1]]},"abstract":"ABSTRACT<\/jats:title>Understanding - the effects of rare genetic variants remains challenging, both in coding and - non-coding regions. While multiplexed assays of variant effect (MAVEs) have - enabled scalable functional assessment of variants, established MAVEs are - limited by either exogenous expression of variants or constraints of genome - editing. Here, we introduce a pooled prime editing (PE) platform in haploid - human cells to scalably assay variants in their endogenous context. We first - optimized delivery of variants to HAP1 cells, defining optimal pegRNA designs - and establishing a co-selection strategy for improved efficiency. We characterize - our platform in the context of negative selection by testing over 7,500 pegRNAs - targetingSMARCB1<\/jats:italic>for editing activity and observing - depletion of highly active pegRNAs installing loss-of-function variants. We - next assess variants inMLH1<\/jats:italic>via 6-thioguanine selection, - assaying 65.3% of all possible SNVs in a 200-bp region spanning exon 10 and - distinguishing LoF variants with high accuracy. Lastly, we assay 362 non-codingMLH1<\/jats:italic>variants - across a 60 kb region in a single experiment, identifying pathogenic variants - acting via multiple mechanisms with high specificity. Our analyses detail - how filtering for highly active pegRNAs can facilitate both positive and negative - selection screens. Accordingly, our platform promises to enable highly scalable - functional assessment of human variants.<\/jats:p>","DOI":"10.1101\/2024.04.01.587366","type":"posted-content","created":{"date-parts":[[2024,4,2]],"date-time":"2024-04-02T02:05:17Z","timestamp":1712023517000},"source":"Crossref","is-referenced-by-count":0,"title":["High-throughput - screening of human genetic variants by pooled prime editing"],"prefix":"10.1101","author":[{"given":"Michael","family":"Herger","sequence":"first","affiliation":[]},{"given":"Christina - M.","family":"Kajba","sequence":"additional","affiliation":[]},{"given":"Megan","family":"Buckley","sequence":"additional","affiliation":[]},{"given":"Ana","family":"Cunha","sequence":"additional","affiliation":[]},{"given":"Molly","family":"Strom","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-7767-8608","authenticated-orcid":false,"given":"Gregory - M.","family":"Findlay","sequence":"additional","affiliation":[]}],"member":"246","reference":[{"key":"2024040415500652000_2024.04.01.587366v1.1","doi-asserted-by":"publisher","DOI":"10.1038\/gim.2015.30"},{"key":"2024040415500652000_2024.04.01.587366v1.2","doi-asserted-by":"crossref","first-page":"116","DOI":"10.1016\/j.cels.2017.11.003","article-title":"Quantitative - Missense Variant Effect Prediction Using Large-Scale Mutagenesis Data","volume":"6","year":"2018","journal-title":"Cell - Syst"},{"key":"2024040415500652000_2024.04.01.587366v1.3","doi-asserted-by":"publisher","DOI":"10.1126\/science.abi8207"},{"key":"2024040415500652000_2024.04.01.587366v1.4","doi-asserted-by":"publisher","DOI":"10.1016\/J.CELL.2018.12.015"},{"key":"2024040415500652000_2024.04.01.587366v1.5","doi-asserted-by":"crossref","first-page":"eabn8153","DOI":"10.1126\/science.abn8197","article-title":"The - landscape of tolerated genetic variation in humans and primates","volume":"380","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.6","doi-asserted-by":"crossref","first-page":"eadg7492","DOI":"10.1126\/science.adg7492","article-title":"Accurate - proteome-wide missense variant effect prediction with AlphaMissense","volume":"381","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.7","doi-asserted-by":"publisher","DOI":"10.1093\/nar\/gkv1222"},{"key":"2024040415500652000_2024.04.01.587366v1.8","doi-asserted-by":"crossref","first-page":"1381","DOI":"10.1038\/s41436-021-01172-3","article-title":"ACMG - SF v3.0 list for reporting of secondary findings in clinical exome and genome - sequencing: a policy statement of the American College of Medical Genetics - and Genomics (ACMG)","volume":"23","year":"2021","journal-title":"Genet. Med"},{"key":"2024040415500652000_2024.04.01.587366v1.9","doi-asserted-by":"publisher","DOI":"10.1038\/nprot.2016.135"},{"key":"2024040415500652000_2024.04.01.587366v1.10","doi-asserted-by":"publisher","DOI":"10.1093\/hmg\/ddab219"},{"key":"2024040415500652000_2024.04.01.587366v1.11","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-11526-w"},{"key":"2024040415500652000_2024.04.01.587366v1.12","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-022-02839-z"},{"key":"2024040415500652000_2024.04.01.587366v1.13","doi-asserted-by":"crossref","first-page":"2248","DOI":"10.1016\/j.ajhg.2021.11.001","article-title":"Closing - the gap: Systematic integration of multiplexed functional data resolves variants - of uncertain significance in BRCA1, TP53, and PTEN","volume":"108","year":"2021","journal-title":"Am. - J. Hum. Genet."},{"key":"2024040415500652000_2024.04.01.587366v1.14","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-018-0461-z"},{"key":"2024040415500652000_2024.04.01.587366v1.15","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.10.015"},{"key":"2024040415500652000_2024.04.01.587366v1.16","doi-asserted-by":"crossref","first-page":"7702","DOI":"10.1038\/s41467-023-43041-4","article-title":"Saturation - genome editing of DDX3X clarifies pathogenicity of germline and somatic variation","volume":"14","year":"2023","journal-title":"Nat. - Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.17","doi-asserted-by":"publisher","DOI":"10.1038\/nature13695"},{"key":"2024040415500652000_2024.04.01.587366v1.18","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.012"},{"key":"2024040415500652000_2024.04.01.587366v1.19","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.041"},{"key":"2024040415500652000_2024.04.01.587366v1.20","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-019-1711-4"},{"key":"2024040415500652000_2024.04.01.587366v1.21","doi-asserted-by":"crossref","first-page":"288","DOI":"10.1016\/j.ccell.2022.12.009","article-title":"Base - editing screens map mutations affecting interferon-\u03b3 signaling in cancer","volume":"41","year":"2023","journal-title":"Cancer - Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.22","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.09.018"},{"key":"2024040415500652000_2024.04.01.587366v1.23","doi-asserted-by":"crossref","first-page":"402","DOI":"10.1038\/s41587-021-01039-7","article-title":"Engineered - pegRNAs improve prime editing efficiency","volume":"40","year":"2022","journal-title":"Nat. - Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.24","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01201-1"},{"key":"2024040415500652000_2024.04.01.587366v1.25","doi-asserted-by":"publisher","DOI":"10.1016\/j.molcel.2023.11.021"},{"key":"2024040415500652000_2024.04.01.587366v1.26","doi-asserted-by":"publisher","DOI":"10.1038\/nature10348"},{"key":"2024040415500652000_2024.04.01.587366v1.27","doi-asserted-by":"publisher","DOI":"10.1126\/science.1247005"},{"key":"2024040415500652000_2024.04.01.587366v1.28","doi-asserted-by":"crossref","first-page":"1151","DOI":"10.1038\/s41587-022-01613-7","article-title":"Predicting - prime editing efficiency and product purity by deep learning","volume":"41","year":"2023","journal-title":"Nat. - Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.29","doi-asserted-by":"crossref","first-page":"2256","DOI":"10.1016\/j.cell.2023.03.034","article-title":"Prediction - of efficiencies for diverse prime editing systems in multiple cell types","volume":"186","year":"2023","journal-title":"Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.30","doi-asserted-by":"publisher","DOI":"10.1101\/2022.10.26.513842"},{"key":"2024040415500652000_2024.04.01.587366v1.31","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01172-3"},{"key":"2024040415500652000_2024.04.01.587366v1.32","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-020-0677-y"},{"key":"2024040415500652000_2024.04.01.587366v1.33","doi-asserted-by":"publisher","DOI":"10.1016\/j.tibtech.2018.07.017"},{"key":"2024040415500652000_2024.04.01.587366v1.34","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-020-20810-z"},{"key":"2024040415500652000_2024.04.01.587366v1.35","doi-asserted-by":"crossref","first-page":"5909","DOI":"10.1038\/s41467-022-33669-z","article-title":"Marker-free - co-selection for successive rounds of prime editing in human cells","volume":"13","year":"2022","journal-title":"Nat. - Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.36","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2013.12.001"},{"key":"2024040415500652000_2024.04.01.587366v1.37","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-018-02974-x"},{"key":"2024040415500652000_2024.04.01.587366v1.38","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-021-27838-9"},{"key":"2024040415500652000_2024.04.01.587366v1.39","doi-asserted-by":"publisher","DOI":"10.1126\/science.1225829"},{"key":"2024040415500652000_2024.04.01.587366v1.40","doi-asserted-by":"publisher","DOI":"10.3390\/cancers14153645"},{"key":"2024040415500652000_2024.04.01.587366v1.41","doi-asserted-by":"publisher","DOI":"10.1126\/science.aac7557"},{"key":"2024040415500652000_2024.04.01.587366v1.42","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-10849-y"},{"key":"2024040415500652000_2024.04.01.587366v1.43","doi-asserted-by":"publisher","DOI":"10.1038\/nbt.3437"},{"key":"2024040415500652000_2024.04.01.587366v1.44","doi-asserted-by":"publisher","DOI":"10.1136\/jmg.2007.056499"},{"key":"2024040415500652000_2024.04.01.587366v1.45","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.12.003"},{"key":"2024040415500652000_2024.04.01.587366v1.46","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-019-0032-3"},{"key":"2024040415500652000_2024.04.01.587366v1.47","doi-asserted-by":"publisher","DOI":"10.1038\/nmeth.3047"},{"key":"2024040415500652000_2024.04.01.587366v1.48","doi-asserted-by":"crossref","first-page":"96","DOI":"10.1089\/hgtb.2017.198","article-title":"Determination - of Lentiviral Infectious Titer by a Novel Droplet Digital PCR Method","volume":"29","year":"2018","journal-title":"Hum. - Gene Ther. Methods"},{"key":"2024040415500652000_2024.04.01.587366v1.49","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-020-02091-3"},{"key":"2024040415500652000_2024.04.01.587366v1.50","doi-asserted-by":"publisher","DOI":"10.1186\/s13073-021-00835-9"}],"link":[{"URL":"https:\/\/syndication.highwire.org\/content\/doi\/10.1101\/2024.04.01.587366","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,4,4]],"date-time":"2024-04-04T22:50:19Z","timestamp":1712271019000},"score":61.49816,"resource":{"primary":{"URL":"http:\/\/biorxiv.org\/lookup\/doi\/10.1101\/2024.04.01.587366"}},"issued":{"date-parts":[[2024,4,1]]},"references-count":50,"URL":"http:\/\/dx.doi.org\/10.1101\/2024.04.01.587366","published":{"date-parts":[[2024,4,1]]},"subtype":"preprint"}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + '{"data": [{"paperId": "762ceacc20db232d3f6bcda6e867eb8148848ced", "externalIds": + {"DOI": "10.1007/s40278-023-33114-1", "CorpusId": 256742540}, "url": "https://www.semanticscholar.org/paper/762ceacc20db232d3f6bcda6e867eb8148848ced", + "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions + weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, + "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": null, "publicationDate": + "2023-02-01", "journal": {"name": "Reactions Weekly", "pages": "229", "volume": + "1943"}, "citationStyles": {"bibtex": "@Article{None,\n booktitle = {Reactions + weekly},\n journal = {Reactions Weekly},\n pages = {229},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n + volume = {1943},\n year = {2023}\n}\n"}, "authors": [], "matchScore": 240.49017}]} + + ' headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close - Content-Encoding: - - gzip + - keep-alive Content-Length: - - "3247" + - "848" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:10 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 ca38026a6de2ccb3904aed4709dff3d0.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - BekSoSV5_hDVcPr3pEfFqDuohiZ_bvOkJ-60h1CNHOAE2nlzXNuLXg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipDGiDPHcEkXQ= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "848" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - a4c541fa-4233-404c-861f-a8cec6e7dd5b status: code: 200 message: OK @@ -642,14 +671,20 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2/transform/application/x-bibtex + uri: https://api.crossref.org/works?mailto=test@example.com&query.title=Convalescent-anti-sars-cov-2-plasma/immune-globulin&rows=1 response: body: string: - " @article{2023, volume={1962}, ISSN={1179-2051}, url={http://dx.doi.org/10.1007/s40278-023-41815-2}, - DOI={10.1007/s40278-023-41815-2}, number={1}, journal={Reactions Weekly}, - publisher={Springer Science and Business Media LLC}, year={2023}, month=jun, - pages={145\u2013145} }\n" + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":1484561,"items":[{"indexed":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T04:17:17Z","timestamp":1687580237047},"reference-count":1,"publisher":"Springer + Science and Business Media LLC","issue":"1","license":[{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"},{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Reactions + Weekly"],"DOI":"10.1007\/s40278-023-41815-2","type":"journal-article","created":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T18:42:29Z","timestamp":1687545749000},"page":"145-145","source":"Crossref","is-referenced-by-count":0,"title":["Convalescent-anti-sars-cov-2-plasma\/immune-globulin"],"prefix":"10.1007","volume":"1962","member":"297","published-online":{"date-parts":[[2023,6,24]]},"reference":[{"key":"41815_CR1","doi-asserted-by":"crossref","unstructured":"Delgado-Fernandez + M, et al. Treatment of COVID-19 with convalescent plasma in patients with + humoral immunodeficiency - Three consecutive cases and review of the literature. + Enfermedades Infecciosas Y Microbiologia Clinica 40\n: 507-516, No. 9, Nov + 2022. Available from: URL: \nhttps:\/\/seimc.org\/","DOI":"10.1016\/j.eimce.2021.01.009"}],"container-title":["Reactions + Weekly"],"language":"en","link":[{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/article\/10.1007\/s40278-023-41815-2\/fulltext.html","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T19:23:18Z","timestamp":1687548198000},"score":57.623535,"resource":{"primary":{"URL":"https:\/\/link.springer.com\/10.1007\/s40278-023-41815-2"}},"subtitle":["Fever + and mild decrease in baseline oxygen saturation following off-label use: 3 + case reports"],"issued":{"date-parts":[[2023,6,24]]},"references-count":1,"journal-issue":{"issue":"1","published-online":{"date-parts":[[2023,6]]}},"alternative-id":["41815"],"URL":"http:\/\/dx.doi.org\/10.1007\/s40278-023-41815-2","ISSN":["1179-2051"],"issn-type":[{"value":"1179-2051","type":"electronic"}],"published":{"date-parts":[[2023,6,24]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -660,12 +695,18 @@ interactions: - Link Connection: - close + Content-Encoding: + - gzip + Content-Length: + - "1208" + Content-Type: + - application/json Date: - - Tue, 13 Aug 2024 19:48:11 GMT + - Wed, 04 Sep 2024 22:52:16 GMT Server: - Jetty(9.4.40.v20210413) - Transfer-Encoding: - - chunked + Vary: + - Accept-Encoding permissions-policy: - interest-cohort=() x-api-pool: @@ -685,47 +726,240 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works?mailto=test@example.com&query.title=An+essential+role+of+active+site+arginine+residue+in+iodide+binding+and+histidine+residue+in+electron+transfer+for+iodide+oxidation+by+horseradish+peroxidase&rows=1 + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=PaperQA:+Retrieval-Augmented+Generative+Agent+for+Scientific+Research&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":4058432,"items":[{"indexed":{"date-parts":[[2024,6,24]],"date-time":"2024-06-24T23:08:06Z","timestamp":1719270486276},"reference-count":40,"publisher":"Elsevier - BV","issue":"14","license":[{"start":{"date-parts":[[1992,5,1]],"date-time":"1992-05-01T00:00:00Z","timestamp":704678400000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.elsevier.com\/tdm\/userlicense\/1.0\/"},{"start":{"date-parts":[[2020,10,3]],"date-time":"2020-10-03T00:00:00Z","timestamp":1601683200000},"content-version":"vor","delay-in-days":10382,"URL":"http:\/\/creativecommons.org\/licenses\/by\/4.0\/"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Journal - of Biological Chemistry"],"published-print":{"date-parts":[[1992,5]]},"DOI":"10.1016\/s0021-9258(19)50164-8","type":"journal-article","created":{"date-parts":[[2021,1,6]],"date-time":"2021-01-06T15:57:43Z","timestamp":1609948663000},"page":"9800-9804","source":"Crossref","is-referenced-by-count":23,"title":["Chemical - and kinetic evidence for an essential histidine in horseradish peroxidase - for iodide oxidation."],"prefix":"10.1016","volume":"267","author":[{"given":"D.K.","family":"Bhattacharyya","sequence":"first","affiliation":[]},{"given":"U","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"R.K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"78","reference":[{"key":"10.1016\/S0021-9258(19)50164-8_bib1","doi-asserted-by":"crossref","first-page":"531","DOI":"10.1093\/oxfordjournals.jbchem.a133961","volume":"92","author":"Aibara","year":"1982","journal-title":"J. - Biochem. (Tokyo)"},{"key":"10.1016\/S0021-9258(19)50164-8_bib2","doi-asserted-by":"crossref","first-page":"341","DOI":"10.1016\/0003-2697(62)90097-0","volume":"4","author":"Alexander","year":"1962","journal-title":"Anal. - Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib3","doi-asserted-by":"crossref","first-page":"10592","DOI":"10.1016\/S0021-9258(18)67426-5","volume":"261","author":"Banerjee","year":"1986","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib4","doi-asserted-by":"crossref","first-page":"396","DOI":"10.1016\/0005-2744(70)90245-7","volume":"212","author":"Bjorkstein","year":"1970","journal-title":"Biochim. - Biophys. Acta"},{"key":"10.1016\/S0021-9258(19)50164-8_bib5","doi-asserted-by":"crossref","first-page":"12454","DOI":"10.1016\/S0021-9258(19)38367-X","volume":"265","author":"Blanke","year":"1990","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib6","doi-asserted-by":"crossref","first-page":"205","DOI":"10.1021\/bi00698a030","volume":"13","author":"Burstein","year":"1974","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib7","doi-asserted-by":"crossref","first-page":"19666","DOI":"10.1016\/S0021-9258(19)47165-2","volume":"264","author":"Chang","year":"1989","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib8","doi-asserted-by":"crossref","first-page":"4936","DOI":"10.1016\/S0021-9258(18)89162-1","volume":"260","author":"Church","year":"1985","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib9","doi-asserted-by":"crossref","first-page":"4992","DOI":"10.1021\/bi00668a008","volume":"15","author":"Cousineau","year":"1976","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib10","doi-asserted-by":"crossref","first-page":"21498","DOI":"10.1016\/S0021-9258(18)45766-3","volume":"265","author":"Dumas","year":"1990","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib11","first-page":"410","volume":"4","author":"Dunford","year":"1982","journal-title":"Adv. - Inorg. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib12","doi-asserted-by":"crossref","first-page":"187","DOI":"10.1016\/S0010-8545(00)80316-1","volume":"19","author":"Dunford","year":"1976","journal-title":"Coord. - Chem. Rev."},{"key":"10.1016\/S0021-9258(19)50164-8_bib13","doi-asserted-by":"crossref","first-page":"21","DOI":"10.1016\/0022-2836(85)90180-9","volume":"185","author":"Fita","year":"1985","journal-title":"J. - Mol. Biol."},{"key":"10.1016\/S0021-9258(19)50164-8_bib14","doi-asserted-by":"crossref","first-page":"15054","DOI":"10.1016\/S0021-9258(18)33392-1","volume":"257","author":"Kaput","year":"1982","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib15","doi-asserted-by":"crossref","first-page":"409","DOI":"10.1016\/0003-9861(87)90118-4","volume":"254","author":"Kenigsberg","year":"1987","journal-title":"Arch. - Biochem. Biophys."},{"key":"10.1016\/S0021-9258(19)50164-8_bib16","doi-asserted-by":"crossref","first-page":"55","DOI":"10.1016\/0003-9861(88)90103-8","volume":"261","author":"Konpka","year":"1988","journal-title":"Arch. - Biochem. Biophys."},{"key":"10.1016\/S0021-9258(19)50164-8_bib17","doi-asserted-by":"crossref","first-page":"3654","DOI":"10.1016\/S0021-9258(19)75322-8","volume":"238","author":"Levy","year":"1963","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib18","volume":"1","author":"Lundblad","year":"1984"},{"key":"10.1016\/S0021-9258(19)50164-8_bib19","doi-asserted-by":"crossref","first-page":"197","DOI":"10.1016\/S0021-9258(17)43641-6","volume":"259","author":"Magnusson","year":"1984","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib20","doi-asserted-by":"crossref","first-page":"251","DOI":"10.1021\/bi00804a010","volume":"9","author":"Melchior","year":"1970","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib21","doi-asserted-by":"crossref","first-page":"431","DOI":"10.1016\/0076-6879(77)47043-5","volume":"47","author":"Miles","year":"1977","journal-title":"Methods - Enzymol."},{"key":"10.1016\/S0021-9258(19)50164-8_bib22","doi-asserted-by":"crossref","first-page":"861","DOI":"10.1146\/annurev.bi.45.070176.004241","volume":"45","author":"Morrison","year":"1976","journal-title":"Annu. - Rev. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib23","doi-asserted-by":"crossref","first-page":"13546","DOI":"10.1016\/S0021-9258(17)38757-4","volume":"260","author":"Nakamura","year":"1985","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib24","doi-asserted-by":"crossref","first-page":"805","DOI":"10.1016\/S0021-9258(19)70048-9","volume":"256","author":"Ohtaki","year":"1981","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib25","doi-asserted-by":"crossref","first-page":"761","DOI":"10.1016\/S0021-9258(19)68261-X","volume":"257","author":"Ohtaki","year":"1982","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib26","first-page":"445","volume":"2","author":"Ovadi","year":"1967","journal-title":"Acta - Biochim. Biophys. Acad. Sci. Hung."},{"key":"10.1016\/S0021-9258(19)50164-8_bib27","doi-asserted-by":"crossref","first-page":"395","DOI":"10.3891\/acta.chem.scand.32b-0395","volume":"B32","author":"Paul","year":"1978","journal-title":"Acta - Chem. Scand."},{"key":"10.1016\/S0021-9258(19)50164-8_bib28","doi-asserted-by":"crossref","first-page":"497","DOI":"10.1111\/j.1432-1033.1973.tb03085.x","volume":"38","author":"Pommier","year":"1973","journal-title":"Eur. - J. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib29","doi-asserted-by":"crossref","first-page":"8199","DOI":"10.1016\/S0021-9258(19)70630-9","volume":"255","author":"Poulos","year":"1980","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib30","doi-asserted-by":"crossref","first-page":"2076","DOI":"10.1021\/bi00761a013","volume":"11","author":"Roman","year":"1972","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib31","doi-asserted-by":"crossref","first-page":"211","DOI":"10.1246\/cl.1985.211","author":"Sakurada","year":"1985","journal-title":"Chem. - Lett."},{"key":"10.1016\/S0021-9258(19)50164-8_bib32","doi-asserted-by":"crossref","first-page":"9657","DOI":"10.1016\/S0021-9258(18)67564-7","volume":"261","author":"Sakurada","year":"1986","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib33","doi-asserted-by":"crossref","first-page":"4007","DOI":"10.1016\/S0021-9258(18)61303-1","volume":"262","author":"Sakurada","year":"1987","journal-title":"J. - Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib34","doi-asserted-by":"crossref","first-page":"6478","DOI":"10.1021\/bi00394a028","volume":"26","author":"Sakurada","year":"1987","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib35","doi-asserted-by":"crossref","first-page":"2277","DOI":"10.1021\/bi00407a005","volume":"27","author":"Sams","year":"1988","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib36","doi-asserted-by":"crossref","first-page":"1571","DOI":"10.1093\/oxfordjournals.jbchem.a135630","volume":"99","author":"Takeuchi","year":"1986","journal-title":"J. - Biochem. (Tokyo)"},{"key":"10.1016\/S0021-9258(19)50164-8_bib37","doi-asserted-by":"crossref","first-page":"520","DOI":"10.1038\/326520a0","volume":"326","author":"Tien","year":"1987","journal-title":"Nature"},{"key":"10.1016\/S0021-9258(19)50164-8_bib38","doi-asserted-by":"crossref","first-page":"87","DOI":"10.1111\/j.1432-1033.1986.tb09461.x","volume":"155","author":"Topham","year":"1986","journal-title":"Eur. - J. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib39","doi-asserted-by":"crossref","first-page":"210","DOI":"10.1016\/0005-2744(81)90032-2","volume":"662","author":"Ugarova","year":"1981","journal-title":"Biochim. - Biophys. Acta"},{"key":"10.1016\/S0021-9258(19)50164-8_bib40","doi-asserted-by":"crossref","first-page":"483","DOI":"10.1111\/j.1432-1033.1979.tb13061.x","volume":"96","author":"Welinder","year":"1979","journal-title":"Eur. - J. Biochem."}],"container-title":["Journal of Biological Chemistry"],"language":"en","link":[{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0021925819501648?httpAccept=text\/xml","content-type":"text\/xml","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0021925819501648?httpAccept=text\/plain","content-type":"text\/plain","content-version":"vor","intended-application":"text-mining"}],"deposited":{"date-parts":[[2022,1,2]],"date-time":"2022-01-02T19:58:22Z","timestamp":1641153502000},"score":52.82067,"resource":{"primary":{"URL":"https:\/\/linkinghub.elsevier.com\/retrieve\/pii\/S0021925819501648"}},"issued":{"date-parts":[[1992,5]]},"references-count":40,"journal-issue":{"issue":"14","published-print":{"date-parts":[[1992,5]]}},"alternative-id":["S0021925819501648"],"URL":"http:\/\/dx.doi.org\/10.1016\/s0021-9258(19)50164-8","ISSN":["0021-9258"],"issn-type":[{"value":"0021-9258","type":"print"}],"published":{"date-parts":[[1992,5]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + '{"data": [{"paperId": "7e55d8701785818776323b4147cb13354c820469", "externalIds": + {"ArXiv": "2312.07559", "DBLP": "journals/corr/abs-2312-07559", "DOI": "10.48550/arXiv.2312.07559", + "CorpusId": 266191420}, "url": "https://www.semanticscholar.org/paper/7e55d8701785818776323b4147cb13354c820469", + "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", + "venue": "arXiv.org", "year": 2023, "citationCount": 23, "influentialCitationCount": + 1, "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], + "publicationDate": "2023-12-08", "journal": {"name": "ArXiv", "volume": "abs/2312.07559"}, + "citationStyles": {"bibtex": "@Article{L''ala2023PaperQARG,\n author = {Jakub + L''ala and Odhran O''Donoghue and Aleksandar Shtedritski and Sam Cox and Samuel + G. Rodriques and Andrew D. White},\n booktitle = {arXiv.org},\n journal = + {ArXiv},\n title = {PaperQA: Retrieval-Augmented Generative Agent for Scientific + Research},\n volume = {abs/2312.07559},\n year = {2023}\n}\n"}, "authors": + [{"authorId": "2219926382", "name": "Jakub L''ala"}, {"authorId": "2258961056", + "name": "Odhran O''Donoghue"}, {"authorId": "2258961451", "name": "Aleksandar + Shtedritski"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": + "2258964497", "name": "Samuel G. Rodriques"}, {"authorId": "2273941271", "name": + "Andrew D. White"}], "matchScore": 252.56201}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1386" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 ac3f0425be668a2439884bb8cbd3ccd8.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - akQIiUpSMnHVbA6Rd7fM9bx3okuFjd51sJHSN13ytMKyN2yu82OiTw== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipDF4hvHcEb2A= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1386" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 2c7f8551-c2f5-44e5-b443-1e2e55cc4535 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=An+essential+role+of+active+site+arginine+residue+in+iodide+binding+and+histidine+residue+in+electron+transfer+for+iodide+oxidation+by+horseradish+peroxidase&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"data": [{"paperId": "19807da5b11f3e641535cb72e465001b49b48ee5", "externalIds": + {"MAG": "1554322594", "DOI": "10.1023/A:1007154515475", "CorpusId": 22646521, + "PubMed": "11330823"}, "url": "https://www.semanticscholar.org/paper/19807da5b11f3e641535cb72e465001b49b48ee5", + "title": "An essential role of active site arginine residue in iodide binding + and histidine residue in electron transfer for iodide oxidation by horseradish + peroxidase", "venue": "Molecular and Cellular Biochemistry", "year": 2001, + "citationCount": 7, "influentialCitationCount": 0, "isOpenAccess": false, + "openAccessPdf": null, "publicationTypes": ["JournalArticle", "Study"], "publicationDate": + "2001-02-01", "journal": {"name": "Molecular and Cellular Biochemistry", "pages": + "1-11", "volume": "218"}, "citationStyles": {"bibtex": "@Article{Adak2001AnER,\n + author = {S. Adak and D. Bandyopadhyay and U. Bandyopadhyay and R. Banerjee},\n + booktitle = {Molecular and Cellular Biochemistry},\n journal = {Molecular + and Cellular Biochemistry},\n pages = {1-11},\n title = {An essential role + of active site arginine residue in iodide binding and histidine residue in + electron transfer for iodide oxidation by horseradish peroxidase},\n volume + = {218},\n year = {2001}\n}\n"}, "authors": [{"authorId": "1940081", "name": + "S. Adak"}, {"authorId": "1701389", "name": "D. Bandyopadhyay"}, {"authorId": + "5343877", "name": "U. Bandyopadhyay"}, {"authorId": "32656528", "name": "R. + Banerjee"}], "matchScore": 386.07397}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1483" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 0f4013a0af68dcba176ca4372e470df4.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - ZryTJm7V0OUJNMOw-4WtBaG6Xa9q7tIRGaC-VwmN7nbaHLD_oe4VWg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipDFyRPHcESSg= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1483" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 436a1dd8-fcea-4b29-902b-b6c4deda66db + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": + {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, + "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", + "title": "Effect of native oxide layers on copper thin-film tensile properties: + A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": + 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": + null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": + {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, + "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = + {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n + pages = {235306},\n title = {Effect of native oxide layers on copper thin-film + tensile properties: A reactive molecular dynamics study},\n volume = {118},\n + year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael + Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": + 283.23236}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1109" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:16 GMT + Via: + - 1.1 cc58556a6e846289f4d3105969536e4c.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - fr8wFJr8qUMSHVSM8k2HN7PVFQLvdl5NGBYIylx4PXKMWimUr_1btw== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmipDE-TvHcEKkQ= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1109" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:16 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - db83976b-3b0e-46db-ab2d-0f79796d0935 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works?mailto=test@example.com&query.title=High-throughput+screening+of+human+genetic+variants+by+pooled+prime+editing&rows=1 + response: + body: + string: + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":5160892,"items":[{"institution":[{"name":"bioRxiv"}],"indexed":{"date-parts":[[2024,4,5]],"date-time":"2024-04-05T00:42:23Z","timestamp":1712277743507},"posted":{"date-parts":[[2024,4,1]]},"group-title":"Genomics","reference-count":50,"publisher":"Cold + Spring Harbor Laboratory","content-domain":{"domain":[],"crossmark-restriction":false},"accepted":{"date-parts":[[2024,4,1]]},"abstract":"ABSTRACT<\/jats:title>Understanding + the effects of rare genetic variants remains challenging, both in coding and + non-coding regions. While multiplexed assays of variant effect (MAVEs) have + enabled scalable functional assessment of variants, established MAVEs are + limited by either exogenous expression of variants or constraints of genome + editing. Here, we introduce a pooled prime editing (PE) platform in haploid + human cells to scalably assay variants in their endogenous context. We first + optimized delivery of variants to HAP1 cells, defining optimal pegRNA designs + and establishing a co-selection strategy for improved efficiency. We characterize + our platform in the context of negative selection by testing over 7,500 pegRNAs + targetingSMARCB1<\/jats:italic>for editing activity and observing + depletion of highly active pegRNAs installing loss-of-function variants. We + next assess variants inMLH1<\/jats:italic>via 6-thioguanine selection, + assaying 65.3% of all possible SNVs in a 200-bp region spanning exon 10 and + distinguishing LoF variants with high accuracy. Lastly, we assay 362 non-codingMLH1<\/jats:italic>variants + across a 60 kb region in a single experiment, identifying pathogenic variants + acting via multiple mechanisms with high specificity. Our analyses detail + how filtering for highly active pegRNAs can facilitate both positive and negative + selection screens. Accordingly, our platform promises to enable highly scalable + functional assessment of human variants.<\/jats:p>","DOI":"10.1101\/2024.04.01.587366","type":"posted-content","created":{"date-parts":[[2024,4,2]],"date-time":"2024-04-02T02:05:17Z","timestamp":1712023517000},"source":"Crossref","is-referenced-by-count":0,"title":["High-throughput + screening of human genetic variants by pooled prime editing"],"prefix":"10.1101","author":[{"given":"Michael","family":"Herger","sequence":"first","affiliation":[]},{"given":"Christina + M.","family":"Kajba","sequence":"additional","affiliation":[]},{"given":"Megan","family":"Buckley","sequence":"additional","affiliation":[]},{"given":"Ana","family":"Cunha","sequence":"additional","affiliation":[]},{"given":"Molly","family":"Strom","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-7767-8608","authenticated-orcid":false,"given":"Gregory + M.","family":"Findlay","sequence":"additional","affiliation":[]}],"member":"246","reference":[{"key":"2024040415500652000_2024.04.01.587366v1.1","doi-asserted-by":"publisher","DOI":"10.1038\/gim.2015.30"},{"key":"2024040415500652000_2024.04.01.587366v1.2","doi-asserted-by":"crossref","first-page":"116","DOI":"10.1016\/j.cels.2017.11.003","article-title":"Quantitative + Missense Variant Effect Prediction Using Large-Scale Mutagenesis Data","volume":"6","year":"2018","journal-title":"Cell + Syst"},{"key":"2024040415500652000_2024.04.01.587366v1.3","doi-asserted-by":"publisher","DOI":"10.1126\/science.abi8207"},{"key":"2024040415500652000_2024.04.01.587366v1.4","doi-asserted-by":"publisher","DOI":"10.1016\/J.CELL.2018.12.015"},{"key":"2024040415500652000_2024.04.01.587366v1.5","doi-asserted-by":"crossref","first-page":"eabn8153","DOI":"10.1126\/science.abn8197","article-title":"The + landscape of tolerated genetic variation in humans and primates","volume":"380","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.6","doi-asserted-by":"crossref","first-page":"eadg7492","DOI":"10.1126\/science.adg7492","article-title":"Accurate + proteome-wide missense variant effect prediction with AlphaMissense","volume":"381","year":"2023","journal-title":"Science"},{"key":"2024040415500652000_2024.04.01.587366v1.7","doi-asserted-by":"publisher","DOI":"10.1093\/nar\/gkv1222"},{"key":"2024040415500652000_2024.04.01.587366v1.8","doi-asserted-by":"crossref","first-page":"1381","DOI":"10.1038\/s41436-021-01172-3","article-title":"ACMG + SF v3.0 list for reporting of secondary findings in clinical exome and genome + sequencing: a policy statement of the American College of Medical Genetics + and Genomics (ACMG)","volume":"23","year":"2021","journal-title":"Genet. Med"},{"key":"2024040415500652000_2024.04.01.587366v1.9","doi-asserted-by":"publisher","DOI":"10.1038\/nprot.2016.135"},{"key":"2024040415500652000_2024.04.01.587366v1.10","doi-asserted-by":"publisher","DOI":"10.1093\/hmg\/ddab219"},{"key":"2024040415500652000_2024.04.01.587366v1.11","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-11526-w"},{"key":"2024040415500652000_2024.04.01.587366v1.12","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-022-02839-z"},{"key":"2024040415500652000_2024.04.01.587366v1.13","doi-asserted-by":"crossref","first-page":"2248","DOI":"10.1016\/j.ajhg.2021.11.001","article-title":"Closing + the gap: Systematic integration of multiplexed functional data resolves variants + of uncertain significance in BRCA1, TP53, and PTEN","volume":"108","year":"2021","journal-title":"Am. + J. Hum. Genet."},{"key":"2024040415500652000_2024.04.01.587366v1.14","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-018-0461-z"},{"key":"2024040415500652000_2024.04.01.587366v1.15","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.10.015"},{"key":"2024040415500652000_2024.04.01.587366v1.16","doi-asserted-by":"crossref","first-page":"7702","DOI":"10.1038\/s41467-023-43041-4","article-title":"Saturation + genome editing of DDX3X clarifies pathogenicity of germline and somatic variation","volume":"14","year":"2023","journal-title":"Nat. + Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.17","doi-asserted-by":"publisher","DOI":"10.1038\/nature13695"},{"key":"2024040415500652000_2024.04.01.587366v1.18","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.012"},{"key":"2024040415500652000_2024.04.01.587366v1.19","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.01.041"},{"key":"2024040415500652000_2024.04.01.587366v1.20","doi-asserted-by":"publisher","DOI":"10.1038\/s41586-019-1711-4"},{"key":"2024040415500652000_2024.04.01.587366v1.21","doi-asserted-by":"crossref","first-page":"288","DOI":"10.1016\/j.ccell.2022.12.009","article-title":"Base + editing screens map mutations affecting interferon-\u03b3 signaling in cancer","volume":"41","year":"2023","journal-title":"Cancer + Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.22","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2021.09.018"},{"key":"2024040415500652000_2024.04.01.587366v1.23","doi-asserted-by":"crossref","first-page":"402","DOI":"10.1038\/s41587-021-01039-7","article-title":"Engineered + pegRNAs improve prime editing efficiency","volume":"40","year":"2022","journal-title":"Nat. + Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.24","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01201-1"},{"key":"2024040415500652000_2024.04.01.587366v1.25","doi-asserted-by":"publisher","DOI":"10.1016\/j.molcel.2023.11.021"},{"key":"2024040415500652000_2024.04.01.587366v1.26","doi-asserted-by":"publisher","DOI":"10.1038\/nature10348"},{"key":"2024040415500652000_2024.04.01.587366v1.27","doi-asserted-by":"publisher","DOI":"10.1126\/science.1247005"},{"key":"2024040415500652000_2024.04.01.587366v1.28","doi-asserted-by":"crossref","first-page":"1151","DOI":"10.1038\/s41587-022-01613-7","article-title":"Predicting + prime editing efficiency and product purity by deep learning","volume":"41","year":"2023","journal-title":"Nat. + Biotechnol"},{"key":"2024040415500652000_2024.04.01.587366v1.29","doi-asserted-by":"crossref","first-page":"2256","DOI":"10.1016\/j.cell.2023.03.034","article-title":"Prediction + of efficiencies for diverse prime editing systems in multiple cell types","volume":"186","year":"2023","journal-title":"Cell"},{"key":"2024040415500652000_2024.04.01.587366v1.30","doi-asserted-by":"publisher","DOI":"10.1101\/2022.10.26.513842"},{"key":"2024040415500652000_2024.04.01.587366v1.31","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-021-01172-3"},{"key":"2024040415500652000_2024.04.01.587366v1.32","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-020-0677-y"},{"key":"2024040415500652000_2024.04.01.587366v1.33","doi-asserted-by":"publisher","DOI":"10.1016\/j.tibtech.2018.07.017"},{"key":"2024040415500652000_2024.04.01.587366v1.34","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-020-20810-z"},{"key":"2024040415500652000_2024.04.01.587366v1.35","doi-asserted-by":"crossref","first-page":"5909","DOI":"10.1038\/s41467-022-33669-z","article-title":"Marker-free + co-selection for successive rounds of prime editing in human cells","volume":"13","year":"2022","journal-title":"Nat. + Commun"},{"key":"2024040415500652000_2024.04.01.587366v1.36","doi-asserted-by":"publisher","DOI":"10.1016\/j.cell.2013.12.001"},{"key":"2024040415500652000_2024.04.01.587366v1.37","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-018-02974-x"},{"key":"2024040415500652000_2024.04.01.587366v1.38","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-021-27838-9"},{"key":"2024040415500652000_2024.04.01.587366v1.39","doi-asserted-by":"publisher","DOI":"10.1126\/science.1225829"},{"key":"2024040415500652000_2024.04.01.587366v1.40","doi-asserted-by":"publisher","DOI":"10.3390\/cancers14153645"},{"key":"2024040415500652000_2024.04.01.587366v1.41","doi-asserted-by":"publisher","DOI":"10.1126\/science.aac7557"},{"key":"2024040415500652000_2024.04.01.587366v1.42","doi-asserted-by":"publisher","DOI":"10.1038\/s41467-019-10849-y"},{"key":"2024040415500652000_2024.04.01.587366v1.43","doi-asserted-by":"publisher","DOI":"10.1038\/nbt.3437"},{"key":"2024040415500652000_2024.04.01.587366v1.44","doi-asserted-by":"publisher","DOI":"10.1136\/jmg.2007.056499"},{"key":"2024040415500652000_2024.04.01.587366v1.45","doi-asserted-by":"publisher","DOI":"10.1016\/j.ajhg.2020.12.003"},{"key":"2024040415500652000_2024.04.01.587366v1.46","doi-asserted-by":"publisher","DOI":"10.1038\/s41587-019-0032-3"},{"key":"2024040415500652000_2024.04.01.587366v1.47","doi-asserted-by":"publisher","DOI":"10.1038\/nmeth.3047"},{"key":"2024040415500652000_2024.04.01.587366v1.48","doi-asserted-by":"crossref","first-page":"96","DOI":"10.1089\/hgtb.2017.198","article-title":"Determination + of Lentiviral Infectious Titer by a Novel Droplet Digital PCR Method","volume":"29","year":"2018","journal-title":"Hum. + Gene Ther. Methods"},{"key":"2024040415500652000_2024.04.01.587366v1.49","doi-asserted-by":"publisher","DOI":"10.1186\/s13059-020-02091-3"},{"key":"2024040415500652000_2024.04.01.587366v1.50","doi-asserted-by":"publisher","DOI":"10.1186\/s13073-021-00835-9"}],"link":[{"URL":"https:\/\/syndication.highwire.org\/content\/doi\/10.1101\/2024.04.01.587366","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,4,4]],"date-time":"2024-04-04T22:50:19Z","timestamp":1712271019000},"score":61.46666,"resource":{"primary":{"URL":"http:\/\/biorxiv.org\/lookup\/doi\/10.1101\/2024.04.01.587366"}},"issued":{"date-parts":[[2024,4,1]]},"references-count":50,"URL":"http:\/\/dx.doi.org\/10.1101\/2024.04.01.587366","published":{"date-parts":[[2024,4,1]]},"subtype":"preprint"}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -739,11 +973,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "2523" + - "3246" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:11 GMT + - Wed, 04 Sep 2024 22:52:17 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -767,17 +1001,14 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1038%2Fs42256-024-00832-8/transform/application/x-bibtex + uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2/transform/application/x-bibtex response: body: string: - " @article{M_Bran_2024, title={Augmenting large language models with - chemistry tools}, volume={6}, ISSN={2522-5839}, url={http://dx.doi.org/10.1038/s42256-024-00832-8}, - DOI={10.1038/s42256-024-00832-8}, number={5}, journal={Nature Machine Intelligence}, - publisher={Springer Science and Business Media LLC}, author={M. Bran, Andres - and Cox, Sam and Schilter, Oliver and Baldassari, Carlo and White, Andrew - D. and Schwaller, Philippe}, year={2024}, month=may, pages={525\u2013535} - }\n" + " @article{2023, volume={1962}, ISSN={1179-2051}, url={http://dx.doi.org/10.1007/s40278-023-41815-2}, + DOI={10.1007/s40278-023-41815-2}, number={1}, journal={Reactions Weekly}, + publisher={Springer Science and Business Media LLC}, year={2023}, month=jun, + pages={145\u2013145} }\n" headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -789,7 +1020,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 19:48:11 GMT + - Wed, 04 Sep 2024 22:52:17 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -813,30 +1044,158 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works?mailto=test@example.com&query.title=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&rows=1 + uri: https://api.crossref.org/works?mailto=test@example.com&query.title=An+essential+role+of+active+site+arginine+residue+in+iodide+binding+and+histidine+residue+in+electron+transfer+for+iodide+oxidation+by+horseradish+peroxidase&rows=1 response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":11734920,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP - Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office - of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide - layers are likely to be present on metallic nano-structures due to either - environmental exposure during use, or high temperature processing techniques - such as annealing. It is well known that nano-structured metals have vastly - different mechanical properties from bulk metals; however, difficulties in - modeling the transition between metallic and ionic bonding have prevented - the computational investigation of the effects of oxide surface layers. Newly - developed charge-optimized many body [Liang et al., Mater. Sci. Eng., R 74, - 255 (2013)] potentials are used to perform fully reactive molecular dynamics - simulations which elucidate the effects that metal-oxide layers have on the - mechanical properties of a copper thin-film. Simulated tensile tests are performed - on thin-films while using different strain-rates, temperatures, and oxide - thicknesses to evaluate changes in yield stress, modulus, and failure mechanisms. - Findings indicate that copper-thin film mechanical properties are strongly - affected by native oxide layers. The formed oxide layers have an amorphous - structure with lower Cu-O bond-densities than bulk CuO, and a mixture of Cu2O - and CuO charge character. It is found that oxidation will cause modifications - to the strain response of the elastic modulii, producing a stiffened modulii + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":4075855,"items":[{"indexed":{"date-parts":[[2024,6,24]],"date-time":"2024-06-24T23:08:06Z","timestamp":1719270486276},"reference-count":40,"publisher":"Elsevier + BV","issue":"14","license":[{"start":{"date-parts":[[1992,5,1]],"date-time":"1992-05-01T00:00:00Z","timestamp":704678400000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.elsevier.com\/tdm\/userlicense\/1.0\/"},{"start":{"date-parts":[[2020,10,3]],"date-time":"2020-10-03T00:00:00Z","timestamp":1601683200000},"content-version":"vor","delay-in-days":10382,"URL":"http:\/\/creativecommons.org\/licenses\/by\/4.0\/"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Journal + of Biological Chemistry"],"published-print":{"date-parts":[[1992,5]]},"DOI":"10.1016\/s0021-9258(19)50164-8","type":"journal-article","created":{"date-parts":[[2021,1,6]],"date-time":"2021-01-06T15:57:43Z","timestamp":1609948663000},"page":"9800-9804","source":"Crossref","is-referenced-by-count":23,"title":["Chemical + and kinetic evidence for an essential histidine in horseradish peroxidase + for iodide oxidation."],"prefix":"10.1016","volume":"267","author":[{"given":"D.K.","family":"Bhattacharyya","sequence":"first","affiliation":[]},{"given":"U","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"R.K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"78","reference":[{"key":"10.1016\/S0021-9258(19)50164-8_bib1","doi-asserted-by":"crossref","first-page":"531","DOI":"10.1093\/oxfordjournals.jbchem.a133961","volume":"92","author":"Aibara","year":"1982","journal-title":"J. + Biochem. (Tokyo)"},{"key":"10.1016\/S0021-9258(19)50164-8_bib2","doi-asserted-by":"crossref","first-page":"341","DOI":"10.1016\/0003-2697(62)90097-0","volume":"4","author":"Alexander","year":"1962","journal-title":"Anal. + Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib3","doi-asserted-by":"crossref","first-page":"10592","DOI":"10.1016\/S0021-9258(18)67426-5","volume":"261","author":"Banerjee","year":"1986","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib4","doi-asserted-by":"crossref","first-page":"396","DOI":"10.1016\/0005-2744(70)90245-7","volume":"212","author":"Bjorkstein","year":"1970","journal-title":"Biochim. + Biophys. Acta"},{"key":"10.1016\/S0021-9258(19)50164-8_bib5","doi-asserted-by":"crossref","first-page":"12454","DOI":"10.1016\/S0021-9258(19)38367-X","volume":"265","author":"Blanke","year":"1990","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib6","doi-asserted-by":"crossref","first-page":"205","DOI":"10.1021\/bi00698a030","volume":"13","author":"Burstein","year":"1974","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib7","doi-asserted-by":"crossref","first-page":"19666","DOI":"10.1016\/S0021-9258(19)47165-2","volume":"264","author":"Chang","year":"1989","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib8","doi-asserted-by":"crossref","first-page":"4936","DOI":"10.1016\/S0021-9258(18)89162-1","volume":"260","author":"Church","year":"1985","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib9","doi-asserted-by":"crossref","first-page":"4992","DOI":"10.1021\/bi00668a008","volume":"15","author":"Cousineau","year":"1976","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib10","doi-asserted-by":"crossref","first-page":"21498","DOI":"10.1016\/S0021-9258(18)45766-3","volume":"265","author":"Dumas","year":"1990","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib11","first-page":"410","volume":"4","author":"Dunford","year":"1982","journal-title":"Adv. + Inorg. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib12","doi-asserted-by":"crossref","first-page":"187","DOI":"10.1016\/S0010-8545(00)80316-1","volume":"19","author":"Dunford","year":"1976","journal-title":"Coord. + Chem. Rev."},{"key":"10.1016\/S0021-9258(19)50164-8_bib13","doi-asserted-by":"crossref","first-page":"21","DOI":"10.1016\/0022-2836(85)90180-9","volume":"185","author":"Fita","year":"1985","journal-title":"J. + Mol. Biol."},{"key":"10.1016\/S0021-9258(19)50164-8_bib14","doi-asserted-by":"crossref","first-page":"15054","DOI":"10.1016\/S0021-9258(18)33392-1","volume":"257","author":"Kaput","year":"1982","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib15","doi-asserted-by":"crossref","first-page":"409","DOI":"10.1016\/0003-9861(87)90118-4","volume":"254","author":"Kenigsberg","year":"1987","journal-title":"Arch. + Biochem. Biophys."},{"key":"10.1016\/S0021-9258(19)50164-8_bib16","doi-asserted-by":"crossref","first-page":"55","DOI":"10.1016\/0003-9861(88)90103-8","volume":"261","author":"Konpka","year":"1988","journal-title":"Arch. + Biochem. Biophys."},{"key":"10.1016\/S0021-9258(19)50164-8_bib17","doi-asserted-by":"crossref","first-page":"3654","DOI":"10.1016\/S0021-9258(19)75322-8","volume":"238","author":"Levy","year":"1963","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib18","volume":"1","author":"Lundblad","year":"1984"},{"key":"10.1016\/S0021-9258(19)50164-8_bib19","doi-asserted-by":"crossref","first-page":"197","DOI":"10.1016\/S0021-9258(17)43641-6","volume":"259","author":"Magnusson","year":"1984","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib20","doi-asserted-by":"crossref","first-page":"251","DOI":"10.1021\/bi00804a010","volume":"9","author":"Melchior","year":"1970","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib21","doi-asserted-by":"crossref","first-page":"431","DOI":"10.1016\/0076-6879(77)47043-5","volume":"47","author":"Miles","year":"1977","journal-title":"Methods + Enzymol."},{"key":"10.1016\/S0021-9258(19)50164-8_bib22","doi-asserted-by":"crossref","first-page":"861","DOI":"10.1146\/annurev.bi.45.070176.004241","volume":"45","author":"Morrison","year":"1976","journal-title":"Annu. + Rev. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib23","doi-asserted-by":"crossref","first-page":"13546","DOI":"10.1016\/S0021-9258(17)38757-4","volume":"260","author":"Nakamura","year":"1985","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib24","doi-asserted-by":"crossref","first-page":"805","DOI":"10.1016\/S0021-9258(19)70048-9","volume":"256","author":"Ohtaki","year":"1981","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib25","doi-asserted-by":"crossref","first-page":"761","DOI":"10.1016\/S0021-9258(19)68261-X","volume":"257","author":"Ohtaki","year":"1982","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib26","first-page":"445","volume":"2","author":"Ovadi","year":"1967","journal-title":"Acta + Biochim. Biophys. Acad. Sci. Hung."},{"key":"10.1016\/S0021-9258(19)50164-8_bib27","doi-asserted-by":"crossref","first-page":"395","DOI":"10.3891\/acta.chem.scand.32b-0395","volume":"B32","author":"Paul","year":"1978","journal-title":"Acta + Chem. Scand."},{"key":"10.1016\/S0021-9258(19)50164-8_bib28","doi-asserted-by":"crossref","first-page":"497","DOI":"10.1111\/j.1432-1033.1973.tb03085.x","volume":"38","author":"Pommier","year":"1973","journal-title":"Eur. + J. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib29","doi-asserted-by":"crossref","first-page":"8199","DOI":"10.1016\/S0021-9258(19)70630-9","volume":"255","author":"Poulos","year":"1980","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib30","doi-asserted-by":"crossref","first-page":"2076","DOI":"10.1021\/bi00761a013","volume":"11","author":"Roman","year":"1972","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib31","doi-asserted-by":"crossref","first-page":"211","DOI":"10.1246\/cl.1985.211","author":"Sakurada","year":"1985","journal-title":"Chem. + Lett."},{"key":"10.1016\/S0021-9258(19)50164-8_bib32","doi-asserted-by":"crossref","first-page":"9657","DOI":"10.1016\/S0021-9258(18)67564-7","volume":"261","author":"Sakurada","year":"1986","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib33","doi-asserted-by":"crossref","first-page":"4007","DOI":"10.1016\/S0021-9258(18)61303-1","volume":"262","author":"Sakurada","year":"1987","journal-title":"J. + Biol. Chem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib34","doi-asserted-by":"crossref","first-page":"6478","DOI":"10.1021\/bi00394a028","volume":"26","author":"Sakurada","year":"1987","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib35","doi-asserted-by":"crossref","first-page":"2277","DOI":"10.1021\/bi00407a005","volume":"27","author":"Sams","year":"1988","journal-title":"Biochemistry"},{"key":"10.1016\/S0021-9258(19)50164-8_bib36","doi-asserted-by":"crossref","first-page":"1571","DOI":"10.1093\/oxfordjournals.jbchem.a135630","volume":"99","author":"Takeuchi","year":"1986","journal-title":"J. + Biochem. (Tokyo)"},{"key":"10.1016\/S0021-9258(19)50164-8_bib37","doi-asserted-by":"crossref","first-page":"520","DOI":"10.1038\/326520a0","volume":"326","author":"Tien","year":"1987","journal-title":"Nature"},{"key":"10.1016\/S0021-9258(19)50164-8_bib38","doi-asserted-by":"crossref","first-page":"87","DOI":"10.1111\/j.1432-1033.1986.tb09461.x","volume":"155","author":"Topham","year":"1986","journal-title":"Eur. + J. Biochem."},{"key":"10.1016\/S0021-9258(19)50164-8_bib39","doi-asserted-by":"crossref","first-page":"210","DOI":"10.1016\/0005-2744(81)90032-2","volume":"662","author":"Ugarova","year":"1981","journal-title":"Biochim. + Biophys. Acta"},{"key":"10.1016\/S0021-9258(19)50164-8_bib40","doi-asserted-by":"crossref","first-page":"483","DOI":"10.1111\/j.1432-1033.1979.tb13061.x","volume":"96","author":"Welinder","year":"1979","journal-title":"Eur. + J. Biochem."}],"container-title":["Journal of Biological Chemistry"],"language":"en","link":[{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0021925819501648?httpAccept=text\/xml","content-type":"text\/xml","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/api.elsevier.com\/content\/article\/PII:S0021925819501648?httpAccept=text\/plain","content-type":"text\/plain","content-version":"vor","intended-application":"text-mining"}],"deposited":{"date-parts":[[2022,1,2]],"date-time":"2022-01-02T19:58:22Z","timestamp":1641153502000},"score":52.803257,"resource":{"primary":{"URL":"https:\/\/linkinghub.elsevier.com\/retrieve\/pii\/S0021925819501648"}},"issued":{"date-parts":[[1992,5]]},"references-count":40,"journal-issue":{"issue":"14","published-print":{"date-parts":[[1992,5]]}},"alternative-id":["S0021925819501648"],"URL":"http:\/\/dx.doi.org\/10.1016\/s0021-9258(19)50164-8","ISSN":["0021-9258"],"issn-type":[{"value":"0021-9258","type":"print"}],"published":{"date-parts":[[1992,5]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Link + Connection: + - close + Content-Encoding: + - gzip + Content-Length: + - "2524" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:17 GMT + Server: + - Jetty(9.4.40.v20210413) + Vary: + - Accept-Encoding + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works/10.1038%2Fs42256-024-00832-8/transform/application/x-bibtex + response: + body: + string: + " @article{M_Bran_2024, title={Augmenting large language models with + chemistry tools}, volume={6}, ISSN={2522-5839}, url={http://dx.doi.org/10.1038/s42256-024-00832-8}, + DOI={10.1038/s42256-024-00832-8}, number={5}, journal={Nature Machine Intelligence}, + publisher={Springer Science and Business Media LLC}, author={M. Bran, Andres + and Cox, Sam and Schilter, Oliver and Baldassari, Carlo and White, Andrew + D. and Schwaller, Philippe}, year={2024}, month=may, pages={525\u2013535} + }\n" + headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Link + Connection: + - close + Date: + - Wed, 04 Sep 2024 22:52:17 GMT + Server: + - Jetty(9.4.40.v20210413) + Transfer-Encoding: + - chunked + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works?mailto=test@example.com&query.title=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&rows=1 + response: + body: + string: + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":11790075,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP + Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office + of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"],"id":[{"id":"10.13039\/100000006","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide + layers are likely to be present on metallic nano-structures due to either + environmental exposure during use, or high temperature processing techniques + such as annealing. It is well known that nano-structured metals have vastly + different mechanical properties from bulk metals; however, difficulties in + modeling the transition between metallic and ionic bonding have prevented + the computational investigation of the effects of oxide surface layers. Newly + developed charge-optimized many body [Liang et al., Mater. Sci. Eng., R 74, + 255 (2013)] potentials are used to perform fully reactive molecular dynamics + simulations which elucidate the effects that metal-oxide layers have on the + mechanical properties of a copper thin-film. Simulated tensile tests are performed + on thin-films while using different strain-rates, temperatures, and oxide + thicknesses to evaluate changes in yield stress, modulus, and failure mechanisms. + Findings indicate that copper-thin film mechanical properties are strongly + affected by native oxide layers. The formed oxide layers have an amorphous + structure with lower Cu-O bond-densities than bulk CuO, and a mixture of Cu2O + and CuO charge character. It is found that oxidation will cause modifications + to the strain response of the elastic modulii, producing a stiffened modulii at low temperatures (&lt;75\u2009K) and low strain values (&lt;5%), and a softened modulii at higher temperatures. While under strain, structural reorganization within the oxide layers facilitates brittle yielding through @@ -911,7 +1270,7 @@ interactions: Sci. Eng. A"},{"key":"2023062402360541600_c55","doi-asserted-by":"publisher","first-page":"057129","DOI":"10.1063\/1.4880241","volume":"4","year":"2014","journal-title":"AIP Adv."},{"key":"2023062402360541600_c56","doi-asserted-by":"publisher","first-page":"94","DOI":"10.1016\/j.susc.2014.10.017","volume":"633","year":"2015","journal-title":"Surf. Sci."},{"key":"2023062402360541600_c57","doi-asserted-by":"publisher","first-page":"710","DOI":"10.1016\/j.pmatsci.2010.04.001","volume":"55","year":"2010","journal-title":"Prog. - Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":64.01897,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":64.01529,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -925,11 +1284,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "3928" + - "3945" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 19:48:11 GMT + - Wed, 04 Sep 2024 22:52:17 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -975,7 +1334,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 19:48:11 GMT + - Wed, 04 Sep 2024 22:52:17 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -1022,7 +1381,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 19:48:12 GMT + - Wed, 04 Sep 2024 22:52:17 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -1042,363 +1401,4 @@ interactions: status: code: 200 message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Convalescent-anti-sars-cov-2-plasma/immune-globulin&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "762ceacc20db232d3f6bcda6e867eb8148848ced", "externalIds": - {"DOI": "10.1007/s40278-023-33114-1", "CorpusId": 256742540}, "url": "https://www.semanticscholar.org/paper/762ceacc20db232d3f6bcda6e867eb8148848ced", - "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions - weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, - "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": null, "publicationDate": - "2023-02-01", "journal": {"name": "Reactions Weekly", "pages": "229", "volume": - "1943"}, "citationStyles": {"bibtex": "@Article{None,\n booktitle = {Reactions - weekly},\n journal = {Reactions Weekly},\n pages = {229},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n - volume = {1943},\n year = {2023}\n}\n"}, "authors": [], "matchScore": 240.53656}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "848" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:15 GMT - Via: - - 1.1 0af050b863ec46156a524df4e5d86692.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - g4O7sGb3PiXZV-fPpPVE2G2GffyPMijX4lQfgf2rRH3d7Ok9Q0xmYQ== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDJHo3vHcEhTA= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "848" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:15 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - 40059075-3a1a-43b0-9c73-d57c5a549333 - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Augmenting+large+language+models+with+chemistry+tools&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", "externalIds": - {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": - "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": - "38799228"}, "url": "https://www.semanticscholar.org/paper/354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", - "title": "Augmenting large language models with chemistry tools", "venue": - "Nat. Mac. Intell.", "year": 2023, "citationCount": 187, "influentialCitationCount": - 12, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.nature.com/articles/s42256-024-00832-8.pdf", - "status": "HYBRID"}, "publicationTypes": ["JournalArticle"], "publicationDate": - "2023-04-11", "journal": {"name": "Nature Machine Intelligence", "pages": - "525 - 535", "volume": "6"}, "citationStyles": {"bibtex": "@Article{Bran2023AugmentingLL,\n - author = {Andr\u00e9s M Bran and Sam Cox and Oliver Schilter and Carlo Baldassari - and Andrew D. White and P. Schwaller},\n booktitle = {Nat. Mac. Intell.},\n - journal = {Nature Machine Intelligence},\n pages = {525 - 535},\n title = - {Augmenting large language models with chemistry tools},\n volume = {6},\n - year = {2023}\n}\n"}, "authors": [{"authorId": "2216007369", "name": "Andr\u00e9s - M Bran"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", - "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, - {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", - "name": "P. Schwaller"}], "matchScore": 181.36966}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1551" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:15 GMT - Via: - - 1.1 8e6324c5a68bac8fd8e6eead6a5b73f2.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - zuVPj6yMN5rCD2R7HcLxHy8x-kLnwMahQ7G8pfSyNiFDT0cWbvfmNw== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDJEUTPHcEmug= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1551" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:15 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - e745ac14-504e-4348-aa4a-06e390fcc9bf - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=An+essential+role+of+active+site+arginine+residue+in+iodide+binding+and+histidine+residue+in+electron+transfer+for+iodide+oxidation+by+horseradish+peroxidase&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "19807da5b11f3e641535cb72e465001b49b48ee5", "externalIds": - {"MAG": "1554322594", "DOI": "10.1023/A:1007154515475", "CorpusId": 22646521, - "PubMed": "11330823"}, "url": "https://www.semanticscholar.org/paper/19807da5b11f3e641535cb72e465001b49b48ee5", - "title": "An essential role of active site arginine residue in iodide binding - and histidine residue in electron transfer for iodide oxidation by horseradish - peroxidase", "venue": "Molecular and Cellular Biochemistry", "year": 2001, - "citationCount": 7, "influentialCitationCount": 0, "isOpenAccess": false, - "openAccessPdf": null, "publicationTypes": ["JournalArticle", "Study"], "publicationDate": - "2001-02-01", "journal": {"name": "Molecular and Cellular Biochemistry", "pages": - "1-11", "volume": "218"}, "citationStyles": {"bibtex": "@Article{Adak2001AnER,\n - author = {S. Adak and D. Bandyopadhyay and U. Bandyopadhyay and R. Banerjee},\n - booktitle = {Molecular and Cellular Biochemistry},\n journal = {Molecular - and Cellular Biochemistry},\n pages = {1-11},\n title = {An essential role - of active site arginine residue in iodide binding and histidine residue in - electron transfer for iodide oxidation by horseradish peroxidase},\n volume - = {218},\n year = {2001}\n}\n"}, "authors": [{"authorId": "1940081", "name": - "S. Adak"}, {"authorId": "1701389", "name": "D. Bandyopadhyay"}, {"authorId": - "5343877", "name": "U. Bandyopadhyay"}, {"authorId": "32656528", "name": "R. - Banerjee"}], "matchScore": 386.0899}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1482" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:16 GMT - Via: - - 1.1 e7803a00a023f1e04faef1ed4f572ace.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - MMShBSHvjIEqV7dMMopd69PyMoIyLOGwR7EaBoeO2wSrkk2CLTEgXw== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDJGY-PHcECwg= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1482" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:16 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - 40970c02-4f72-4951-8233-7b2bc06ca357 - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": - {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, - "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", - "title": "Effect of native oxide layers on copper thin-film tensile properties: - A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": - 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": - null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": - {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, - "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = - {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n - pages = {235306},\n title = {Effect of native oxide layers on copper thin-film - tensile properties: A reactive molecular dynamics study},\n volume = {118},\n - year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael - Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": - 283.86478}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1109" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:16 GMT - Via: - - 1.1 6d5b0fa46ef77b2ff227bdbcee6603ee.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - eg4X8O8nSOfFymtbAfahuzJx5flTjiyknQB-4WuqRhsMZ_qwftt2Cw== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDKElMPHcEsng= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1109" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:16 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - de32ffb2-6693-4732-b687-c858492bf043 - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=High-throughput+screening+of+human+genetic+variants+by+pooled+prime+editing&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": - {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", - "title": "High-throughput screening of human genetic variants by pooled prime - editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": - 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", - "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", - "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n - author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana - Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n - journal = {bioRxiv},\n title = {High-throughput screening of human genetic - variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": - "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": - "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, - {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", - "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}], - "matchScore": 252.66568}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1362" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:18 GMT - Via: - - 1.1 ce05e2e2ef149c875905ee7ff636fb28.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - k9mONTTlp728qjoElst9l23YQPE_wNuyrPFQFHTpquZDsDqGWZbRuA== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDLHnCvHcEWDg= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1362" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:18 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - 9a08224c-83b7-4b80-b178-8112100b71c0 - status: - code: 200 - message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=PaperQA:+Retrieval-Augmented+Generative+Agent+for+Scientific+Research&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"data": [{"paperId": "7e55d8701785818776323b4147cb13354c820469", "externalIds": - {"ArXiv": "2312.07559", "DBLP": "journals/corr/abs-2312-07559", "DOI": "10.48550/arXiv.2312.07559", - "CorpusId": 266191420}, "url": "https://www.semanticscholar.org/paper/7e55d8701785818776323b4147cb13354c820469", - "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", - "venue": "arXiv.org", "year": 2023, "citationCount": 21, "influentialCitationCount": - 1, "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], - "publicationDate": "2023-12-08", "journal": {"name": "ArXiv", "volume": "abs/2312.07559"}, - "citationStyles": {"bibtex": "@Article{L''ala2023PaperQARG,\n author = {Jakub - L''ala and Odhran O''Donoghue and Aleksandar Shtedritski and Sam Cox and Samuel - G. Rodriques and Andrew D. White},\n booktitle = {arXiv.org},\n journal = - {ArXiv},\n title = {PaperQA: Retrieval-Augmented Generative Agent for Scientific - Research},\n volume = {abs/2312.07559},\n year = {2023}\n}\n"}, "authors": - [{"authorId": "2219926382", "name": "Jakub L''ala"}, {"authorId": "2258961056", - "name": "Odhran O''Donoghue"}, {"authorId": "2258961451", "name": "Aleksandar - Shtedritski"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": - "2258964497", "name": "Samuel G. Rodriques"}, {"authorId": "2273941271", "name": - "Andrew D. White"}], "matchScore": 245.13055}]} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "1386" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 19:48:20 GMT - Via: - - 1.1 ddd3d8441374ce62d11d031216138152.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - PNlpdqVj19cLnEc8Et0E_OPukDYcChYs_uxr6On1Jg9ze-bDq8iwfA== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdnDJHF0vHcEftw= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1386" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 19:48:20 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - 85aa5394-fef3-4c7c-b0a4-7d862f56bd92 - status: - code: 200 - message: OK version: 1 diff --git a/tests/cassettes/test_crossref_journalquality_fields_filtering.yaml b/tests/cassettes/test_crossref_journalquality_fields_filtering.yaml index 3b29e77d..633328e0 100644 --- a/tests/cassettes/test_crossref_journalquality_fields_filtering.yaml +++ b/tests/cassettes/test_crossref_journalquality_fields_filtering.yaml @@ -7,7 +7,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283974,"items":[{"DOI":"10.1038\/s42256-024-00832-8","author":[{"given":"Andres","family":"M. + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"DOI":"10.1038\/s42256-024-00832-8","author":[{"given":"Andres","family":"M. Bran","sequence":"first","affiliation":[]},{"given":"Sam","family":"Cox","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-0310-0851","authenticated-orcid":false,"given":"Oliver","family":"Schilter","sequence":"additional","affiliation":[]},{"given":"Carlo","family":"Baldassari","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-6647-3965","authenticated-orcid":false,"given":"Andrew D.","family":"White","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-3046-6576","authenticated-orcid":false,"given":"Philippe","family":"Schwaller","sequence":"additional","affiliation":[]}],"container-title":["Nature Machine Intelligence"],"title":["Augmenting large language models with chemistry @@ -25,11 +25,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "480" + - "479" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:44 GMT + - Wed, 04 Sep 2024 22:52:23 GMT Server: - Jetty(9.4.40.v20210413) Vary: diff --git a/tests/cassettes/test_doi_search[paper_attributes0].yaml b/tests/cassettes/test_doi_search[paper_attributes0].yaml index ed575f41..6b1867f9 100644 --- a/tests/cassettes/test_doi_search[paper_attributes0].yaml +++ b/tests/cassettes/test_doi_search[paper_attributes0].yaml @@ -1,4 +1,63 @@ interactions: + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1101/2024.04.01.587366?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": + {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", + "title": "High-throughput screening of human genetic variants by pooled prime + editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": + 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", + "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n + author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana + Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n + journal = {bioRxiv},\n title = {High-throughput screening of human genetic + variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": + "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": + "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, + {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", + "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1325" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:22 GMT + Via: + - 1.1 c3d007e42510cc2bd48d2a205774e488.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - I6tmEWVtUOXx8vgyBUlVQsGXL5_gZzTTOWmdPYOnc4TgsGLCVr1Ndg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmi82FjxPHcEaaA= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1325" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:54:22 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - edeab9f8-504e-4ac9-b8fd-13af5e37ff5d + status: + code: 200 + message: OK - request: body: null headers: {} @@ -71,7 +130,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:22 GMT + - Wed, 04 Sep 2024 22:54:22 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -95,58 +154,72 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1101/2024.04.01.587366?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.unpaywall.org/v2/10.1101/2024.04.01.587366?email=test@example.com response: body: string: - '{"paperId": "7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", "externalIds": - {"DOI": "10.1101/2024.04.01.587366", "CorpusId": 268890006}, "url": "https://www.semanticscholar.org/paper/7e5d4466c8b85f93775fe183e1a318a3e65ac8e4", + '{"doi": "10.1101/2024.04.01.587366", "doi_url": "https://doi.org/10.1101/2024.04.01.587366", "title": "High-throughput screening of human genetic variants by pooled prime - editing", "venue": "bioRxiv", "year": 2024, "citationCount": 1, "influentialCitationCount": - 0, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", - "status": "GREEN"}, "publicationTypes": null, "publicationDate": "2024-04-01", - "journal": {"name": "bioRxiv"}, "citationStyles": {"bibtex": "@Article{Herger2024HighthroughputSO,\n - author = {Michael Herger and Christina M. Kajba and Megan Buckley and Ana - Cunha and Molly Strom and Gregory M. Findlay},\n booktitle = {bioRxiv},\n - journal = {bioRxiv},\n title = {High-throughput screening of human genetic - variants by pooled prime editing},\n year = {2024}\n}\n"}, "authors": [{"authorId": - "2294884120", "name": "Michael Herger"}, {"authorId": "2163800172", "name": - "Christina M. Kajba"}, {"authorId": "2120283350", "name": "Megan Buckley"}, - {"authorId": "2294861709", "name": "Ana Cunha"}, {"authorId": "2294881320", - "name": "Molly Strom"}, {"authorId": "145686550", "name": "Gregory M. Findlay"}]} - - ' + editing", "genre": "posted-content", "is_paratext": false, "published_date": + "2024-04-01", "year": 2024, "journal_name": null, "journal_issns": null, "journal_issn_l": + null, "journal_is_oa": false, "journal_is_in_doaj": false, "publisher": "Cold + Spring Harbor Laboratory", "is_oa": true, "oa_status": "green", "has_repository_copy": + true, "best_oa_location": {"updated": "2024-04-03T02:38:26.107690", "url": + "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_pdf": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_landing_page": "https://doi.org/10.1101/2024.04.01.587366", "evidence": + "oa repository (via page says license)", "license": "cc-by", "version": "submittedVersion", + "host_type": "repository", "is_best": true, "pmh_id": null, "endpoint_id": + null, "repository_institution": null, "oa_date": "2024-04-01"}, "first_oa_location": + {"updated": "2024-04-03T02:38:26.107690", "url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_pdf": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_landing_page": "https://doi.org/10.1101/2024.04.01.587366", "evidence": + "oa repository (via page says license)", "license": "cc-by", "version": "submittedVersion", + "host_type": "repository", "is_best": true, "pmh_id": null, "endpoint_id": + null, "repository_institution": null, "oa_date": "2024-04-01"}, "oa_locations": + [{"updated": "2024-04-03T02:38:26.107690", "url": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_pdf": "https://www.biorxiv.org/content/biorxiv/early/2024/04/01/2024.04.01.587366.full.pdf", + "url_for_landing_page": "https://doi.org/10.1101/2024.04.01.587366", "evidence": + "oa repository (via page says license)", "license": "cc-by", "version": "submittedVersion", + "host_type": "repository", "is_best": true, "pmh_id": null, "endpoint_id": + null, "repository_institution": null, "oa_date": "2024-04-01"}], "oa_locations_embargoed": + [], "updated": "2024-04-03T02:38:52.695109", "data_standard": 2, "z_authors": + [{"given": "Michael", "family": "Herger", "sequence": "first"}, {"given": + "Christina M.", "family": "Kajba", "sequence": "additional"}, {"given": "Megan", + "family": "Buckley", "sequence": "additional"}, {"given": "Ana", "family": + "Cunha", "sequence": "additional"}, {"given": "Molly", "family": "Strom", + "sequence": "additional"}, {"ORCID": "http://orcid.org/0000-0002-7767-8608", + "given": "Gregory M.", "family": "Findlay", "sequence": "additional", "authenticated-orcid": + false}]}' headers: + Access-Control-Allow-Headers: + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH Access-Control-Allow-Origin: - "*" Connection: - keep-alive + Content-Encoding: + - gzip Content-Length: - - "1325" + - "736" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:51:22 GMT - Via: - - 1.1 d1dad7d3c339d87d553c26a84c9ca5d2.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - YdElWpMCzPZBBVcmqso_pKXH0JZf-c3i0htHXzE-jvK940UiDB0TfQ== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdeutGf0PHcEffA= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1325" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:51:22 GMT - x-amzn-Remapped-Server: + - Wed, 04 Sep 2024 22:54:22 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490462&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=odE5ZlVPcetyhAsFl%2FsK%2BfqBlOzg14I8aiMcemLe%2FTM%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490462&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=odE5ZlVPcetyhAsFl%2FsK%2BfqBlOzg14I8aiMcemLe%2FTM%3D + Server: - gunicorn - x-amzn-RequestId: - - 62d15057-610f-4f42-be58-3d85daa4834e + Vary: + - Accept-Encoding + Via: + - 1.1 vegur status: code: 200 message: OK @@ -176,7 +249,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:51:23 GMT + - Wed, 04 Sep 2024 22:54:23 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_doi_search[paper_attributes1].yaml b/tests/cassettes/test_doi_search[paper_attributes1].yaml index 067c8feb..d31d9bf0 100644 --- a/tests/cassettes/test_doi_search[paper_attributes1].yaml +++ b/tests/cassettes/test_doi_search[paper_attributes1].yaml @@ -1,53 +1,4 @@ interactions: - - request: - body: null - headers: {} - method: GET - uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475?mailto=test@example.com - response: - body: - string: - '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,1,21]],"date-time":"2024-01-21T17:53:48Z","timestamp":1705859628791},"reference-count":0,"publisher":"Springer - Science and Business Media LLC","issue":"1\/2","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"published-print":{"date-parts":[[2001]]},"DOI":"10.1023\/a:1007154515475","type":"journal-article","created":{"date-parts":[[2002,12,22]],"date-time":"2002-12-22T09:07:15Z","timestamp":1040548035000},"page":"1-11","source":"Crossref","is-referenced-by-count":6,"title":[],"prefix":"10.1007","volume":"218","author":[{"given":"Subrata","family":"Adak","sequence":"first","affiliation":[]},{"given":"Debashis","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Uday","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Ranajit - K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"297","container-title":["Molecular - and Cellular Biochemistry"],"original-title":[],"deposited":{"date-parts":[[2012,12,27]],"date-time":"2012-12-27T23:10:34Z","timestamp":1356649834000},"score":1,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1023\/A:1007154515475"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2001]]},"references-count":0,"journal-issue":{"issue":"1\/2"},"alternative-id":["271450"],"URL":"http:\/\/dx.doi.org\/10.1023\/a:1007154515475","relation":{},"ISSN":["0300-8177"],"issn-type":[{"value":"0300-8177","type":"print"}],"subject":[],"published":{"date-parts":[[2001]]}}}' - headers: - Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control - Access-Control-Allow-Origin: - - "*" - Access-Control-Expose-Headers: - - Link - Connection: - - close - Content-Encoding: - - gzip - Content-Length: - - "762" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 18:34:01 GMT - Server: - - Jetty(9.4.40.v20210413) - Vary: - - Accept-Encoding - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" - status: - code: 200 - message: OK - request: body: null headers: {} @@ -87,27 +38,76 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:02 GMT + - Wed, 04 Sep 2024 22:54:23 GMT Via: - - 1.1 ef066a0102f66b719933dbbef3bc5968.cloudfront.net (CloudFront) + - 1.1 9d47bedfe2d83b5ed14a7d02ea9ca902.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - UTYNrOWx6hAF2oDHtNJriKFUfyI5WuhHnu6OER07rQycY9GGvhAXhg== + - PIxlQLXqDglOwKmu8opGS0LLEvatR2WfeqUMkLIgyZ2PgnWFLJbXOQ== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcMHEwsvHcEB4w= + - dmi9BHbsPHcElCw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "1446" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:02 GMT + - Wed, 04 Sep 2024 22:54:23 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 1c51e9d8-caf7-4a06-ad5d-815a80bd1356 + - e159dae5-b176-4415-a6a8-689d92cf88d4 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works/10.1023%2Fa:1007154515475?mailto=test@example.com + response: + body: + string: + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2024,1,21]],"date-time":"2024-01-21T17:53:48Z","timestamp":1705859628791},"reference-count":0,"publisher":"Springer + Science and Business Media LLC","issue":"1\/2","content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":[],"published-print":{"date-parts":[[2001]]},"DOI":"10.1023\/a:1007154515475","type":"journal-article","created":{"date-parts":[[2002,12,22]],"date-time":"2002-12-22T09:07:15Z","timestamp":1040548035000},"page":"1-11","source":"Crossref","is-referenced-by-count":6,"title":[],"prefix":"10.1007","volume":"218","author":[{"given":"Subrata","family":"Adak","sequence":"first","affiliation":[]},{"given":"Debashis","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Uday","family":"Bandyopadhyay","sequence":"additional","affiliation":[]},{"given":"Ranajit + K.","family":"Banerjee","sequence":"additional","affiliation":[]}],"member":"297","container-title":["Molecular + and Cellular Biochemistry"],"original-title":[],"deposited":{"date-parts":[[2012,12,27]],"date-time":"2012-12-27T23:10:34Z","timestamp":1356649834000},"score":1,"resource":{"primary":{"URL":"http:\/\/link.springer.com\/10.1023\/A:1007154515475"}},"subtitle":[],"short-title":[],"issued":{"date-parts":[[2001]]},"references-count":0,"journal-issue":{"issue":"1\/2"},"alternative-id":["271450"],"URL":"http:\/\/dx.doi.org\/10.1023\/a:1007154515475","relation":{},"ISSN":["0300-8177"],"issn-type":[{"value":"0300-8177","type":"print"}],"subject":[],"published":{"date-parts":[[2001]]}}}' + headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Link + Connection: + - close + Content-Encoding: + - gzip + Content-Length: + - "762" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:23 GMT + Server: + - Jetty(9.4.40.v20210413) + Vary: + - Accept-Encoding + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: code: 200 message: OK @@ -135,7 +135,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:34:02 GMT + - Wed, 04 Sep 2024 22:54:24 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -155,4 +155,56 @@ interactions: status: code: 200 message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.unpaywall.org/v2/10.1023/a:1007154515475?email=test@example.com + response: + body: + string: + '{"doi": "10.1023/a:1007154515475", "doi_url": "https://doi.org/10.1023/a:1007154515475", + "title": null, "genre": "journal-article", "is_paratext": false, "published_date": + "2001-01-01", "year": 2001, "journal_name": "Molecular and Cellular Biochemistry", + "journal_issns": "0300-8177", "journal_issn_l": "0300-8177", "journal_is_oa": + false, "journal_is_in_doaj": false, "publisher": "Springer Science and Business + Media LLC", "is_oa": false, "oa_status": "closed", "has_repository_copy": + false, "best_oa_location": null, "first_oa_location": null, "oa_locations": + [], "oa_locations_embargoed": [], "updated": "2021-01-15T22:46:04.723698", + "data_standard": 2, "z_authors": [{"given": "Subrata", "family": "Adak", "sequence": + "first"}, {"given": "Debashis", "family": "Bandyopadhyay", "sequence": "additional"}, + {"given": "Uday", "family": "Bandyopadhyay", "sequence": "additional"}, {"given": + "Ranajit K.", "family": "Banerjee", "sequence": "additional"}]}' + headers: + Access-Control-Allow-Headers: + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - "453" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:28 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490463&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=16xcKFCnanatZSTYbtEmrEHMRUMswP%2FH%2FhmhMexErKo%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490463&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=16xcKFCnanatZSTYbtEmrEHMRUMswP%2FH%2FhmhMexErKo%3D + Server: + - gunicorn + Vary: + - Accept-Encoding + Via: + - 1.1 vegur + status: + code: 200 + message: OK version: 1 diff --git a/tests/cassettes/test_doi_search[paper_attributes2].yaml b/tests/cassettes/test_doi_search[paper_attributes2].yaml index f728e188..bd5530e3 100644 --- a/tests/cassettes/test_doi_search[paper_attributes2].yaml +++ b/tests/cassettes/test_doi_search[paper_attributes2].yaml @@ -1,4 +1,107 @@ interactions: + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": + {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "url": "https://www.semanticscholar.org/paper/e0d2719e49ad216f98ed640864cdacd1c20f53e6", + "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions + weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, + "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], + "publicationDate": "2023-06-01", "journal": {"name": "Reactions Weekly", "pages": + "145 - 145", "volume": "1962"}, "citationStyles": {"bibtex": "@Article{None,\n + booktitle = {Reactions weekly},\n journal = {Reactions Weekly},\n pages = + {145 - 145},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n + volume = {1962},\n year = {2023}\n}\n"}, "authors": []} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "837" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:28 GMT + Via: + - 1.1 01dbe7e23991a177a7cdfba5803db41c.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - uRmNn23CJGWRi9ZYCvXKxbn6Gh9RBXv1V2O6VFwf_fJ8AEyurMVRzw== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmi9xEkCPHcEa4w= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "837" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:54:28 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - be736639-2cc3-46ef-bb7f-ce9d4abc9180 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.unpaywall.org/v2/10.1007/s40278-023-41815-2?email=test@example.com + response: + body: + string: + '{"doi": "10.1007/s40278-023-41815-2", "doi_url": "https://doi.org/10.1007/s40278-023-41815-2", + "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "genre": "journal-article", + "is_paratext": false, "published_date": "2023-06-24", "year": 2023, "journal_name": + "Reactions Weekly", "journal_issns": "1179-2051", "journal_issn_l": "0114-9954", + "journal_is_oa": false, "journal_is_in_doaj": false, "publisher": "Springer + Science and Business Media LLC", "is_oa": false, "oa_status": "closed", "has_repository_copy": + false, "best_oa_location": null, "first_oa_location": null, "oa_locations": + [], "oa_locations_embargoed": [], "updated": "2023-06-24T05:46:22.445497", + "data_standard": 2, "z_authors": null}' + headers: + Access-Control-Allow-Headers: + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - "396" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:28 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490468&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=7atICqhTs9Cjq2B7Szr5e8RJKh5vHUfuNCpH8ChJWyw%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490468&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=7atICqhTs9Cjq2B7Szr5e8RJKh5vHUfuNCpH8ChJWyw%3D + Server: + - gunicorn + Vary: + - Accept-Encoding + Via: + - 1.1 vegur + status: + code: 200 + message: OK - request: body: null headers: {} @@ -34,7 +137,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:02 GMT + - Wed, 04 Sep 2024 22:54:28 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -54,60 +157,6 @@ interactions: status: code: 200 message: OK - - request: - body: null - headers: {} - method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year - response: - body: - string: - '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": - {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "url": "https://www.semanticscholar.org/paper/e0d2719e49ad216f98ed640864cdacd1c20f53e6", - "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", "venue": "Reactions - weekly", "year": 2023, "citationCount": 0, "influentialCitationCount": 0, - "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], - "publicationDate": "2023-06-01", "journal": {"name": "Reactions Weekly", "pages": - "145 - 145", "volume": "1962"}, "citationStyles": {"bibtex": "@Article{None,\n - booktitle = {Reactions weekly},\n journal = {Reactions Weekly},\n pages = - {145 - 145},\n title = {Convalescent-anti-sars-cov-2-plasma/immune-globulin},\n - volume = {1962},\n year = {2023}\n}\n"}, "authors": []} - - ' - headers: - Access-Control-Allow-Origin: - - "*" - Connection: - - keep-alive - Content-Length: - - "837" - Content-Type: - - application/json - Date: - - Tue, 13 Aug 2024 18:34:02 GMT - Via: - - 1.1 ddd3d8441374ce62d11d031216138152.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - pNq8XNMTPxZyPVVrRe1N7igInqU9ADvOVppmBatkSrtTJM4qVmgHxA== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdcMQHBhvHcEd-g= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "837" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:02 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - e0ccacc5-3403-42dc-a0f4-c33a60337aa0 - status: - code: 200 - message: OK - request: body: null headers: {} @@ -131,7 +180,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:34:03 GMT + - Wed, 04 Sep 2024 22:54:29 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_ensure_sequential_run.yaml b/tests/cassettes/test_ensure_sequential_run.yaml index eadcb263..cbccb764 100644 --- a/tests/cassettes/test_ensure_sequential_run.yaml +++ b/tests/cassettes/test_ensure_sequential_run.yaml @@ -3,12 +3,10 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.48550%2Farxiv.2312.07559?mailto=test@example.com&select=DOI,title + uri: https://api.crossref.org/works/10.48550%2Farxiv.2312.07559?mailto=test@example.com response: body: - string: - '{"status":"failed","message-type":"validation-failure","message":[{"type":"parameter-not-allowed","value":"select","message":"This - route does not support select"}]}' + string: Resource not found. headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -20,15 +18,13 @@ interactions: Connection: - close Content-Type: - - application/json;charset=utf-8 + - text/plain Date: - - Wed, 14 Aug 2024 19:20:22 GMT + - Thu, 05 Sep 2024 00:12:56 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: - chunked - Vary: - - Accept permissions-policy: - interest-cohort=() x-api-pool: @@ -42,8 +38,8 @@ interactions: x-ratelimit-limit: - "150" status: - code: 400 - message: Bad Request + code: 404 + message: Not Found - request: body: null headers: {} @@ -68,27 +64,27 @@ interactions: Content-Type: - application/json Date: - - Wed, 14 Aug 2024 19:20:22 GMT + - Thu, 05 Sep 2024 00:12:57 GMT Via: - - 1.1 4ce044af637284f41cd11c7043e8eaaa.cloudfront.net (CloudFront) + - 1.1 cc58556a6e846289f4d3105969536e4c.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - XAy8Bc-bjjHFQpgqiH4rdAA1BOrUsXSpjmrKxH3bk3daL7NMAGDi2A== + - C174Qdslu8Q0TRvDU4q2oWwiV02Q4vXzl0l0E9GSQSgkoJkgFd3LCw== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cg16mEksPHcEoiQ= + - dmuddGfRvHcEavg= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "277" x-amzn-Remapped-Date: - - Wed, 14 Aug 2024 19:20:22 GMT + - Thu, 05 Sep 2024 00:12:57 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 1b02bab8-e592-429d-9272-23531aa671c5 + - 704483b3-9ffa-4311-b78c-5699d76e2272 status: code: 200 message: OK @@ -113,7 +109,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Wed, 14 Aug 2024 19:20:23 GMT + - Thu, 05 Sep 2024 00:12:57 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_ensure_sequential_run_early_stop.yaml b/tests/cassettes/test_ensure_sequential_run_early_stop.yaml index e77988da..628160b7 100644 --- a/tests/cassettes/test_ensure_sequential_run_early_stop.yaml +++ b/tests/cassettes/test_ensure_sequential_run_early_stop.yaml @@ -23,27 +23,27 @@ interactions: Content-Type: - application/json Date: - - Wed, 14 Aug 2024 19:20:23 GMT + - Thu, 05 Sep 2024 00:12:57 GMT Via: - - 1.1 4eed67f4be7da2537d3407735b8962a8.cloudfront.net (CloudFront) + - 1.1 0da7848263e39308b12bac6a925793b0.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - 1V3eXUygg_LOx2FhDnDnchCQT-ysiC-bp_43pFVQzjQqtV5ZVsUieQ== + - k1pZDKV8IaGlgLC-ZDHhPYqr8iMmXW_Y-efWFWTX8uqPkKPtRJK_Aw== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cg16uGVWPHcEYOg= + - dmudjEEVPHcEDNw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "277" x-amzn-Remapped-Date: - - Wed, 14 Aug 2024 19:20:23 GMT + - Thu, 05 Sep 2024 00:12:57 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 067867ec-8f41-462e-8798-2c075147be4f + - 5d3a21f9-30b6-4510-a79c-b3125a035db1 status: code: 200 message: OK @@ -68,7 +68,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Wed, 14 Aug 2024 19:20:23 GMT + - Thu, 05 Sep 2024 00:12:58 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_minimal_fields_filtering.yaml b/tests/cassettes/test_minimal_fields_filtering.yaml index c4abd2fe..b9b07b94 100644 --- a/tests/cassettes/test_minimal_fields_filtering.yaml +++ b/tests/cassettes/test_minimal_fields_filtering.yaml @@ -7,7 +7,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283974,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting large language models with chemistry tools"]}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: @@ -22,7 +22,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:28 GMT + - Wed, 04 Sep 2024 22:52:21 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -54,7 +54,7 @@ interactions: {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": "38799228"}, "title": "Augmenting large language models with chemistry tools", - "matchScore": 181.37788}]} + "matchScore": 179.37169}]} ' headers: @@ -67,27 +67,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:37 GMT + - Wed, 04 Sep 2024 22:52:21 GMT Via: - - 1.1 2db4851b6d360f79d8bbeb4eae3c9eb6.cloudfront.net (CloudFront) + - 1.1 bd414f5f75d6893558dff609c5ff1fe6.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - IiLEFl6DqYOTEwoXbVW_IELLcpelLNK0CI5_R5rQEVNdP9f_VYhzFQ== + - y9LH1Kkvy7AkEVcJ2xQo8FicwDSkeTvt7ELbNE8NIHVDQBfryZIuYg== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcQLHZwPHcECzA= + - dmipyG2VvHcEfNQ= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "348" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:37 GMT + - Wed, 04 Sep 2024 22:52:21 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 452022b5-32e5-4c67-9b7a-d57d29f74200 + - b0e7573d-bb72-4f0f-a29b-d556474a476c status: code: 200 message: OK @@ -112,7 +112,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:34:37 GMT + - Wed, 04 Sep 2024 22:52:21 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_odd_client_requests.yaml b/tests/cassettes/test_odd_client_requests.yaml index a929ca39..64343ece 100644 --- a/tests/cassettes/test_odd_client_requests.yaml +++ b/tests/cassettes/test_odd_client_requests.yaml @@ -7,7 +7,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":5526,"items":[{"DOI":"10.1038\/s42256-024-00832-8","author":[{"given":"Andres","family":"M. + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":5569,"items":[{"DOI":"10.1038\/s42256-024-00832-8","author":[{"given":"Andres","family":"M. Bran","sequence":"first","affiliation":[]},{"given":"Sam","family":"Cox","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-0310-0851","authenticated-orcid":false,"given":"Oliver","family":"Schilter","sequence":"additional","affiliation":[]},{"given":"Carlo","family":"Baldassari","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-6647-3965","authenticated-orcid":false,"given":"Andrew D.","family":"White","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-3046-6576","authenticated-orcid":false,"given":"Philippe","family":"Schwaller","sequence":"additional","affiliation":[]}],"title":["Augmenting large language models with chemistry tools"]}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' @@ -24,11 +24,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "450" + - "451" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:56 GMT + - Wed, 04 Sep 2024 22:52:25 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -64,7 +64,7 @@ interactions: "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", "name": - "P. Schwaller"}], "matchScore": 175.55591}]} + "P. Schwaller"}], "matchScore": 181.10751}]} ' headers: @@ -77,27 +77,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:35:12 GMT + - Wed, 04 Sep 2024 22:52:25 GMT Via: - - 1.1 d1dad7d3c339d87d553c26a84c9ca5d2.cloudfront.net (CloudFront) + - 1.1 6d77342eb60c8ea96903996368766612.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - jR4en8rFQR1kLKWLt8yREaFgiVbxyV3Bfsu1UfA6EUk5U1DaosU6_A== + - S6x8f0DO8iApl_ckPhU9_t1oPg9SxrZeUerK8DIT1DmlW5wMGkFhLQ== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcUlEPKvHcEVtA= + - dmiqgG8FPHcEQag= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "684" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:35:12 GMT + - Wed, 04 Sep 2024 22:52:25 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 57a3817d-a6a8-4bc2-bc7b-781977962682 + - db084384-127d-4c64-bde2-9688907e8236 status: code: 200 message: OK @@ -122,7 +122,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:35:12 GMT + - Wed, 04 Sep 2024 22:52:26 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -150,7 +150,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283974,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting large language models with chemistry tools"]}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: @@ -165,7 +165,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:35:13 GMT + - Wed, 04 Sep 2024 22:52:26 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -197,7 +197,7 @@ interactions: {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": "38799228"}, "title": "Augmenting large language models with chemistry tools", - "matchScore": 181.37788}]} + "matchScore": 179.37169}]} ' headers: @@ -210,27 +210,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:35:19 GMT + - Wed, 04 Sep 2024 22:52:27 GMT Via: - - 1.1 5a0e8b615e213d3d5cc20b095e088b16.cloudfront.net (CloudFront) + - 1.1 c4f3ed9d980b0f0938aa71b835d9bf96.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - xPUd91qaqbhIVuKSveghxDni1dbLiULD94fqrTUnVZNi8MKuDzRiPA== + - EVPvfs10icErl-1VEGg4mhfSdPk4tKxIm4LWdGr0e18AkJl9l_aNMA== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcXOFbhPHcEVtA= + - dmiqrHAjPHcEDvw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "348" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:35:19 GMT + - Wed, 04 Sep 2024 22:52:27 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - f987b3c4-dbaf-44b3-8e1d-c5dc8c821658 + - 31d8dee3-bbf4-4742-9a8e-9262ee6cae1c status: code: 200 message: OK @@ -255,7 +255,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:35:19 GMT + - Wed, 04 Sep 2024 22:52:27 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -283,7 +283,7 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283974,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"DOI":"10.1038\/s42256-024-00832-8","title":["Augmenting large language models with chemistry tools"]}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: @@ -298,7 +298,7 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:35:20 GMT + - Wed, 04 Sep 2024 22:52:28 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -330,7 +330,7 @@ interactions: {"DBLP": "journals/natmi/BranCSBWS24", "ArXiv": "2304.05376", "PubMedCentral": "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": "38799228"}, "title": "Augmenting large language models with chemistry tools", - "matchScore": 181.37788}]} + "matchScore": 179.37169}]} ' headers: @@ -343,27 +343,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:35:30 GMT + - Wed, 04 Sep 2024 22:52:28 GMT Via: - - 1.1 170caffbbbc9abe2c5fd15f4f58b75b4.cloudfront.net (CloudFront) + - 1.1 c601f966b37ebf3a1ddf28f033b35904.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - ZTE52QH0cvC-EQoaxr6Eld61DOWduPUYH5XunbuAzqJ89FeSfkePDw== + - aV2mXR534hFnOezRa1OFwmQerAi-1sIArtaSvuLnUGPew4iXGyKiKg== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcYTFtAvHcEffA= + - dmiq5GCIPHcEVVg= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "348" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:35:30 GMT + - Wed, 04 Sep 2024 22:52:28 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 1b823d40-9ea2-4f0b-b405-3fa6984154c4 + - 01bb6719-ec46-4883-919a-7be57eeacf69 status: code: 200 message: OK @@ -388,7 +388,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:35:30 GMT + - Wed, 04 Sep 2024 22:52:28 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -412,12 +412,66 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2?mailto=test@example.com&select=DOI,title + uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=externalIds,title + response: + body: + string: + '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": + {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin"} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "197" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:52:28 GMT + Via: + - 1.1 61770d955dae13eda6e8f1b3baae4d1e.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - 4EO6_Ro68exBcBX-IIzKbAc59Rv2xew-yk0bgPsMyPGLoCDVi6vu0Q== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmirCETbPHcEZ1w= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "197" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:52:28 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 6eeeafbe-a4fe-4345-8839-162c2990ce23 + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2?mailto=test@example.com response: body: string: - '{"status":"failed","message-type":"validation-failure","message":[{"type":"parameter-not-allowed","value":"select","message":"This - route does not support select"}]}' + '{"status":"ok","message-type":"work","message-version":"1.0.0","message":{"indexed":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T04:17:17Z","timestamp":1687580237047},"reference-count":1,"publisher":"Springer + Science and Business Media LLC","issue":"1","license":[{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"},{"start":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T00:00:00Z","timestamp":1687564800000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/www.springernature.com\/gp\/researchers\/text-and-data-mining"}],"content-domain":{"domain":[],"crossmark-restriction":false},"short-container-title":["Reactions + Weekly"],"DOI":"10.1007\/s40278-023-41815-2","type":"journal-article","created":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T18:42:29Z","timestamp":1687545749000},"page":"145-145","source":"Crossref","is-referenced-by-count":0,"title":["Convalescent-anti-sars-cov-2-plasma\/immune-globulin"],"prefix":"10.1007","volume":"1962","member":"297","published-online":{"date-parts":[[2023,6,24]]},"reference":[{"key":"41815_CR1","doi-asserted-by":"crossref","unstructured":"Delgado-Fernandez + M, et al. Treatment of COVID-19 with convalescent plasma in patients with + humoral immunodeficiency - Three consecutive cases and review of the literature. + Enfermedades Infecciosas Y Microbiologia Clinica 40\n: 507-516, No. 9, Nov + 2022. Available from: URL: \nhttps:\/\/seimc.org\/","DOI":"10.1016\/j.eimce.2021.01.009"}],"container-title":["Reactions + Weekly"],"original-title":[],"language":"en","link":[{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/article\/10.1007\/s40278-023-41815-2\/fulltext.html","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/link.springer.com\/content\/pdf\/10.1007\/s40278-023-41815-2.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,23]],"date-time":"2023-06-23T19:23:18Z","timestamp":1687548198000},"score":1,"resource":{"primary":{"URL":"https:\/\/link.springer.com\/10.1007\/s40278-023-41815-2"}},"subtitle":["Fever + and mild decrease in baseline oxygen saturation following off-label use: 3 + case reports"],"short-title":[],"issued":{"date-parts":[[2023,6,24]]},"references-count":1,"journal-issue":{"issue":"1","published-online":{"date-parts":[[2023,6]]}},"alternative-id":["41815"],"URL":"http:\/\/dx.doi.org\/10.1007\/s40278-023-41815-2","relation":{},"ISSN":["1179-2051"],"issn-type":[{"value":"1179-2051","type":"electronic"}],"subject":[],"published":{"date-parts":[[2023,6,24]]}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -428,16 +482,18 @@ interactions: - Link Connection: - close + Content-Encoding: + - gzip + Content-Length: + - "1163" Content-Type: - - application/json;charset=utf-8 + - application/json Date: - - Tue, 13 Aug 2024 18:35:30 GMT + - Wed, 04 Sep 2024 22:52:28 GMT Server: - Jetty(9.4.40.v20210413) - Transfer-Encoding: - - chunked Vary: - - Accept + - Accept-Encoding permissions-policy: - interest-cohort=() x-api-pool: @@ -451,51 +507,48 @@ interactions: x-ratelimit-limit: - "150" status: - code: 400 - message: Bad Request + code: 200 + message: OK - request: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1007/s40278-023-41815-2?fields=externalIds,title + uri: https://api.crossref.org/works/10.1007%2Fs40278-023-41815-2/transform/application/x-bibtex response: body: string: - '{"paperId": "e0d2719e49ad216f98ed640864cdacd1c20f53e6", "externalIds": - {"DOI": "10.1007/s40278-023-41815-2", "CorpusId": 259225376}, "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin"} - - ' + " @article{2023, volume={1962}, ISSN={1179-2051}, url={http://dx.doi.org/10.1007/s40278-023-41815-2}, + DOI={10.1007/s40278-023-41815-2}, number={1}, journal={Reactions Weekly}, + publisher={Springer Science and Business Media LLC}, year={2023}, month=jun, + pages={145\u2013145} }\n" headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control Access-Control-Allow-Origin: - "*" + Access-Control-Expose-Headers: + - Link Connection: - - keep-alive - Content-Length: - - "197" - Content-Type: - - application/json + - close Date: - - Tue, 13 Aug 2024 18:35:30 GMT - Via: - - 1.1 b3169f8fae0104e39a0a9728b6537e08.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - BMX9JmQMQSa_4OWVgqZz3d2oDuamu6qnA6eTt087wqu-kvspzMk4-A== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdcZ9F1TPHcEYkQ= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "197" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:35:30 GMT - x-amzn-Remapped-Server: - - gunicorn - x-amzn-RequestId: - - 74d91639-902f-4321-9e57-cb8301b2f50b + - Wed, 04 Sep 2024 22:52:29 GMT + Server: + - Jetty(9.4.40.v20210413) + Transfer-Encoding: + - chunked + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: code: 200 message: OK @@ -522,7 +575,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:35:31 GMT + - Wed, 04 Sep 2024 22:52:29 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_s2_only_fields_filtering.yaml b/tests/cassettes/test_s2_only_fields_filtering.yaml index 01c0c6f2..370b028a 100644 --- a/tests/cassettes/test_s2_only_fields_filtering.yaml +++ b/tests/cassettes/test_s2_only_fields_filtering.yaml @@ -15,7 +15,7 @@ interactions: "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", "name": - "P. Schwaller"}], "matchScore": 175.55591}]} + "P. Schwaller"}], "matchScore": 179.37169}]} ' headers: @@ -28,27 +28,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:42 GMT + - Wed, 04 Sep 2024 22:52:22 GMT Via: - - 1.1 4091abb8cac392d8bc54145a27288bc6.cloudfront.net (CloudFront) + - 1.1 83a6f42adff040301dd9cde8ff611a9c.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - oXXxkNUAoOhnGqIJ65jMPzRAR-ZUWgk-HxktvqS9AXRgUObCB8NJzA== + - 1HN8mFjZPofmpyQ4fF_EGLWbgMXI7pT_W-VdVy9L7YyN9jsJFSRazA== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcRvEGfPHcEQ-A= + - dmip8FZFPHcEKGw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "684" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:42 GMT + - Wed, 04 Sep 2024 22:52:22 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 5e02b89c-edc3-40c7-b308-3d5aee0b439a + - ed4aabca-2c6a-4740-9643-85222d4e3d55 status: code: 200 message: OK @@ -73,7 +73,7 @@ interactions: Content-Type: - text/plain;charset=utf-8 Date: - - Tue, 13 Aug 2024 18:34:43 GMT + - Wed, 04 Sep 2024 22:52:22 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: diff --git a/tests/cassettes/test_title_search[paper_attributes0].yaml b/tests/cassettes/test_title_search[paper_attributes0].yaml index 8b371921..3e55c513 100644 --- a/tests/cassettes/test_title_search[paper_attributes0].yaml +++ b/tests/cassettes/test_title_search[paper_attributes0].yaml @@ -1,4 +1,63 @@ interactions: + - request: + body: null + headers: {} + method: GET + uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + response: + body: + string: + '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": + {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, + "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", + "title": "Effect of native oxide layers on copper thin-film tensile properties: + A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": + 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": + null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": + {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, + "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = + {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n + pages = {235306},\n title = {Effect of native oxide layers on copper thin-film + tensile properties: A reactive molecular dynamics study},\n volume = {118},\n + year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael + Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": + 283.23224}]} + + ' + headers: + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "1109" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:18 GMT + Via: + - 1.1 e8075a4d83e15c8c6543597e1a8de938.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - vB0latP66Hte1od43bgj7T8qLdb6UzgZtdqHNa2gxDLLvDKO6z_zQg== + X-Amz-Cf-Pop: + - SFO53-C1 + X-Cache: + - Miss from cloudfront + x-amz-apigw-id: + - dmi8GFhlvHcErEg= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - "1109" + x-amzn-Remapped-Date: + - Wed, 04 Sep 2024 22:54:18 GMT + x-amzn-Remapped-Server: + - gunicorn + x-amzn-RequestId: + - 88340ebb-a44f-4660-aa0f-f42e231133a9 + status: + code: 200 + message: OK - request: body: null headers: {} @@ -7,9 +66,9 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":11734918,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":11790075,"items":[{"indexed":{"date-parts":[[2023,9,29]],"date-time":"2023-09-29T22:47:50Z","timestamp":1696027670718},"reference-count":57,"publisher":"AIP Publishing","issue":"23","funder":[{"DOI":"10.13039\/100000006","name":"Office - of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide + of Naval Research","doi-asserted-by":"publisher","award":["N00014-12-1-0542"],"id":[{"id":"10.13039\/100000006","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["pubs.aip.org"],"crossmark-restriction":true},"published-print":{"date-parts":[[2015,12,21]]},"abstract":"Metal-oxide layers are likely to be present on metallic nano-structures due to either environmental exposure during use, or high temperature processing techniques such as annealing. It is well known that nano-structured metals have vastly @@ -101,7 +160,7 @@ interactions: Sci. Eng. A"},{"key":"2023062402360541600_c55","doi-asserted-by":"publisher","first-page":"057129","DOI":"10.1063\/1.4880241","volume":"4","year":"2014","journal-title":"AIP Adv."},{"key":"2023062402360541600_c56","doi-asserted-by":"publisher","first-page":"94","DOI":"10.1016\/j.susc.2014.10.017","volume":"633","year":"2015","journal-title":"Surf. Sci."},{"key":"2023062402360541600_c57","doi-asserted-by":"publisher","first-page":"710","DOI":"10.1016\/j.pmatsci.2010.04.001","volume":"55","year":"2010","journal-title":"Prog. - Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":64.02262,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + Mater. Sci."}],"container-title":["Journal of Applied Physics"],"language":"en","link":[{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/pubs.aip.org\/aip\/jap\/article-pdf\/doi\/10.1063\/1.4938384\/15174088\/235306_1_online.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2023,6,24]],"date-time":"2023-06-24T15:07:33Z","timestamp":1687619253000},"score":64.015274,"resource":{"primary":{"URL":"https:\/\/pubs.aip.org\/jap\/article\/118\/23\/235306\/141678\/Effect-of-native-oxide-layers-on-copper-thin-film"}},"issued":{"date-parts":[[2015,12,21]]},"references-count":57,"journal-issue":{"issue":"23","published-print":{"date-parts":[[2015,12,21]]}},"URL":"http:\/\/dx.doi.org\/10.1063\/1.4938384","ISSN":["0021-8979","1089-7550"],"issn-type":[{"value":"0021-8979","type":"print"},{"value":"1089-7550","type":"electronic"}],"published-other":{"date-parts":[[2015,12,21]]},"published":{"date-parts":[[2015,12,21]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -115,11 +174,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "3927" + - "3945" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:33:52 GMT + - Wed, 04 Sep 2024 22:54:18 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -166,7 +225,7 @@ interactions: Connection: - close Date: - - Tue, 13 Aug 2024 18:33:52 GMT + - Wed, 04 Sep 2024 22:54:19 GMT Server: - Jetty(9.4.40.v20210413) Transfer-Encoding: @@ -190,58 +249,53 @@ interactions: body: null headers: {} method: GET - uri: https://api.semanticscholar.org/graph/v1/paper/search/match?query=Effect+of+native+oxide+layers+on+copper+thin-film+tensile+properties:+A+reactive+molecular+dynamics+study&fields=authors,citationCount,citationStyles,externalIds,influentialCitationCount,isOpenAccess,journal,openAccessPdf,publicationDate,publicationTypes,title,url,venue,year + uri: https://api.unpaywall.org/v2/search?query=Effect%20of%20native%20oxide%20layers%20on%20copper%20thin-film%20tensile%20properties:%20A%20reactive%20molecular%20dynamics%20study&email=test@example.com response: body: string: - '{"data": [{"paperId": "4187800ac995ae172c88b83f8c2c4da990d02934", "externalIds": - {"MAG": "2277923667", "DOI": "10.1063/1.4938384", "CorpusId": 124514389}, - "url": "https://www.semanticscholar.org/paper/4187800ac995ae172c88b83f8c2c4da990d02934", - "title": "Effect of native oxide layers on copper thin-film tensile properties: - A reactive molecular dynamics study", "venue": "", "year": 2015, "citationCount": - 8, "influentialCitationCount": 0, "isOpenAccess": false, "openAccessPdf": - null, "publicationTypes": null, "publicationDate": "2015-12-21", "journal": - {"name": "Journal of Applied Physics", "pages": "235306", "volume": "118"}, - "citationStyles": {"bibtex": "@Article{Skarlinski2015EffectON,\n author = - {Michael Skarlinski and D. Quesnel},\n journal = {Journal of Applied Physics},\n - pages = {235306},\n title = {Effect of native oxide layers on copper thin-film - tensile properties: A reactive molecular dynamics study},\n volume = {118},\n - year = {2015}\n}\n"}, "authors": [{"authorId": "9821934", "name": "Michael - Skarlinski"}, {"authorId": "37723150", "name": "D. Quesnel"}], "matchScore": - 283.7328}]} + '{"elapsed_seconds":0.357,"results":[{"response":{"best_oa_location":null,"data_standard":2,"doi":"10.1063/1.4938384","doi_url":"https://doi.org/10.1063/1.4938384","first_oa_location":null,"genre":"journal-article","has_repository_copy":false,"is_oa":false,"is_paratext":false,"journal_is_in_doaj":false,"journal_is_oa":false,"journal_issn_l":"0021-8979","journal_issns":"0021-8979,1089-7550","journal_name":"Journal + of Applied Physics","oa_locations":[],"oa_locations_embargoed":[],"oa_status":"closed","published_date":"2015-12-21","publisher":"AIP + Publishing","title":"Effect of native oxide layers on copper thin-film tensile + properties: A reactive molecular dynamics study","updated":"2023-07-31T05:28:57.007821","year":2015,"z_authors":[{"affiliation":[{"name":"University + of Rochester 1 Materials Science Program, , Rochester, New York 14627, USA"}],"family":"Skarlinski","given":"Michael + D.","sequence":"first"},{"affiliation":[{"name":"University of Rochester 1 + Materials Science Program, , Rochester, New York 14627, USA"},{"name":"University + of Rochester 2 Department of Mechanical Engineering, , Rochester, New York + 14627, USA"}],"family":"Quesnel","given":"David J.","sequence":"additional"}]},"score":0.009231734,"snippet":"Effect + of native oxide layers on copper thin-film + tensile properties: A reactive molecular dynamics + study"}]} ' headers: + Access-Control-Allow-Headers: + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH Access-Control-Allow-Origin: - "*" Connection: - keep-alive + Content-Encoding: + - gzip Content-Length: - - "1108" + - "706" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:33:56 GMT - Via: - - 1.1 ddd3d8441374ce62d11d031216138152.cloudfront.net (CloudFront) - X-Amz-Cf-Id: - - 9aSLHkzNg4rEGApjVN3aOWzejo54f_82pKKm0MEtEsqwyPMjcpXhkw== - X-Amz-Cf-Pop: - - IAD55-P4 - X-Cache: - - Miss from cloudfront - x-amz-apigw-id: - - cdcKdFPEPHcEHWw= - x-amzn-Remapped-Connection: - - keep-alive - x-amzn-Remapped-Content-Length: - - "1108" - x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:33:56 GMT - x-amzn-Remapped-Server: + - Wed, 04 Sep 2024 22:54:20 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490458&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=3iH3mreiGrF6hHOFWXTx5avt8oC7DhAjNvJhBq5sqs8%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490458&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=3iH3mreiGrF6hHOFWXTx5avt8oC7DhAjNvJhBq5sqs8%3D + Server: - gunicorn - x-amzn-RequestId: - - 7df5e5d0-6891-4b52-8ea3-d7ecbd595932 + Vary: + - Accept-Encoding + Via: + - 1.1 vegur status: code: 200 message: OK diff --git a/tests/cassettes/test_title_search[paper_attributes1].yaml b/tests/cassettes/test_title_search[paper_attributes1].yaml index 7f89c193..64475d25 100644 --- a/tests/cassettes/test_title_search[paper_attributes1].yaml +++ b/tests/cassettes/test_title_search[paper_attributes1].yaml @@ -1,4 +1,42 @@ interactions: + - request: + body: null + headers: {} + method: GET + uri: https://api.unpaywall.org/v2/search?query=PaperQA:%20Retrieval-Augmented%20Generative%20Agent%20for%20Scientific%20Research&email=test@example.com + response: + body: + string: '{"elapsed_seconds":0.018,"results":[]} + + ' + headers: + Access-Control-Allow-Headers: + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH + Access-Control-Allow-Origin: + - "*" + Connection: + - keep-alive + Content-Length: + - "39" + Content-Type: + - application/json + Date: + - Wed, 04 Sep 2024 22:54:20 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490460&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=HLxBN33rMeUOx491OpwKHBqFWhk%2F47Zh%2FMQMQA3ZIc0%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490460&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=HLxBN33rMeUOx491OpwKHBqFWhk%2F47Zh%2FMQMQA3ZIc0%3D + Server: + - gunicorn + Via: + - 1.1 vegur + status: + code: 200 + message: OK - request: body: null headers: {} @@ -7,11 +45,11 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2004346,"items":[{"indexed":{"date-parts":[[2024,3,8]],"date-time":"2024-03-08T00:26:06Z","timestamp":1709857566241},"reference-count":48,"publisher":"Oxford + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2016375,"items":[{"indexed":{"date-parts":[[2024,3,8]],"date-time":"2024-03-08T00:26:06Z","timestamp":1709857566241},"reference-count":48,"publisher":"Oxford University Press (OUP)","issue":"1","license":[{"start":{"date-parts":[[2024,2,21]],"date-time":"2024-02-21T00:00:00Z","timestamp":1708473600000},"content-version":"vor","delay-in-days":48,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0\/"}],"funder":[{"DOI":"10.13039\/100000092","name":"National - Library of Medicine","doi-asserted-by":"publisher","award":["R01LM009886","R01LM014344"]},{"DOI":"10.13039\/100006108","name":"National - Center for Advancing Translational Sciences","doi-asserted-by":"publisher","award":["UL1TR001873"]},{"DOI":"10.13039\/100000002","name":"National - Institutes of Health","doi-asserted-by":"publisher"}],"content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2024,1,4]]},"abstract":"Abstract<\/jats:title>\n \n Objective<\/jats:title>\n To + Library of Medicine","doi-asserted-by":"publisher","award":["R01LM009886","R01LM014344"],"id":[{"id":"10.13039\/100000092","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100006108","name":"National + Center for Advancing Translational Sciences","doi-asserted-by":"publisher","award":["UL1TR001873"],"id":[{"id":"10.13039\/100006108","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100000002","name":"National + Institutes of Health","doi-asserted-by":"publisher","id":[{"id":"10.13039\/100000002","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":[],"crossmark-restriction":false},"published-print":{"date-parts":[[2024,1,4]]},"abstract":"Abstract<\/jats:title>\n \n Objective<\/jats:title>\n To automate scientific claim verification using PubMed abstracts.<\/jats:p>\n <\/jats:sec>\n \n Materials and Methods<\/jats:title>\n We developed CliVER, an end-to-end scientific Claim VERification system that leverages retrieval-augmented @@ -94,7 +132,7 @@ interactions: gain-based evaluation of IR techniques","volume":"20","author":"J\u00e4rvelin","year":"2002","journal-title":"ACM Trans Inf Syst"},{"key":"2024030720490192400_ooae021-B46","volume-title":"Evidence-Based Practice in Nursing & Healthcare: A Guide to Best Practice","author":"Melnyk","year":"2022"},{"key":"2024030720490192400_ooae021-B47","first-page":"206","author":"Gupta","year":"2017"},{"key":"2024030720490192400_ooae021-B48","first-page":"1","author":"Park","year":"2012"}],"container-title":["JAMIA - Open"],"language":"en","link":[{"URL":"https:\/\/academic.oup.com\/jamiaopen\/advance-article-pdf\/doi\/10.1093\/jamiaopen\/ooae021\/56732770\/ooae021.pdf","content-type":"application\/pdf","content-version":"am","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,3,7]],"date-time":"2024-03-07T20:49:23Z","timestamp":1709844563000},"score":28.253725,"resource":{"primary":{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article\/doi\/10.1093\/jamiaopen\/ooae021\/7612234"}},"issued":{"date-parts":[[2024,1,4]]},"references-count":48,"journal-issue":{"issue":"1","published-print":{"date-parts":[[2024,1,4]]}},"URL":"http:\/\/dx.doi.org\/10.1093\/jamiaopen\/ooae021","ISSN":["2574-2531"],"issn-type":[{"value":"2574-2531","type":"electronic"}],"published-other":{"date-parts":[[2024,4,1]]},"published":{"date-parts":[[2024,1,4]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' + Open"],"language":"en","link":[{"URL":"https:\/\/academic.oup.com\/jamiaopen\/advance-article-pdf\/doi\/10.1093\/jamiaopen\/ooae021\/56732770\/ooae021.pdf","content-type":"application\/pdf","content-version":"am","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"syndication"},{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article-pdf\/7\/1\/ooae021\/56904263\/ooae021.pdf","content-type":"unspecified","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,3,7]],"date-time":"2024-03-07T20:49:23Z","timestamp":1709844563000},"score":28.21724,"resource":{"primary":{"URL":"https:\/\/academic.oup.com\/jamiaopen\/article\/doi\/10.1093\/jamiaopen\/ooae021\/7612234"}},"issued":{"date-parts":[[2024,1,4]]},"references-count":48,"journal-issue":{"issue":"1","published-print":{"date-parts":[[2024,1,4]]}},"URL":"http:\/\/dx.doi.org\/10.1093\/jamiaopen\/ooae021","ISSN":["2574-2531"],"issn-type":[{"value":"2574-2531","type":"electronic"}],"published-other":{"date-parts":[[2024,4,1]]},"published":{"date-parts":[[2024,1,4]]}}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}' headers: Access-Control-Allow-Headers: - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, @@ -108,11 +146,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "4472" + - "4501" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:33:57 GMT + - Wed, 04 Sep 2024 22:54:20 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -144,7 +182,7 @@ interactions: {"ArXiv": "2312.07559", "DBLP": "journals/corr/abs-2312-07559", "DOI": "10.48550/arXiv.2312.07559", "CorpusId": 266191420}, "url": "https://www.semanticscholar.org/paper/7e55d8701785818776323b4147cb13354c820469", "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", - "venue": "arXiv.org", "year": 2023, "citationCount": 21, "influentialCitationCount": + "venue": "arXiv.org", "year": 2023, "citationCount": 23, "influentialCitationCount": 1, "isOpenAccess": false, "openAccessPdf": null, "publicationTypes": ["JournalArticle"], "publicationDate": "2023-12-08", "journal": {"name": "ArXiv", "volume": "abs/2312.07559"}, "citationStyles": {"bibtex": "@Article{L''ala2023PaperQARG,\n author = {Jakub @@ -156,7 +194,7 @@ interactions: "name": "Odhran O''Donoghue"}, {"authorId": "2258961451", "name": "Aleksandar Shtedritski"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "2258964497", "name": "Samuel G. Rodriques"}, {"authorId": "2273941271", "name": - "Andrew D. White"}], "matchScore": 245.13031}]} + "Andrew D. White"}], "matchScore": 245.32262}]} ' headers: @@ -169,27 +207,27 @@ interactions: Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:33:58 GMT + - Wed, 04 Sep 2024 22:54:21 GMT Via: - - 1.1 4f3476fc0ed69f4f9209b2ccb91b0050.cloudfront.net (CloudFront) + - 1.1 22dc875d744f932282ce89367c98a9de.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - CwsOIjMLqJMdElsyLdeS2wwxKXgCRhX8CmKCRqs0eqpUoxdjaDKKdw== + - wPdXFyJ6K74yqvcthCmUvijgxfYiphfPhTqPkAOi1NLDO3OTHvZfVQ== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcLUHkfPHcEbNA= + - dmi8gHlpvHcEFBg= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - "1386" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:33:58 GMT + - Wed, 04 Sep 2024 22:54:21 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 644ea493-688c-42a3-bfbf-e4778f4a1711 + - 3f4118a6-9ea7-482d-b995-b6732d7193e3 status: code: 200 message: OK diff --git a/tests/cassettes/test_title_search[paper_attributes2].yaml b/tests/cassettes/test_title_search[paper_attributes2].yaml index d07e9d85..2a4a06ee 100644 --- a/tests/cassettes/test_title_search[paper_attributes2].yaml +++ b/tests/cassettes/test_title_search[paper_attributes2].yaml @@ -7,10 +7,10 @@ interactions: response: body: string: - '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2283974,"items":[{"indexed":{"date-parts":[[2024,8,9]],"date-time":"2024-08-09T17:10:06Z","timestamp":1723223406992},"reference-count":103,"publisher":"Springer + '{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":2296021,"items":[{"indexed":{"date-parts":[[2024,9,4]],"date-time":"2024-09-04T17:39:08Z","timestamp":1725471548702},"reference-count":103,"publisher":"Springer Science and Business Media LLC","issue":"5","license":[{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"tdm","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"},{"start":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T00:00:00Z","timestamp":1715126400000},"content-version":"vor","delay-in-days":0,"URL":"https:\/\/creativecommons.org\/licenses\/by\/4.0"}],"funder":[{"DOI":"10.13039\/501100001711","name":"Schweizerischer - Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"]},{"DOI":"10.13039\/100000001","name":"National - Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat + Nationalfonds zur F\u00f6rderung der Wissenschaftlichen Forschung","doi-asserted-by":"publisher","award":["180544","180544","180544"],"id":[{"id":"10.13039\/501100001711","id-type":"DOI","asserted-by":"publisher"}]},{"DOI":"10.13039\/100000001","name":"National + Science Foundation","doi-asserted-by":"publisher","award":["1751471","1751471"],"id":[{"id":"10.13039\/100000001","id-type":"DOI","asserted-by":"publisher"}]}],"content-domain":{"domain":["link.springer.com"],"crossmark-restriction":false},"short-container-title":["Nat Mach Intell"],"abstract":"Abstract<\/jats:title>Large language models (LLMs) have shown strong performance in tasks across domains but struggle with chemistry-related problems. These models also lack access @@ -24,7 +24,7 @@ interactions: both LLM and expert assessments, demonstrates ChemCrow\u2019s effectiveness in automating a diverse set of chemical tasks. Our work not only aids expert chemists and lowers barriers for non-experts but also fosters scientific advancement - by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":12,"title":["Augmenting + by bridging the gap between experimental and computational chemistry.<\/jats:p>","DOI":"10.1038\/s42256-024-00832-8","type":"journal-article","created":{"date-parts":[[2024,5,8]],"date-time":"2024-05-08T10:03:31Z","timestamp":1715162611000},"page":"525-535","update-policy":"http:\/\/dx.doi.org\/10.1007\/springer_crossmark_policy","source":"Crossref","is-referenced-by-count":17,"title":["Augmenting large language models with chemistry tools"],"prefix":"10.1038","volume":"6","author":[{"given":"Andres","family":"M. Bran","sequence":"first","affiliation":[]},{"given":"Sam","family":"Cox","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-0310-0851","authenticated-orcid":false,"given":"Oliver","family":"Schilter","sequence":"additional","affiliation":[]},{"given":"Carlo","family":"Baldassari","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0002-6647-3965","authenticated-orcid":false,"given":"Andrew D.","family":"White","sequence":"additional","affiliation":[]},{"ORCID":"http:\/\/orcid.org\/0000-0003-3046-6576","authenticated-orcid":false,"given":"Philippe","family":"Schwaller","sequence":"additional","affiliation":[]}],"member":"297","published-online":{"date-parts":[[2024,5,8]]},"reference":[{"key":"832_CR1","unstructured":"Devlin, @@ -313,7 +313,7 @@ interactions: (2024).","DOI":"10.5281\/zenodo.10884645"},{"key":"832_CR103","doi-asserted-by":"publisher","unstructured":"Bran, A., Cox, S., White, A. & Schwaller, P. ur-whitelab\/chemcrow-public: v0.3.24. Zenodo https:\/\/doi.org\/10.5281\/zenodo.10884639 (2024).","DOI":"10.5281\/zenodo.10884639"}],"container-title":["Nature - Machine Intelligence"],"language":"en","link":[{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,5,23]],"date-time":"2024-05-23T23:03:31Z","timestamp":1716505411000},"score":46.864754,"resource":{"primary":{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8"}},"issued":{"date-parts":[[2024,5,8]]},"references-count":103,"journal-issue":{"issue":"5","published-online":{"date-parts":[[2024,5]]}},"alternative-id":["832"],"URL":"http:\/\/dx.doi.org\/10.1038\/s42256-024-00832-8","ISSN":["2522-5839"],"issn-type":[{"value":"2522-5839","type":"electronic"}],"published":{"date-parts":[[2024,5,8]]},"assertion":[{"value":"13 + Machine Intelligence"],"language":"en","link":[{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8","content-type":"text\/html","content-version":"vor","intended-application":"text-mining"},{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8.pdf","content-type":"application\/pdf","content-version":"vor","intended-application":"similarity-checking"}],"deposited":{"date-parts":[[2024,5,23]],"date-time":"2024-05-23T23:03:31Z","timestamp":1716505411000},"score":46.731644,"resource":{"primary":{"URL":"https:\/\/www.nature.com\/articles\/s42256-024-00832-8"}},"issued":{"date-parts":[[2024,5,8]]},"references-count":103,"journal-issue":{"issue":"5","published-online":{"date-parts":[[2024,5]]}},"alternative-id":["832"],"URL":"http:\/\/dx.doi.org\/10.1038\/s42256-024-00832-8","ISSN":["2522-5839"],"issn-type":[{"value":"2522-5839","type":"electronic"}],"published":{"date-parts":[[2024,5,8]]},"assertion":[{"value":"13 September 2023","order":1,"name":"received","label":"Received","group":{"name":"ArticleHistory","label":"Article History"}},{"value":"27 March 2024","order":2,"name":"accepted","label":"Accepted","group":{"name":"ArticleHistory","label":"Article History"}},{"value":"8 May 2024","order":3,"name":"first_online","label":"First @@ -334,11 +334,11 @@ interactions: Content-Encoding: - gzip Content-Length: - - "10544" + - "10574" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:33:59 GMT + - Wed, 04 Sep 2024 22:54:22 GMT Server: - Jetty(9.4.40.v20210413) Vary: @@ -362,45 +362,1597 @@ interactions: body: null headers: {} method: GET - uri: https://api.crossref.org/works/10.1038%2Fs42256-024-00832-8/transform/application/x-bibtex + uri: https://api.unpaywall.org/v2/search?query=Augmenting%20large%20language%20models%20with%20chemistry%20tools&email=test@example.com response: body: string: - " @article{M_Bran_2024, title={Augmenting large language models with - chemistry tools}, volume={6}, ISSN={2522-5839}, url={http://dx.doi.org/10.1038/s42256-024-00832-8}, - DOI={10.1038/s42256-024-00832-8}, number={5}, journal={Nature Machine Intelligence}, - publisher={Springer Science and Business Media LLC}, author={M. Bran, Andres - and Cox, Sam and Schilter, Oliver and Baldassari, Carlo and White, Andrew - D. and Schwaller, Philippe}, year={2024}, month=may, pages={525\u2013535} - }\n" + '{"elapsed_seconds":0.298,"results":[{"response":{"best_oa_location":{"endpoint_id":null,"evidence":"open + (via crossref license)","host_type":"publisher","is_best":true,"license":"cc-by","oa_date":"2024-05-08","pmh_id":null,"repository_institution":null,"updated":"2024-07-28T13:13:26.235272","url":"https://www.nature.com/articles/s42256-024-00832-8.pdf","url_for_landing_page":"https://doi.org/10.1038/s42256-024-00832-8","url_for_pdf":"https://www.nature.com/articles/s42256-024-00832-8.pdf","version":"publishedVersion"},"data_standard":2,"doi":"10.1038/s42256-024-00832-8","doi_url":"https://doi.org/10.1038/s42256-024-00832-8","first_oa_location":{"endpoint_id":null,"evidence":"open + (via crossref license)","host_type":"publisher","is_best":true,"license":"cc-by","oa_date":"2024-05-08","pmh_id":null,"repository_institution":null,"updated":"2024-07-28T13:13:26.235272","url":"https://www.nature.com/articles/s42256-024-00832-8.pdf","url_for_landing_page":"https://doi.org/10.1038/s42256-024-00832-8","url_for_pdf":"https://www.nature.com/articles/s42256-024-00832-8.pdf","version":"publishedVersion"},"genre":"journal-article","has_repository_copy":true,"is_oa":true,"is_paratext":false,"journal_is_in_doaj":false,"journal_is_oa":false,"journal_issn_l":"2522-5839","journal_issns":"2522-5839","journal_name":"Nature + Machine Intelligence","oa_locations":[{"endpoint_id":null,"evidence":"open + (via crossref license)","host_type":"publisher","is_best":true,"license":"cc-by","oa_date":"2024-05-08","pmh_id":null,"repository_institution":null,"updated":"2024-07-28T13:13:26.235272","url":"https://www.nature.com/articles/s42256-024-00832-8.pdf","url_for_landing_page":"https://doi.org/10.1038/s42256-024-00832-8","url_for_pdf":"https://www.nature.com/articles/s42256-024-00832-8.pdf","version":"publishedVersion"},{"endpoint_id":null,"evidence":"oa + repository (via pmcid lookup)","host_type":"repository","is_best":false,"license":null,"oa_date":null,"pmh_id":null,"repository_institution":null,"updated":"2024-07-28T13:13:26.235373","url":"https://www.ncbi.nlm.nih.gov/pmc/articles/PMC11116106","url_for_landing_page":"https://www.ncbi.nlm.nih.gov/pmc/articles/PMC11116106","url_for_pdf":null,"version":"publishedVersion"}],"oa_locations_embargoed":[],"oa_status":"hybrid","published_date":"2024-05-08","publisher":"Springer + Science and Business Media LLC","title":"Augmenting large language models + with chemistry tools","updated":"2024-05-09T06:24:40.646053","year":2024,"z_authors":[{"family":"M. + Bran","given":"Andres","sequence":"first"},{"family":"Cox","given":"Sam","sequence":"additional"},{"ORCID":"http://orcid.org/0000-0003-0310-0851","authenticated-orcid":false,"family":"Schilter","given":"Oliver","sequence":"additional"},{"family":"Baldassari","given":"Carlo","sequence":"additional"},{"ORCID":"http://orcid.org/0000-0002-6647-3965","authenticated-orcid":false,"family":"White","given":"Andrew + D.","sequence":"additional"},{"ORCID":"http://orcid.org/0000-0003-3046-6576","authenticated-orcid":false,"family":"Schwaller","given":"Philippe","sequence":"additional"}]},"score":0.025694918,"snippet":"Augmenting + large language models with chemistry tools"},{"response":{"best_oa_location":{"endpoint_id":null,"evidence":"open + (via page says license)","host_type":"publisher","is_best":true,"license":"cc-by-nc","oa_date":"2021-12-31","pmh_id":null,"repository_institution":null,"updated":"2023-02-22T13:05:09.634202","url":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","url_for_landing_page":"https://doi.org/10.47205/jdss.2021(2-iv)74","url_for_pdf":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","version":"publishedVersion"},"data_standard":2,"doi":"10.47205/jdss.2021(2-iv)74","doi_url":"https://doi.org/10.47205/jdss.2021(2-iv)74","first_oa_location":{"endpoint_id":null,"evidence":"open + (via page says license)","host_type":"publisher","is_best":true,"license":"cc-by-nc","oa_date":"2021-12-31","pmh_id":null,"repository_institution":null,"updated":"2023-02-22T13:05:09.634202","url":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","url_for_landing_page":"https://doi.org/10.47205/jdss.2021(2-iv)74","url_for_pdf":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","version":"publishedVersion"},"genre":"journal-article","has_repository_copy":false,"is_oa":true,"is_paratext":false,"journal_is_in_doaj":false,"journal_is_oa":true,"journal_issn_l":"2709-6254","journal_issns":"2709-6254,2709-6262","journal_name":"Journal + of Development and Social Sciences","oa_locations":[{"endpoint_id":null,"evidence":"open + (via page says license)","host_type":"publisher","is_best":true,"license":"cc-by-nc","oa_date":"2021-12-31","pmh_id":null,"repository_institution":null,"updated":"2023-02-22T13:05:09.634202","url":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","url_for_landing_page":"https://doi.org/10.47205/jdss.2021(2-iv)74","url_for_pdf":"https://jdss.org.pk/issues/v2/4/water-sharing-issues-in-pakistan-impacts-on-inter-provincial-relations.pdf","version":"publishedVersion"}],"oa_locations_embargoed":[],"oa_status":"gold","published_date":"2021-12-31","publisher":"Pakistan + Social Sciences Research Institute (PSSRI)","title":"(2021) Volume 2, Issue + 4 Cultural Implications of China Pakistan Economic Corridor (CPEC Authors: + Dr. Unsa Jamshed Amar Jahangir Anbrin Khawaja Abstract: This study is an attempt + to highlight the cultural implication of CPEC on Pak-China relations, how + it will align two nations culturally, and what steps were taken by the governments + of two states to bring the people closer. After the establishment of diplomatic + relations between Pakistan and China, the cultural aspect of relations between + the two states also moved forward. The flow of cultural delegations intensified + after the 2010, because this year was celebrated as the \u2018Pak-China Friendship + Year\u2019. This dimension of relations further cemented between the two states + with the signing of CPEC in April 2015. CPEC will not only bring economic + prosperity in Pakistan but it will also bring two states culturally closer. + The roads and other communication link under this project will become source + of cultural flow between the two states. Keyswords: China, CPEC, Culture, + Exhibitions Pages: 01-11 Article: 1 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)01 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)01 Download Pdf: download + pdf view article Creative Commons License Political Persona on Twittersphere: + Comparing the Stardom of Prime Minister(s) of Pakistan, UK and India Authors: + Maryam Waqas Mudassar Hussain Shah Saima Kausar Abstract: Political setup + demands to use Twittersphere for preserving its reputation because of significant + twitter audience, which follows celebrities and political figures. In this + perspective, political figures frequently use twitter to highlight their political + as well as personal lives worldwide. However, political figures take the stardom + status among the twitter audience that follow, retweet and comment by their + fans. The purpose of this study is, to analyze what kind of language, level + of interest is made by political figures while communicating via twitter, + text, phrases and languages used by political figures, and do their tweets + contribute in their reputation. The qualitative content analysis is used for + evaluation of the interests shared by PM Imran Khan, PM Boris John Son and + PM Narendra Modi with the key words of tweets. A well-established coding sheet + is developed for the analysis of text, phrases and words in the frames of + negative, positive and neutral from March 2020 to May 2020. The results are + demonstrating on the basis of content shared by Prime Ministers of three countries + i.e., From Pakistan, Imran Khan, United Kingdom, Johnson Boris and India, + Narendra Modi on twitter. The findings also reveal that varied issues discussed + in tweets, significantly positive and neutral words are selected by these + political figures. PM Imran tweeted more negative tweets than PM Boris Johnson + and PM Narendra Modi. However, PM Boris Johnson and PM Narendra Modi make + significant positive and neutral tweets. It is observed that political figures + are conscious about their personal reputation while tweeting. It also revealed + that the issues and tweets shared by these leaders contribute to their personal + reputation. Keyswords: Imran Khan, Johnson Boris, Narendra Modi, Political + Persona, Stardom, Twittersphere Pages: 12-23 Article: 2 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)02 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)02 + Download Pdf: download pdf view article Creative Commons License An Empirical + Relationship between Government Size and Economic Growth of Pakistan in the + Presence of Different Budget Uncertainty Measures Authors: Sunila Jabeen Dr. + Wasim Shahid Malik Abstract: Relationship between government size and economic + growth has always been a debated issue all over the world since the formative + work of Barro (1990). However, this relationship becomes more questionable + when policy uncertainty is added in it. Hence, this paper presents evidence + on the effect of government size on economic growth in the presence of budget + uncertainty measured through three different approaches. Rather than relying + on the traditional and complicated measures of uncertainty, a new method of + measuring uncertainty based on government budget revisions of total spending + is introduced and compared with the other competing approaches. Using time + series annual data from 1973-2018, the short run and long run coefficients + from Autoregressive Distributed Lag (ARDL) framework validate the negative + effect of budget uncertainty and government size on economic growth of Pakistan + regardless of the uncertainty measure used. Therefore, to attain the long + run economic growth, along with the control on the share of government spending + in total GDP, government should keep the revisions in the budget as close + to the initial announcements as it can so that uncertainty can be reduced. + Further, the uncertainty in fiscal spending calculated through the deviation + method raises a big question on the credibility of fiscal policy in Pakistan. + Higher will be the deviation higher will be the uncertainty and lower the + fiscal policy credibility hence making fiscal policy less effective in the + long run. Keyswords: Budget Uncertainty, Economic Growth, Government Size, + Policy Credibility Pages: 24-38 Article: 3 , Volume 2 , Issue 4 DOI Number: + 10.47205/jdss.2021(2-IV)03 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)03 + Download Pdf: download pdf view article Creative Commons License Despair in + The Alchemist by Ben Jonson Authors: Dr. Fatima Syeda Dr. Faiza Zaheer Numrah + Mehmood Abstract: This research aims to challenge the assumption that The + Alchemist by Ben Jonson is one of the greatest examples of the \u201cexplicit + mirth and laughter\u201d (Veneables 86). The paper argues that The Alchemist + is a cynical and despairing play created in an atmosphere not suitable for + a comedy. This is a qualitative study of the text and aims at an analysis + of the theme, situations, characters, language, and the mood of the play to + determine that Jonson is unable to retain the comic spirit in The Alchemist + and in an attempt to \u201cbetter men\u201d (Prologue. 12) he becomes more + satirical and less humorous or comic. This research is important for it contends + that the play, termed as a comedy, may be read as a bitter satire on the cynical, + stinky, and despairing world of the Elizabethan times. Keyswords: Comedy, + Despair, Reformation Pages: 39-47 Article: 4 , Volume 2 , Issue 4 DOI Number: + 10.47205/jdss.2021(2-IV)04 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)04 + Download Pdf: download pdf view article Creative Commons License Analysis + of Principles of Coordinated Border Management (CBM) in articulation of War-Control + Strategies: An Account of Implementation Range on Pakistan and Afghanistan + Authors: Dr. Sehrish Qayyum Dr. Umbreen Javaid Abstract: Currently, Border + Management is crucial issue not only for Pakistan but for the entire world + due to increased technological developments and security circumstances. Pakistan + and Afghanistan being immediate states have inter-connected future with socio-economic + and security prospects. Principles of Coordinated Border Management (CBM) + approach have been extracted on the basis of in-depth interviews with security + agencies and policymakers to understand the real time needs. The current research + employs mixed method approach. Process Tracing is employed in this research + to comprehend the causal mechanism behind the contemporary issue of border + management system. A detailed statistical analysis of prospect outcomes has + been given to validate the implication of CBM. Implication range of CBM has + been discussed with positive and probably negative impacts due to its wide + range of significance. This research gives an analysis of feasibility support + to exercise CBM in best interest of the state and secure future of the region. + Keyswords: Afghanistan, Coordinated Border Management, Fencing, Pakistan, + Security Pages: 48-62 Article: 5 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)05 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)05 Download Pdf: download + pdf view article Creative Commons License The Belt and Road Initiative (BRI) + vs. Quadrilateral Security Dialogue (the Quad): A Perspective of a Game Theory + Authors: Muhammad Atif Prof. Dr. Muqarrab Akbar Abstract: Containment is the + central part of the U.S.''s foreign policy during the cold war. With the application + of containment Policy, the U.S. achieved much success in international politics. + Over time China has become more powerful and sees great power in international + politics. China wants to expand and launched the Belt and Road Initiative + (BRI). The primary purpose of The Belt and Road Initiative (BRI) is to achieve + support from regional countries and save their interests from the U.S. In + 2017, the American administration launched its Containment policy through + Quadrilateral Security Dialogue (the Quad) to keep their interest from China. + The Quadrilateral Security Dialogue (Quad) is comprising of Australia, the + United States, Japan, and India. This Study is based on Qualitative research + with theoretical application of Game theory. This research investigates both + plans of China (BRI) and the U.S. (the Quad) through a Game Theory. In this + study, China and the U.S. both like to act as gamers in international politics. + This study recommends that Game theory can predict all developments in the + long term. Keyswords: Containment, Expansionism, Quadrilateral Security Dialogue, + The Belt and Road Initiative (BRI) Pages: 63-75 Article: 6 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)06 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)06 + Download Pdf: download pdf view article Creative Commons License Narendra + Modi a Machiavellian Prince: An Appraisal Authors: Dr. Imran Khan Dr. Karim + Haider Syed Muhammad Yousaf Abstract: The comparison of Narendra Modi and + Machiavellian Prince is very important as policies of Modi are creating problems + within India and beyond the borders. The Prince is the book of Niccolo Machiavelli + a great philosopher of his time. If Indian Prime Minister Narendra Modi qualifies + as a Prince of Machiavelli is a very important question. This is answered + in the light of his policies and strategies to become the undisputed political + leader of India. Much of the Machiavellian Prince deals with the problem of + how a layman can raise himself from abject and obscure origins to such a position + that Narendra Modi has been holding in India since 2014. The basic theme of + this article is revolving around the question that is following: Can Modi\u2019s + success be attributed to techniques of The Prince in important respects? This + article analyzed Narendra Modi''s policies and strategies to develop an analogy + between Machiavellian Prince and Modi in terms of characteristics and political + strategies. This research work examines, how Narendra Modi became the strongest + person in India. Keyswords: Comparison, India, Machiavelli, Modus Operandi, + Narendra Modi Pages: 76-84 Article: 7 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)07 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)07 Download Pdf: download + pdf view article Creative Commons License Analyzing Beckett''s Waiting for + Godot as a Political Comedy Authors: Muhammad Umer Azim Dr. Muhammad Saleem + Nargis Saleem Abstract: This study was devised to analyze Samuel Beckett\u2019s + play Waiting for Godot in the light of Jean-Francois Lyotard\u2019s theory + of postmodernism given in his book The Postmodern Condition (1984). This Lyotardian + paradigm extends a subversive challenge to all the grand narratives that have + been enjoying the status of an enviable complete code of life in the world + for a long time. Even a cursory scan over the play under analysis creates + a strong feel that Beckett very smartly, comprehensively and successfully + questioned the relevance of the totalizing metanarratives to the present times. + Being an imaginative writer, he was well aware of the fact that ridicule is + a much more useful weapon than satire in the context of political literature. + There are so many foundationalist ideologies that he ridicules in his dramatic + writing. Christianity as a religion is well exposed; the gravity of philosophy + is devalued; the traditional luxury that the humans get from the art of poetry + is ruptured and the great ideals of struggle are punctured. He achieves his + artistic and ideologically evolved authorial intentions with a ringing success. + It is interesting to note that he maintains a healthy balance between art + and message. Keyswords: Beckett, Lyotard, The Postmodern Condition, Waiting + for Godot Pages: 85-94 Article: 8 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)08 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)08 Download Pdf: download + pdf view article Creative Commons License Effect of Parenting Styles on Students\u2019 + Academic Achievement at Elementary Level Authors: Hafsa Noreen Mushtaq Ahmad + Uzma Shahzadi Abstract: The study intended to find out the effect of parenting + styles on students\u2019 academic achievement. Current study was quantitative + in nature. All elementary level enrolled students at government schools in + the province of the Punjab made the population of the study. Multistage sampling + was used to select the sample from four districts of one division (Sargodha) + of the Punjab province i.e., Sargodha. A sample size i.e., n=960; students + and their parents were participated in this study. Research scales i.e. Parenting + Styles Dimension Questionnaire (PSDQ) was adapted to analyze and measure parents\u2019 + parenting styles and an achievement test was developed to measure the academic + achievement of the elementary students. After pilot testing, reliability coefficient + Cronbach Alpha values for PSDQ and achievement test were 0.67 and 0.71 Data + was collected and analyzed using frequencies count, percentages, mean scores + and one way ANOVA. Major findings of the study were; Majority of the parents + had authoritative parental style, a handsome number of parents keep connection + of warmth and support with their children, show intimacy, focus on discipline, + do not grant autonomy to their children, do not indulge with their children + and as well as a handsome number of students were confident during their studies + and study, further, found that parental style had positive relationship with + academic achievement. Recommendations were made on the basis of findings and + conclusion such as arrangement of Parents Teachers Meetings (PTM\u2018s), + parents\u2019 training, provision of incentives and facilities to motivate + families might be an inclusive component of elementary education program. + Keyswords: Academic Achievement, Elementary Education, Parenting Styles Pages: + 95-110 Article: 9 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)09 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)09 Download Pdf: download + pdf view article Creative Commons License Kashmir Conflict and the Question + of Self-Determination Authors: Izzat Raazia Saqib Ur Rehman Abstract: The + objective of this paper is to explore relations between Pakistan and India + since their inception in the perspective of Kashmir conundrum and its impact + on the regional security. Kashmir is the unfinished agenda of partition and + a stumbling block in the bilateral relations between Pakistan and India. After + the partition of sub-continent in 1947, Pakistan and India got their sovereign + status. Kashmir conflict, a disputed status state, is the byproduct of partition. + Pakistan and India are traditional arch-foes. Any clash between Pakistan and + India can bring the two nuclear states toe-to-toe and accelerate into nuclear + warfare. Due to the revulsion, hostility and lack of trust between the two, + the peaceful resolution of the Kashmir issue has been long overdue. Ever-increasing + border spats, arms race and threat of terrorism between the two have augmented + anxiety in the subcontinent along with the halt of talks between India and + Pakistan at several times. Additionally, it hampers the economic and trade + ties between the two. India, time and again, backtracked on Kashmir issue + despite UN efforts to resolve the issue. Recently, Indian government has responded + heavy-handedly to the Kashmiri agitators\u2019 demand for sovereignty and + revocation of \u2018Special Status\u2019 of Kashmir impacting the stability + of the region in future. Keyswords: India, Kashmir Conundrum, Pakistan, Regional + Security, Sovereignty Pages: 111-119 Article: 10 , Volume 2 , Issue 4 DOI + Number: 10.47205/jdss.2021(2-IV)10 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)10 + Download Pdf: download pdf view article Creative Commons License Exploring + Image of China in the Diplomatic Discourse: A Critical Discourse Analysis + Authors: Muhammad Afzaal Muhammad Ilyas Chishti Abstract: The present study + hinges on the major objective of analyzing Pakistani and Indian diplomatic + discourses employed in portrayal of image of China. Data comprises the official + discourse which is used in diplomatic affairs of both the states. The extensive + investigation seeks insights from the fundamentals of Critical Discourse Analysis + propounded by van Dijk, Fairclough and Wodak with a special focus on Bhatia\u2019s + (2006) work. The study reveals that the image of China has always been accorded + priority within Indian and Pakistani diplomatic discourse even though nature + of bilateral relations among China, India and Pakistan is based on entirely + different dynamics; Indian and Pakistani diplomatic discourses are reflective + of sensitivities involved within the bilateral relations. Through employment + of linguistic techniques of \u2018positivity\u2019, \u2018evasion\u2019 and + \u2018influence and power\u2019, Indian diplomats have managed not to compromise + over the fundamentals in bilateral relations with China despite Pakistan\u2019s + already strengthened and deep-rooted relations with China. While Pakistani + diplomatic fronts have been equally successful in further deepening their + already strengthened relations in the midst of surging controversies on CPEC, + BRI and OBOR. Hence, diplomatic fronts of both the counties, through employment + of ideologically loaded linguistic choices, leave no stone unturned in consolidation + of the diplomatic relations with China. Keyswords: CDA, China Image, Corpus, + Language of Diplomacy, Political Discourse Analysis Pages: 120-133 Article: + 11 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)11 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)11 + Download Pdf: download pdf view article Creative Commons License Students\u2019 + Perception about Academic Advising Satisfaction at Higher Education Level + Authors: Rukhsana Sardar Zarina Akhtar Shamsa Aziz Abstract: The purpose of + the study was to examine the students\u2019 perception about academic advising + satisfaction at higher education level. All the students from two years master + (M.A) degree programme and four years (BS) degree programme of eight departments + from International Islamic University Islamabad (IIUI), Faculty of Social + Sciences were taken as a population of the study. 475 students were randomly + selected as a sample of the study. The Academic Advising Inventory (AAI) was + used to assess Academic Advising Style. For measuring level of the satisfaction, + descriptive statistics was used. To compare the mean difference department-wise + and gender-wise about academic advising satisfaction t.test was applied. It + was concluded that from the major findings of the study those students who + received departmental academic advising style are more satisfied as compared + to those students who provided prescriptive academic advising style. Female + students seemed more satisfied as compared to male students regarding the + academic advising style provided to them. Students who satisfied from developmental + academic advising style and they were also highly satisfied from the advising + provided to them at Personalizing Education (PE) and this is the subscale + of developmental academic advising whereas students who received prescriptive + academic advising they were also satisfied from the advising provided to them + regarding personalizing education and academic decision making but their percentage + is less. It is recommended to Universities Administration to focus on Developmental + Academic Advising Style and establish centers at universities/department level + and nominate staff who may be responsible to provide developmental academic + advising. Keyswords: Academic Advising, Higher Level, Students\u2019 Perception + Pages: 134-144 Article: 12 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)12 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)12 Download Pdf: download + pdf view article Creative Commons License Perceptions of Sexual Harassment + in Higher Education Institutions: A Gender Analysis Authors: Ruhina Ghassan + Dr. Subha Malik Nayab Javed Abstract: Sexual harassment is a social issue + which is present in every society, globally, which interferes in an individual\u2019s + social and professional life. It happens almost everywhere i.e. at workplaces, + public places or institutes as well. The focus of the present study was to + explore the differences of male and female students\u2019 perception of sexual + harassment. This study was a quantitative research. Sample of the study included + of 400 students (200 males and 200 females) from two government and two private + universities. In the present study, Sexual Harassment Perception Questionnaire + (SHPQ) was used to find out these differences in perceptions as every person + has his own view for different situations. The study revealed the significant + differences in perception of students. Study showed that both genders perceived + that female students get more harassed than male students. The factors that + affect the perception frequently were gender and age. The findings recommended + that regulations for sexual harassment should be implemented in universities; + laws should be made for sexual harassment in higher education institutes. + Students should be aware of sexual harassment through seminars, self-defense + classes and awareness campaigns. And every institute should have a counseling + center for the better mental health of students. Keyswords: Gender Differences, + Higher Educational Institutions, Sexual Harassment Pages: 145-158 Article: + 13 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)13 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)13 + Download Pdf: download pdf view article Creative Commons License Role of IMF + Over the Governance Structure and Economic Development of Pakistan Authors: + Ali Qamar Sheikh Dr. Muhammad Imran Pasha Muhammad Shakeel Ahmad Siddiqui + Abstract: Developing countries like Pakistan seeks for financial assistance + in order to fulfil their deficits. IMF is one of the largest financial institution + who give loans to countries who need it. This research has studied the IMF + role and the effects of IMF conditions on the economy of Pakistan. To carry + out this research, both quantitative data from primary sources has been gathered + and qualitative analysis has been made to signify whither this borrowing creating + and maintaining dependency of Pakistan on West and financial and governance + structure constructed to curtail Countries like Pakistan. The results concluded + that there is negative and insignificant relationship between GDP and IMF + loans in the long run. The short-term dynamic shows that weak economic and + Political Institutions in Pakistan. The Development dilemma constitutes dependency + even today. The Current Budget Deficit Pakistan''s fiscal deficit climbs to + Rs 3.403 trillion in 2020-21 needs to be readdressed in such a manner that + Pakistan can counter Balance of Payments and import/export imbalance. Keyswords: + Dependency, Development, IMF, Loans, Debt, Pakistan, Governance structure + Pages: 159-172 Article: 14 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)14 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)14 Download Pdf: download + pdf view article Creative Commons License Climate Change and the Indus Basin: + Prospects of Cooperation between India and Pakistan Authors: Sarah Saeed Prof. + Dr. Rana Eijaz Ahmad Abstract: Climate change is transforming the global societies. + The shift in average temperature is putting negative impacts on human health, + food production and the natural resources. In the wake of the altered climate, + water flow in the river systems is experiencing variability and uncertainty. + This paper aims at studying the negative impacts of climate change on the + water resources of the Indus Basin and investigate the prospects of cooperation + between India and Pakistan; two major riparian nations sharing the basin. + Adopting the case study approach, a theoretical framework has been built on + the \u2018Theory of the International Regimes\u2019. It has been argued that + institutional capacity and the dispute resolution mechanism provided in any + water sharing agreement determine the extent of cooperation among the member + states. Since India and Pakistan are bound by the provisions of the Indus + Waters Treaty, this study tries to assess the effectiveness of this agreement + in managing the negative consequences of the climate change. Keyswords: Climate + Change, Cooperation, Dispute Resolution Mechanism, Institutional Capacity + Pages: 173-185 Article: 15 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)15 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)15 Download Pdf: download + pdf view article Creative Commons License Translation, Cultural Adaptation + and Validation of Behavioral-Emotional Reactivity Index for Adolescents Authors: + Saima Saeed Farah Malik Suzanne Bartle Haring Abstract: Measuring differentiation + of self in terms of behavioral/emotional reactivity towards parents is important + because of the complex parent-child connection. This needs a valid and reliable + measure to assess the differentiation of self particularly in a relationship + with parents. Behavior\\Emotional Reactivity Index is such a tool that fulfills + this purpose. The present study was carried out to culturaly adapt and translate + BERI into the Urdu language and establish the psychometric properties of Urdu + version. A sample of 303 adolescents of age (M = 16.07, SD = 1.77) was taken + from different schools and colleges. Scale was split into Mother and father + forms for the convenience of respondents. Findings supported the original + factor structure of the BERI-original version. Higher-order factor analysis + showed good fit indices with excellent alpha ranges (\u03b1= .91 to \u03b1=.80). + BERI scores were compared for the adolescents who were securely attached with + parents and insecurely attached with parents which showed a significant difference + between the groups. BERI-Urdu version was found to be a valid and reliable + measure in the Pakistani cultural context which gives researchers new directions + to work with adolescents. Keyswords: Adolescence, Differentiation of Self, + Behavioral, Emotional Reactivit, Index, Parental Attachment Pages: 186-200 + Article: 16 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)16 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)16 Download Pdf: download pdf + view article Creative Commons License Notion of Repression in Modern Society: + A Comparative Analysis of Sigmund Freud and Herbert Marcuse Authors: Khadija + Naz Abstract: One of the fundamental issues for modern civilized man is how + to adapt a modern society without losing his individual status. Is it possible + for an individual to adjust in a society where he/she loses his/her individuality + and becomes part of collectivity? One point of view is that for society to + flourish, man needs to be repressed. But to what extent is repression necessary + for societies to rise and survive? This paper shall examine the above given + questions from the standpoint of two thinkers who greatly influenced twentieth-century + thought: Sigmund Freud and Herbert Marcuse. To undertake this task, first + the term Repression shall be examined and then the notions of Freud and Marcuse + will be discussed to determine the degree of repression required for the development + of modern society. Keyswords: Modern Society, Performance Principle, Repression, + Surplus-Repression, The Pleasure Principle, The Reality Principle Pages: 201-214 + Article: 17 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)17 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)17 Download Pdf: download pdf + view article Creative Commons License Perceptions of Teacher Educators about + Integration of (ESD) in Elementary Teachers Education Program Authors: Dr. + Rukhsana Durrani Dr. Fazal ur Rahman Dr. Shaista Anjum Abstract: Education + and sustainable development have a close relationship as education provides + sustainability to society. This study explored the perceptions of teacher + educators for integration of Education for Sustainable Development (ESD) in + B.Ed. 4 years\u2019 elementary program. Four major components of ESD i.e., + Education, Social & Culture, Economic and Environment were included in + study. 127 teacher educators from departments of education were randomly selected + from public universities of Pakistan who were offering B.Ed. 4 years\u2019 + elementary program. Data was collected through questionnaires from teacher + educators. The findings recommended the inclusion of the components of Education + for Sustainable Development (ESD) in curriculum of B.Ed. 4 years\u2019 elementary + program. Keyswords: B.Ed. 4 Years Elementary Curriculum, Sustainable Development, + Integration, Teacher Education Pages: 215-225 Article: 18 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)18 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)18 + Download Pdf: download pdf view article Creative Commons License Exploring + TPACK skills of prospective teachers and challenges faced in digital technology + integration in Pakistan Authors: Tariq Saleem Ghayyur Dr. Nargis Abbas Mirza + Abstract: The current study was aimed to explore TPACK skills of prospective + teachers and challenges faced in digital technology integration in Pakistan. + The study was qualitative in nature and semi structured interview schedule + was developed to collect data from prospective teachers. Purposive sampling + technique was employed to collect data from 20 prospective teachers of 7 public + sector universities. It was concluded that majority of the prospective teachers + used general technological and pedagogical practices (GTPP), technological + knowledge practices (TKP), Technological Pedagogical Knowledge practices (TPKP), + Technological Content Knowledge practices (TCKP). Majority of prospective + teachers reported multiple challenges in integration of digital technology + in teacher education programs including lack of teacher training as one of + the largest hurdle in digital technology integration, lack of digital technology + resources or outdated digital technology resources, inadequate computer lab, + lack of learning apps (courseware), financial constraints, lack of teachers\u2019 + motivation to use digital technology, slow computers available at computer + labs, and unavailability of technical support. It was recommended that digital + technology infrastructure should be improved across all teacher education + institution and it was further recommended that TPACK model of digital technology + integration should serve digital technology integration in teacher education + programs in Pakistan. Keyswords: Challenges, Digital Technology Integration, + Digital Technology Resources, Digital Technology, TPACK Pages: 226-241 Article: + 19 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)19 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)19 + Download Pdf: download pdf view article Creative Commons License Revisiting + the Linkage between Money Supply and Income: A Simultaneous Equation Model + for Pakistan Authors: Zenab Faizullah Dr. Shahid Ali Muhammad Imad Khan Abstract: + A reliable estimate of the money supply is an important sign of the Gross + Domestic Product (GDP) and many other macroeconomic indicators. It is widely + discussed that over a long period of time, there is a strong link between + GDP and money supply. This link is significantly important for formation of + monetary policy. The main aim of this study is to estimate the income-money + supply model for Pakistan. This study estimates the income-money supply model + for Pakistan over the period of 2009 to 2019. The study uses Two Stage Least + Square (2SLS) econometric technique due to the presence of endogeneity problem + in the model under consideration. The existence of simultaneity between money + supply (M2) and income (GDP) is also clear from the results of Hausman Specification + test for simultaneity between M2 and GDP. The results further show that there + exists a strong money-income relationship in case of Pakistan. Keyswords: + Money Supply, Income, Simultaneous Equations Pages: 242-247 Article: 20 , + Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)20 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)20 + Download Pdf: download pdf view article Creative Commons License Analyzing + the Mechanism of Language Learning Process by the Use of Language Learning + Strategies Authors: Shafiq Ahmad Farooqi Dr. Muhammad Shakir Sher Muhammad + Awan Abstract: This analytical research study involves the use of learning + strategies to know the mechanism of learning a second language. People acquire + their native language (L1) without any conscious effort and they have a complete + knowledge of L1 and are competent in their native language even without going + to school. It is believed that language learning is a process as well as an + outcome and the focus of current study is to understand the process of learning + a second language. The population in this study comprised of 182 boys and + Girls Govt. Higher Secondary Schools studying at intermediate level in the + 11 Districts of the Southern Punjab. The sample was selected through random + probability sampling and consisted of 40 subject specialists teaching the + subject of English in Govt. higher secondary schools with 400 students studying + English at Intermediate level. A questionnaire comprising some common and + easily accessible learning strategies was designed to determine the frequency + of these strategies used in the classrooms by the language learners through + the specialists of the subject. The data was collected from the selected sample + through the subject specialists teaching in these schools. The data was collected + quantitatively and was analyzed in the statistical package for social sciences + (SPSS) version 20. The most common 27 language learning strategies (LLS) were + applied to analyze the process of language learning. In the light of the results + of the study, it was concluded that application of the learning strategies + according to the nature of the text is helpful in understanding the language + functions and its application. Keyswords: Language Acquisition, Learning Strategies, + Mechanism of Language Learning Pages: 249-258 Article: 21 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)21 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)21 + Download Pdf: download pdf view article Creative Commons License Secondary + School Science Teachers\u2019 Practices for the Development of Critical Thinking + Skills: An Observational Study Authors: Dr. Muhammad Jamil Dr. Yaar Muhammad + Dr. Naima Qureshi Abstract: In the National curriculum policy documents, to + produce rationale and independent critical thinkers, different pedagogical + practices have been recommended like cooperative learning, questioning, discussion, + etc. This qualitative case study aimed at analyzing secondary school science + teachers\u2019 practices for the development of critical thinking skills in + secondary school students. There were twelve classrooms (four from each subject + of Physics, Chemistry and Biology) selected as cases. Video recording was + used for the observations for six lessons in each classroom. In this way, + a total of 72 observations were conducted lasting for approximately 35 minutes. + Qualitative content analysis was used for data analysis through Nvivo 12. + The findings of the observations revealed that all the teachers used the lecture + method. They used this to cover the content at a given specific time. There + was not much focus on the development of critical thinking. In a few of the + classrooms, the students were engaged and active during learning different + specific topics. Whiteboard was used as a visual aid by most of the teachers. + Furthermore, to some extent, discussion, questioning, and daily life examples + were used in different classrooms. It is recommended that teachers\u2019 professional + development should be conducted to focus on the development of critical thinking + skills through pedagogical practices which have been recommended by the national + education policy documents. Keyswords: Analysis, Critical Thinking, Curriculum + Policy, Pedagogy, Secondary Level Pages: 259-265 Article: 22 , Volume 2 , + Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)22 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)22 + Download Pdf: download pdf view article Creative Commons License Historical + Development of Clinical Psychology in Pakistan: A Critical Review-based Study + Authors: Muhammad Nawaz Shahzad Dr. Mushtaq Ahmad Dr. Muhammad Waseem Tufail + Abstract: Clinical Psychology is clinical and curing psychological practices + in Pakistan. The present research study endeavors to examine the contemporary + status of Clinical Psychology in the country and descriptively analyzes the + significant contribution of various psychologists in its development. The + study also elaborates the emergence of Clinical Psychology and its treatment + aspects in the country. The experimental approach of the treatment psychology + has also been defined. The role of different scholars to set and promote the + Clinical Psychology as discipline and dealing about treatment of Human mind + has also been discussed here. The study also presented the scenario of the + issues of legislative acknowledgment, qualifications mandatory for practice, + communal awareness of cerebral treatment, the tradition of ethnic and native + practices about the clinical psychological treatments has also been discussed. + Keyswords: Approaches, Clinical Psychology, Psychologist, Therapist Pages: + 266-272 Article: 23 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)23 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)23 Download Pdf: download + pdf view article Creative Commons License Impact of Devolution of Power on + School Education Performance in Sindh after 18th Constitutional Amendment + Authors: Abdul Hafeez Dr. Saima Iqbal Muhammad Imran Abstract: Devolution + of the authority from central units of empowering authorities to the local + level to develop and exercise policies at local or organizational level is + under debate in various countries of the world. The legation in with the name + of 18th constitutional amendment in constitution of 1973 of Pakistan ensures + more autonomy to federal units. The difference between province and federation + mostly creates misunderstanding in the belief of cooperation and universalism + of education standards, expenditures and service delivery. Very currently + the ministry of education and local government encoring principles and headmasters + to adopt self-management skills to be updated to accept the spin of power + from higher authorities to lower authorities\u2019 pedagogical and local schools. + In this qualitative research semi structured questioner were incorporated + as data collection tool equally, the data was analyzed by usage of NVivo software. + In this regard Government of Sindh has introduced various reforms and new + trends like objectives and policy pillars, better government schools, improved + learning outcomes and increased and improved funding in the education sector + Sindh government has so far been unable to effectively use its resources to + implement effective governance system which provides quality and sustained + education in the province. To achieve this basic universal education, equally + fourth objective of Sustainable Development Goal (SDG) the educational leaders + must develop a comparative education setup that help to educate planers to + plan and design standards for school leaders, instruction, appropriate professional + development of teachers, ways to support school leaders to change in mission. + Parallel, develop new program for early childhood, school and class size and + ensure school enrollment. Keyswords: 18th Constitutional Amendment, Devolution + of Power, Sindh Education Performance Pages: 273-285 Article: 24 , Volume + 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)24 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)24 + Download Pdf: download pdf view article Creative Commons License Legal Aspects + of Evidence Collected by Modern Devices: A Case Study Authors: Muhammad Hassan + Zia Alvina Ali Abstract: This paper is a qualitative research of different + case laws dealing with modern technological evidence. Courts were required + to adopt new methods, techniques and devices obtained through advancement + of science without affecting the original intention of law. Because of modern + technology, a benefit could be taken from said technology to preserve evidences + and to assist proceedings of the Court in the dispensation of justice in modern + times. Owing to the scientific and technological advancements the admissibility + of audio and visual proofs has grown doubtful. No doubt modern evidence assist + the court in reaching out to the just decision but at the same time certain + criteria need to be laid down which must be satisfied to consider such evidence + admissible. Different Case laws are discussed here to show how the cases were + resolved on the basis of technological evidence and when and why such evidence + have been rejected by the court, if it did. Moreover, legal practices developed + in various countries allow our Courts to record evidence through video conferencing. + The Honorable Supreme Court of Pakistan directed that in appropriate cases + statement of juvenile rape victims and other cases of sensitive nature must + be recorded through video conferencing to avoid inconvenience for them to + come to the Court. Nevertheless, it has some problems. The most important + among them is the identification of the witness and an assurance that he is + not being prompted when his statement is recorded. In this paper protocols + that are necessary to follow while examining witness through video link are + discussed Keyswords: DNA Profiling, Finger Prints, , Telephone Calls, Video + Tape Pages: 286-297 Article: 25 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)25 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)25 Download Pdf: download + pdf view article Creative Commons License The Political Economy of Terrorisms: + Economic Cost of War on Terror for Pakistan Authors: Muhammad Shakeel Ahmad + Siddiqui Dr. Muhammad Imran Pasha Saira Akram Abstract: Terrorism and its + effect on contemporary society is one of the core and vital subjects of International + Political Economy (IPE) during the last years. Despite the fact that this + is not a new phenomenon, special attention has been given to this issue, specifically + after the terrorist attacks of 9/11, 2001. The objective of this paper analyzes + to what dimensions terrorism affects the global economy mainly the two predominant + actors of the conflict i.e. Pakistan and the United States. For this purpose, + this article will take a look at the financial cost of War for Pakistan and + how Pakistan\u2019s decision to become frontline State has affected its Economy, + its effect on agriculture, manufacturing, tourism, FDI, increased defense + costs The normative and qualitative methodology shows a significant disadvantage + between terrorist activities and economic growth, social progress, and political + development. The results shows that Pakistan has bear slow economic growth + while facing terrorist activities more than US. In this last section, the + paper suggests ways and means to satisfy people around the world not to go + in the hands of fundamentals and terrorists. Keyswords: Cost of War, Economic + Growth, Frontline States, Pak Us Relations, Terrorism Pages: 297-309 Article: + 26 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)26 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)26 + Download Pdf: download pdf view article Creative Commons License A Comparative + Study of Grade 10 English Textbooks of Sindh Textbook Board and Cambridge + \u201cO Level\u201d in the perspective of Revised Bloom\u2019s Taxonomy Authors: + Mahnoor Shaikh Dr. Shumaila Memon Abstract: The present study evaluated the + cognitive levels of reading comprehension questions present in grade 10 English + Textbooks namely English Textbook for grade 10 by Sindh Textbook Board and + compared it to Oxford Progressive English book 10 used in Cambridge \u201cO + Level\u201d in the perspective of Revised Bloom\u2019s Taxonomy. Qualitative + content analysis was used as a methodology to carry out the study. To collect + the data, a checklist based on Revised Bloom\u2019s taxonomy was used as an + instrument. A total of 260 reading comprehension questions from both the textbooks + were evaluated. The findings of the study revealed that reading comprehension + questions in English textbook for grade 10 were solely based on remembering + level (100%) whereas the questions in Oxford Progressive English 10 were mainly + based on understanding level (75.5%) with a small percentage of remembering + (12.5%), analyzing (11.1%) and evaluating level (0.74%). This suggests that + the reading comprehension questions in both the textbooks are dominantly based + on lower-order thinking skills. Keyswords: Bloom\u2019s Taxonomy, Content + Analysis, Reading Comprehension, Textbook Evaluation Pages: 310-320 Article: + 27 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)27 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)27 + Download Pdf: download pdf view article Creative Commons License Assessing + the Preparedness of Government Hospitals: A Case of Quetta City, Balochiatan + Authors: Sahar Arshad Syed Ainuddin Jamal ud din Abstract: Earthquake with + high magnitude is often resulting in massive destruction with more causalities + and high mortality rate. Timely providence of critical healthcare facilities + to affected people during an emergency response is the core principle of disaster + resilient communities. The main objective of this paper is assessing the hospital + preparedness of government hospitals in Quetta. Primary data was collected + through questionnaire survey. Total of 165 sample size chosen via simple random + sampling. Relative important index (RII) is used to analyze the overall situation + of hospitals preparedness in term of earthquake disaster. Findings of the + study showed that the preparedness level of government hospitals in Quetta + is weak to moderate level. Based on the findings this study recommends the + necessary measures to minimize the risk of earthquake disaster including training + and exercise programs for the staff of hospital, proper resource management + to efficiently use the existing machinery and equipment in the meeting of + disaster to enhance employee\u2019s performance and preparedness of government + hospitals in Quetta to deal with earthquake disaster. Keyswords: Earthquake, + Preparedness, Relative Important Index Pages: 321-329 Article: 28 , Volume + 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)28 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)28 + Download Pdf: download pdf view article Creative Commons License Development + of Reasoning Skills among Prospective Teachers through Cognitive Acceleration + Approach Authors: Memoona Bibi Dr. Shamsa Aziz Abstract: The main objectives + of this study were to; investigate the effects of the Cognitive Acceleration + approach on the reasoning skills of the prospective teachers at the university + level and compare the effects of the Cognitive Acceleration approach and traditional + approach concerning reasoning skills of prospective teachers\u2019 at the + university level. The study was experimental and followed a pre-test post-test + control group experimental design. The sample of the study included the experimental + group and control group from the BS Education program in the Department of + Education at International Islamic University Islamabad. A simple random sampling + technique was used to select the sample after pre-test and pairing of prospective + teachers. CTSR (classroom test for scientific reasoning) developed by A.E. + Lawson (2000) was used to collect the data through pre-tests and post-tests. + The experimental group\u2019s perception about different activities of the + experiment was taken through a self-made rating scale. Collected data were + analyzed by calculating mean scores and t-test for hypothesis testing by using + SPSS. The main findings of the study revealed that the Cognitive Acceleration + teaching approach has a significant positive effect on the reasoning skills + development of prospective teachers at the university level. Findings also + showed that participants found this teaching approach effective and learned + many new concepts and skills with the help of thinking activities. Based on + findings it has been concluded that the Cognitive Acceleration teaching approach + might be encouraged for training prospective teachers at the university level + and training sessions about the use of the Cognitive Acceleration approach + must be arranged by teacher education programs and institutions. Keyswords: + Cognitive Acceleration Approach, Prospective Teachers, Reasoning Skills, Traditional + Approach Pages: 330-342 Article: 29 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)29 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)29 Download Pdf: download + pdf view article Creative Commons License Spatial Injustice in Shamsie\u2019s + Kartography Authors: Syeda Hibba Zainab Zaidi Dr. Ali Usman Saleem Sadia Waheed + Abstract: Social space under postmodernism and wave of globalization have + suffered in and its idealistic representations are lost and deteriorated which + ultimately led to discursiveness in the lives of postmodern man, especially + Karachiites. The boundaries of geographies play a significant role in shaping + fates, biographies, social superstructures and shared collective histories + of its residents. Considering this, Henri Lefebvre and Edward William Soja, + argue that space is something which determines the living circumstances within + the particular social framework and instigates and controls various societal + happenings. City space of Karachi suffers from appalling distortions as a + part of postmodern, globalized and capitalist world. By employing Lefebvre\u2019s + idea of spatial triad and Soja\u2019s views of the trialectrics of spaciality, + this paper foregrounds how social space enforces spatial injustice and serves + for the inculcation of spatial cleansing in the lives of inhabitants of urban + space. Using Shamsie\u2019s Kartography as an interpretive tool for contemporary + urban environment, this paper inquires the engrafting of spatial cleansing + in the lives of Karachiites resulting in multiple standardization and segregation + on the basis of living standards among different social strata. This research + substantiates how in Kartography, Materialism nibbles the roots of social + values and norms while sequentially administering Spatial Injustice in the + lives of Karachiites. This paper proclaims the scarcity of execution of Spatial + Justice in the lives of common people in this postmodern globalized capitalist + era. This paper urges the possibility of a utopian urban space with enforced + spatial justice where people can be saved from dilemmas of injustice and segregation, + especially Karachiites. Keyswords: Capitalistic Hegemony, City Space, Globalization, + Spatial Cleansing, Spatial Injustice Pages: 343-352 Article: 30 , Volume 2 + , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)30 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)30 + Download Pdf: download pdf view article Creative Commons License A Quasi-Experimental + Study on the Performance and Attitudes of Pakistani Undergraduate Students + towards Hello English Language Learning Application Authors: Wafa Pirzada + Dr. Shumaila Memon Dr. Habibullah Pathan Abstract: With the advancement of + technology, more and more avenues of bringing creativity and innovation in + language learning have opened up. These exciting advances have given rise + to a new field of study within linguistics, termed Mobile Assisted Language + Learning (MALL). This paper aims to fill the gap of MALL research in the area + of grammar teaching in the Pakistan. Two BS Part 1 classes from University + of Sindh, Jamshoro, were chosen for this quasi-experimental study. In total, + 62 out of 101 students volunteered to use the Hello English application for + 2 months, making up the experiment group, and the remaining 39 students were + put in a control group. Paired Samples T-Test was run on pretest and posttest + results which revealed no significant difference in both groups\u2019 performances, + proving that Hello English application could not significantly improve students\u2019 + grammar performance. However, in spite of the lack of a significant difference + between the test results, the data gathered through the attitudinal survey + showed that students still found mobile application very easy to use and effective + in language learning. Keyswords: Attitudes, Grammar Learning, Hello English, + Mobile Language Learning, Technology In Language Learning Pages: 353-367 Article: + 31 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)31 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)31 + Download Pdf: download pdf view article Creative Commons License Impact of + Determinants on the Profile Elevation of Secondary School Teachers in Pakistan + Authors: Zahida Aziz Sial Dr. Farah Latif Naz Humaira Saadia Abstract: The + foremost purpose of this research paper was to interrogate the effects of + determinants on the educational and social profile of secondary school teachers + in Pakistan. The key question taken was related to determinants that affect + teachers\u2019 profile. The Population of the study was secondary school teachers + of Punjab province. A questionnaire was used as research instrument. The researcher + personally visited the schools to administer the questionnaire. E-Views software + was used for data analysis. Moreover, OLS regression model and LOGIT regression + model were carried out. It was found that the variable years of teaching experience + (EXPYR) (*** 0.03) can have a vital concrete effect upon the societal figuration + of teachers as the experience of teachers grows, so does their social interactions + with officials, colleagues, students and friends increases. The said variable + is significant at 10 percent level. The variable, Residence (RESIDE) (** 0.53) + have a significant impact upon civic links. This obviously associated with + less community connection of country side teachers than the teachers residing + in urban areas. Keyswords: Determinants, Elevation, Educational Profile, Social + Profile, Secondary School Teacher Pages: 368-372 Article: 32 , Volume 2 , + Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)32 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)32 + Download Pdf: download pdf view article Creative Commons License Impact of + War on Terror on the Tourism Industry in Swat, Pakistan Authors: Sabir Ihsan + Prof. Dr. Anwar Alam Aman Ullah Abstract: The present study was designed to + ascertain the status of tourism before insurgency, during insurgency and after + insurgency in District Swat-KP Pakistan. The study is quantitative and descriptive + in nature. A diverse sample size of 370 out of 9014 was selected through convenient + sampling strategy. Notwithstanding, the objectives of the study was achieved + through structured questionnaire. Data was analysed through chi-square at + Bi Variate level. Findings of the study revealed that earning livelihood in + swat was significantly associated (P=0.016), (P=0.003) with tourism industry + prior 2009 and present time respective, but the same statement was observed + non-significant (P=0.075) at the time of insurgency. Arranging different festivals + in the study area and establishment of different showrooms for local handcrafts, + artificial jewellery and woollen shawl are some of the recommendations of + the study. Keyswords: Business, Insurgency, Swat, Tourism Pages: 373-385 Article: + 33 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)33 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)33 + Download Pdf: download pdf view article Creative Commons License Challenges + and Prospects of Pak-China Economic Corridor Authors: Muhammad Mudabbir Malik + Prof. Dr. Muqarrab Akbar Abstract: Pak-China has historic relationships from + the emergence of both states, and were proved long-lasting in every thick + and thin times. In initial times they supported each other in foreign policies + and regional issues. Pakistan and China have border disputes with India, which + forced them to come close to counter India, letter on the economic interests + strengthened these relations. In order to maximize the economic benefits, + China announced economic corridor with the name China Pakistan Economic Corridor + (CEPC). It was thought it will boost the economic growth of China, and as + a prime partner Pakistan will also get economic benefits. In order to completely + understand how Pakistan and China came on the same page and decided to put + CPEC into reality we have to understand the Geo-political Importance of Pakistan, + Strategic and economic importance of CPEC for China and Pakistan, Influence + and concerns of West and neighboring countries including India. Domestic limitations + and all the possible benefits and risks involved in this project for both + Pakistan and China, this research acknowledges all these questions. Keyswords: + Challenges, China, CPEC, Domestic Limitations Economic Growth, Pakistan, Western + and Regional Concerns Pages: 386-404 Article: 34 , Volume 2 , Issue 4 DOI + Number: 10.47205/jdss.2021(2-IV)34 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)34 + Download Pdf: download pdf view article Creative Commons License An Analysis + of Learning Practices and Habits of Children at Early Childhood Education: + Students\u2019 Perspective Authors: Masood Ahmad Sabiha Iqbal Shaista Noreen + Abstract: The study was designed to analysis learning practices and habits + of children at early childhood education. The major objective of the study + was to find out the learning practices and habits of children. Problem was + related to current situation, so survey method was exercised, 220 students + were selected with the help of convenient sampling technique. Self-constructed + questionnaire were exercised. The collected data was analyzed and calculate + frequency, percentage, mean score, standard deviation and t-test of independent + variable. The major findings of the study were; students learn from the pictures, + cartoons and funny face; student\u2019s eyes get tired of reading. When student + read context continuously then they feel that their eyes get tired. There + was a significance difference between male and female student about learning + practices and habits of children. Keyswords: Early Childhood Education, Learning + Practices and Habits, Pre-School Students Pages: 405-416 Article: 35 , Volume + 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)35 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)35 + Download Pdf: download pdf view article Creative Commons License Gender Identity + Construction in Akhtar\u2019s Melody of a Tear Authors: Dr. Amna Saeed Hina + Quddus Abstract: This study aims to discuss the notion of gender in terms + of performativity and social construction. It also draws upon the idea of + gender identity construction and how it relates to the society, performativity + and biology. As its theoretical framework, the study relies upon the Performative + Theory of Gender and Sex (1990) presented by Judith Butler and studies the + gender identity construction in the female protagonist of Akhtar\u2019s Melody + of a Tear. Zara is a girl who is raised as a boy from his father and there + is a kind of dilemma in Zara\u2019s personality related to being masculine + and feminine. The cultural norms of a particular gender are also a cause of + this dilemma. Throughout the novel, she is in a conflicting state whether + she should behave feminine or masculine. She is being depicted as an incomplete + person until she finds and resolves this issue of gender identity. The paper + discusses the gender performativity, social construction, cultural norms and + identity as these are all contributing to the confusion and construction of + the protagonist\u2019s identity. Character analysis is used as the methodology + of analysis. Keyswords: Cultural Norms, Femininity And Identity Confusion, + Gender, Performativity, Masculinity, Social Construction Pages: 417-427 Article: + 36 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)36 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)36 + Download Pdf: download pdf view article Creative Commons License The Level + of Impulsivity and Aggression among Crystal Meth and Cannabis Users Authors: + Dr. Umbreen Khizar Muhammad Shafique Sana Nawab Abstract: Cannabis and crystal + meth use is pervading in our society. Present study was conducted to explore + the relationship between level of impulsivity and aggression among crystal + meth and cannabis users. The sample of the present study was comprised of + 100 participants. There were 50 cannabis and 50 crystal meth users who were + diagnosed on the basis of DSM-V without any comorbidity. The sample were taken + from all age range of population. The minimum education level was primary + and maximum education level was graduation and above. The sample was selected + from different drug rehabilitation centers of Rawalpindi and Islamabad, Pakistan. + Demographic Performa was used to collect the initial important information, + The \u201cBarratt Impulsiveness Scale was used to measure the impulsivity + and \u201cAggression Questionnaire\u201d were used to measure the level of + aggression. Finding of the study showed that there are significant differences + among crystal meth and cannabis users on level of aggression. The calculated + mean value for crystal meth user and for cannabis users indicates that crystal + meth users have higher level of aggression as compared to the cannabis user. + Over all analysis indicates a significant positive correlation of impulsivity + with the variable aggression. The alpha coefficient value for all scale is + acceptable. Keyswords: Aggression, Cannabis Users, Crystal Meth, Impulsivity + Pages: 428-439 Article: 37 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)37 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)37 Download Pdf: download + pdf view article Creative Commons License Impact of Social Factors on the + Status of Tribal Women: A Case Study of the (Erstwhile) Mohmand Agency Authors: + Sadia Jabeen Prof. Dr. Anwar Alam Muhammad Jawad Abstract: This study investigates + the impact of socio-economic and cultural factors on the status of tribal + women in the erstwhile Mohmand agency of the Ex-Federally Administered Tribal + Area (FATA), Pakistan. Cultural practices and illiteracy impede the role of + women in socio-economic development. The respondents were randomly selected + from tehsil Ekka Ghund and Pindialai with a sample size of 370, through stratified + random sampling. Data collected through structured interview schedule, FGD + and observation technique. The study reveals that tribal practices early marriages, + joint family system, tradition of forced marriages, compensation/Swara, exchange, + purchase marriages, hampers women\u2019s socioeconomic status. The illiteracy + rate is high among the tribal women and it further undermines their role and + negatively affects their socio-economic status. However, improvement in women + status needs peace and stability, reforms in the constitution for women empowerment + and active participation, improvement in the quality and quantity of education, + women employability, skills development and women entrepreneurship Keyswords: + Empowerment and Education, Marriage Types, Tribal Women Role, Tribal Women + Status, Violence against Women Pages: 440-455 Article: 38 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)38 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)38 + Download Pdf: download pdf view article Creative Commons License Effects of + Heavy School Bags on Students\u2019 Health at Primary Level in District Haveli + (Kahutta) Azad Jammu and Kashmir Authors: Dr. Muhammad Mushtaq Shamsa Rathore + Mishbah Saba Abstract: Heavy school bags is a very serious issue for the health + of the primary level students throughout the world particularly in Azad Jammu + and Kashmir. This study intends to explore the effect of heavy school bags + on students\u2019 health at primary level in district Kahuta. Naturally the + study was descriptive and survey method was used, the population consists + of one hundred ninety teachers and a sample of one hundred twenty seven teachers + was selected using non probability sampling technique. A likert scale questionnaire + was developed validated and distributed among the sampled respondents. The + researcher personally visited the schools and collected the filled questionnaire. + The data was coded and fed to the SPSS to analyze and interpret. The Chi Square + test was applied to see the effect of heavy school bags on student\u2019s + health and academic achievement. The study found that heavy bags have negative + effect on their health as well as their academic achievement. Students were + found complaining their sickness, body and back pain. They were also found + improper in their gait and their body postures. The researcher recommended + the policy makers to take and develop strategies to decrease the heavy school + bags. The school administration needs to make alternate days\u2019 time tables + of the subjects. Keyswords: Health, Primary Level, School, Bags, Students + Heavy Pages: 456-466 Article: 39 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)39 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)39 Download Pdf: download + pdf view article Creative Commons License Exploring the \u2018Civil Repair\u2019 + Function of Media: A Case Study of The Christchurch Mosques Shootings Authors: + Ayaz Khan Dr. Muhammad Junaid Ghauri Riffat Alam Abstract: This research endeavor + is an attempt to explore and analyze the discourse produced by The New Zealand + Herald; a newspaper from New Zealand and by The News International; a Pakistani + newspaper. The researchers intend to determine whether and to what extent + both the newspapers have the role of \u2018civil repair\u2019 played after + the Christchurch mosques shootings. The researchers have incorporated the + \u2018lexicalization\u2019 and the \u2018ideological square\u2019 techniques + proposed by Tuen A. van Dijk within the scope of Critical Discourse Analysis. + The findings of this study show that both the selected newspapers assuming + the social status of \u2018vital center\u2019 performed the role of \u2018civil + repair\u2019 in the aftermath of the shootings by producing the \u2018solidarity + discourse\u2019. The \u2018solidarity discourse\u2019 has been produced in + terms of the \u2018we-ness\u2019, harmony, understanding, and by mitigating + the conflicting opinions. Keyswords: Christchurch Mosque Shootings, Civil + Repair, Civil Sphere Theory, Lexicalization, Solidarity Discourse Pages: 467-484 + Article: 40 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)40 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)40 Download Pdf: download pdf + view article Creative Commons License China Pakistan Economic Corridor: Regional + Dominance into Peace and Economic Development Authors: Tayba Anwar Asia Saif + Alvi Abstract: The purpose of this qualitative study was to investigate the + true motivations behind CPEC idea and the advantages it delivers to Pakistan + and China. It also recognizes the Corridor''s potential for mixing regional + economies while dissolving geographical borders. The study is deductive in + character, since it examines financial, political, and military elements of + Pakistan and China''s positions and situations. Enhancing geographical linkages + through improved road, train, and air transport systems with regular and free + exchanges of development and individual\u2019s interaction, boosting through + educational, social, and regional civilization and wisdom, activity of larger + quantity of investment and commerce flow, generating and moving energy to + provide more optimal businesses for the region. Keyswords: Geographical Linkages, + Globalized World, Landlocked, Regional Connectivity, Regionalization Pages: + 485-497 Article: 41 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)41 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)41 Download Pdf: download + pdf view article Creative Commons License China\u2019s New Great Game in Central + Asia: Its Interest and Development Authors: Bushra Fatima Rana Eijaz Ahmad + Abstract: Central Asia is rich in hydrocarbon resources. It\u2019s geostrategic, + geopolitical, and geo-economic significance has grasped the attention of multiple + actors such as China, the USA, Russia, Turkey, the European Union, Pakistan, + Afghanistan, and India. Due to its location, the Central Asian region appeared + as a strategic hub. In the present scenario, China\u2019s strategy is massive + economic development, energy interest, peace, and stability. This article + highlights China\u2019s interest, political and economic development, and + its role as a major player in the New Great Game in Central Asia. Shanghai + Cooperation Organization (SCO) which presents as a platform where China is + playing an active role in political, economic, and security concerns for achieving + its objectives in Central Asia. The new step of the Belt and Road Initiative + (BRI) sheds light on China\u2019s progressive move in this region via land + and sea routes, which creates opportunities for globalization. Keyswords: + Belt and Road Initiative, Central Asia, China, New Great Game Pages: 498-509 + Article: 42 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)42 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)42 Download Pdf: download pdf + view article Creative Commons License Personality Traits as Predictors of + Self-Esteem and Death Anxiety among Drug Addicts Authors: Umbreen Khizar Saira + Irfan Iram Ramzan Abstract: This study seeks to investigate whether personality + traits predict self-esteem and death anxiety among drug addicts. The sample + consisted of 100 drug addicts taken from the two hospitals in Multan city. + Only men between the ages of 20 and 65 were included in the study. Data was + collected through reliable and valid questionnaires. Results revealed positive + relationship between conscientiousness, openness to experience and self-esteem. + Moreover, findings showed positive relationship between extraversion and death + anxiety, and negative correlation between neuroticism and death anxiety. Findings + also showed that self-esteem and death anxiety are significantly and negatively + correlated. Additionally, findings revealed that conscientiousness positively + predicted self-esteem and neuroticism negatively predicted death anxiety. + Furthermore, significant differences were observed in self-esteem, and death + anxiety based on age. Significant differences were also found in extraversion, + agreeableness, openness to experience, and death anxiety based on location. + Understanding how personality traits affect behavior can help drug addicts + get the support they need to live a better life and reduce their risk of death + anxiety and premature death. Keyswords: Death Anxiety, Drug Users, Personality + Traits, Self- Esteem Pages: 510-524 Article: 43 , Volume 2 , Issue 4 DOI Number: + 10.47205/jdss.2021(2-IV)43 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)43 + Download Pdf: download pdf view article Creative Commons License Middle East: + A Regional Instability Prototype Provoking Third Party Interventions Authors: + Waseem Din Prof. Dr. Iram Khalid Abstract: Third party interventions always + prolong the interstate or civil wars with unending sufferings and devastations. + The entire Middle East region is fraught with tensions, conflicts, civil wars + and rivalries. From strategic interests to power grabbing, sectarian divisions, + flaws in the civil and social structure of the state and society, ethnic insurrections, + and many other shapes of instability syndromes can be diagnosed in this region. + In the post-Arab Spring, 2011, the emerging new regional hierarchical order + for power/dominance, in addition to the weakening/declining dominant US power + in the region, changed the entire shape of already conflict-ridden region. + New weak or collapsing states and bifurcation of the \u2018status quo\u2019 + and \u2018counter-hegemonic\u2019 states along with their respective allies, + made this region a prototype of instability in the regional security complex + of the Middle East, as a direct result of these developments. The perpetuation + of these abnormalities would not recede this instability conundrum from the + region, provoking third party intervention, if not contained. Keyswords: Conflicts/Civil + Wars, Dominant Power, Instability, Intervention, Middle East, Middle Powers, + Regional Hierarchy, Regional Powers, Security Complex, Weak State Pages: 525-542 + Article: 44 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)44 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)44 Download Pdf: download pdf + view article Creative Commons License Impact of Classroom Environment on Second + Language Learning Anxiety Authors: Zohaib Zahid Abstract: Second language + learning anxiety has attained the attention of the researchers in almost every + part of the world. Pakistan is a country where English is taught as a second + language from the very beginning of school education. Second Language learning + anxiety is a phenomenon which has been prominently found among the learners + because of their less proficiency in learning English language. This study + has been conducted to investigate the effect of anxiety in learning and using + English language in classroom, university and outside the classroom. There + are variables that affect language learning performance of the learners but + this paper has solely investigated the effect of anxiety. The paper has concluded + that anxiety is a variable which has a striking affect in second language + learning and its use inside classrooms. Keyswords: Effect of Anxiety, Proficiency, + Second Language Learning Anxiety, Striking Affect Pages: 485-497 Article: + 45 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)45 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)45 + Download Pdf: download pdf view article Creative Commons License Struggling + for Democracy: A Case of Democratization in Pakistan Authors: Ammara Tariq + Cheema Dr. Rehana Saeed Hashmi Abstract: The objective of this research paper + is to review the challenges for democratization in Pakistan. The problem of + democratization and consolidation refers to the structure of democracy following + the collapse of non-democratic regime. Ten factors as given by Michael J. + Sodaro are considered effective in helping a democratically unstable state + to stabilize its system in other words helps in the democratic consolidation. + It is argued in this research that the ten factors of democratization as given + by Michael J. Sodaro have been absent in the political system of Pakistan + and working on these factors can lead Pakistan to the road of democratization. + This study uses qualitative method of research and proposes a novel framework + for the deed of parliament, because the effectiveness of parliament can contribute + positively to democratization/consolidated democracy. Keyswords: Electoral + Politics, General Elections, Political Participation, Women Empowerment Pages: + 554-562 Article: 46 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)46 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)46 Download Pdf: download + pdf view article Creative Commons License Impact of Dependency Ratio on Economic + Growth among Most Populated Asian Countries Authors: Dilshad Ahmad Salyha + Zulfiqar Ali Shah Abstract: Demographic transition through different channels + significantly influences economic growth. Malthusian view postulated as dependency + ratio adversely affects economic growth while Julian Simon''s view is quite + different, highlighted the long-run benefits of the population in the range + of 5 to15 years on economic growth. This study can be a valuable addition + in research to analyzing the association of dependency ratio and economic + growth of the five most populated Asian countries (Bangladesh, China, Indonesia, + India, and Pakistan). Empirical findings of the study indicated that a total + dependency and younger dependency ratio has a positive and significant influence + on economic growth in both short-run and long-run scenarios while the old + dependency ratio shows a negative influence on economic growth in the long + run while short-run results are unpredictable. There is a need for state-based + proper policy measures in focusing the higher financing in human capital development + specifically in education and health. Keyswords: Economic Growth, Gross Saving, + Old Dependency Ratio, Young Dependency Ratio Pages: 563-579 Article: 47 , + Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)47 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)47 + Download Pdf: download pdf view article Creative Commons License Chinese Geo-Strategic + Objectives and Economic Interests in Afghanistan under President Xi Jinping + Authors: Farooq Ahmed Prof. Dr. Iram Khalid Abstract: China has its own distinctive + interests, concerns and strategies with respect to the changing security dynamics + in Afghanistan. China has taken an active interest, though retaining a low + profile and avoiding direct military interaction. China has exclusively relished + on economic engagement actively and provided numerous financial aid and financial + support in the rebuilding of Afghanistan''s economy. The aim of this research + study is to analyze the geo-strategic objectives and economic interests of + China under the leadership of President Xi Jinping. This study looks at the + actual diplomatic, economic and protection commitments of both countries as + well as the basis of the geopolitical complexities \u2013 core variables that + form China''s current foreign policy to Afghanistan. Keyswords: Afghanistan, + BRI, China, NATO Withdrawal Pages: 580-592 Article: 48 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)48 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)48 + Download Pdf: download pdf view article Creative Commons License The Argument + Structure of Intransitive Verbs in Pashto Authors: Abdul Hamid Nadeem Haider + Bukhari Ghani Rehman Abstract: This study focuses on the description and categorization + of intransitive verbs in terms of its argument structure. The study concludes + that the unaccusative verbs only project an internal argument. It does not + require the event argument. However, the said verb can be causativised by + adding external argument and at the same time the event argument gets included + in the valency of the derived causative of the unaccusative root. The unergative, + on the other hand, requires an external argument as an obligatory argument + while the internal argument is not the obligatory argument of the verb. The + event argument is also a part of the valency of the verb. The APFs require + one argument which is the internal argument of the verb. However, since the + external argument is not available, the internal argument of the verb gets + realized as the subject of the verb. The verb does not project event argument. + The ergative predicates are derived by the suppression of the external argument + and by the externalization of the internal argument. Keyswords: Argument Structure, + Ergative Case, Event Argument, External Argument, Internal Argument, Valency + Pages: 593-610 Article: 49 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)49 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)49 Download Pdf: download + pdf view article Creative Commons License Positive, Negative and Criminal + Orientation of Beggars in Okara: Perspective of Students Authors: Shahzad + Farid Saif-Ur-Rehman Saif Abbasi Hassan Raza Abstract: This study aimed to + measure the perspective of students about the criminal orientation of beggars. + The sample size of the study (i.e., 100 students) was explored using Taro + Yamane\u2019 equation from the university of Okara, Punjab, Pakistan. The + respondents were approached using simple random sampling and interviewed using + face to face interview schedule. The data was collected using a structured + questionnaire. The analysis was administered through SPSS-20.The study explored + that parental illiteracy is associated with the high criminal and negative + orientation of students towards beggars. It was also explored that females + and respondents from rural background have low negative orientation towards + beggars. However, males and respondents from urban background have medium + criminal orientation and low positive orientation towards beggars, respectively. + The study is useful for the government of Punjab, Pakistan campaign and policy + for anti-begging. The study introduced the geometrical model of youth\u2019s + orientation toward begging. The study also contributed to the literature on + begging by extending its domain from Law and Criminology to sociology as it + incorporated social variables e.g., parents\u2019 education, gender, etc., + to explore their association with the youth\u2019s socialization about begging. + Keyswords: Begging, Crime, Education, Gender, Students Pages: 611-621 Article: + 50 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)50 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)50 + Download Pdf: download pdf view article Creative Commons License Relationship + between Entrepreneurial Export Orientation and Export Entrepreneurship through + Mediation of Entrepreneurial Capabilities Authors: Muhammad Saqib Nawaz Masood + ul Hassan Abstract: Export led growth is prominent paradigm in developing + world since decades. Exports play vital role in the economy by improving the + level of balance of payments, economic growth and employment. Due to strategic + importance of exports, organizational researchers focused on finding antecedents + of export performance of the organizations. To line with this, current study + aims to find the impact of entrepreneurial export orientation on export entrepreneurship + through mediation of entrepreneurial capabilities in the Pakistani context. + For this purpose, data was collected from 221 exporting firms of Pakistan + by using questionnaire. Collected data was analyzed with the help of Smart + PLS. In findings, measurement model confirmed the validity and reliability + of measures of variables. Additionally, structural model provides the positive + impact of entrepreneurial export orientation on export entrepreneurship. Similarly, + entrepreneurial capabilities mediate the relationship between entrepreneurial + export orientation on export entrepreneurship. The findings provide important + implications for the managers of exporting firms to improve export performance. + Keyswords: Entrepreneurial Capabilities, Entrepreneurial Export Orientation, + Export Entrepreneurship Pages: 622-636 Article: 51 , Volume 2 , Issue 4 DOI + Number: 10.47205/jdss.2021(2-IV)51 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)51 + Download Pdf: download pdf view article Creative Commons License China Pakistan + Economic Corridor: Explaining U.S-India Strategic Concerns Authors: Nasreen + Akhtar Dilshad Bano Abstract: Regional and International political and economic + landscape is being changed owing to China Pakistan Economic Corridor (CEPEC)-the + new security paradigm has taken place-that has increased the strategic concerns + of the U.S. and India. This research paper attempts to re-examine China-Pakistan + relations in the new emerging geo-political compass. This paper has investigated + the question that how regional, and global developments have impacted the + China-Pakistan relationship? And why China \u2013 Pakistan have become partners + of CPEC? In the global context, this paper assesses the emerging International + Order, Indo-U. S strategic narrative vis-\u00e0-vis CPEC, and the containment + of China through the new alliances and their impacts on China -Pakistan vis-\u00e0-vis + the Belt Road Initiative (BRI). Quadrilateral (Quad) alliances is shaping + the new strategic political and security paradigms in the world politics. + Keyswords: BRI, China, CPEC, India, Pakistan, Silk Road, Strategic Concerns + Pages: 637-649 Article: 52 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)52 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)52 Download Pdf: download + pdf view article Creative Commons License The Structure of Domestic Politics + and 1973 Constitution of Pakistan Authors: Dr. Fida Bazai Dr. Ruqia Rehman + Amjad Rashid Abstract: Pakistan is located in a pivotal region. Its geo-strategic + location affects its national identity as a nation state. Unlike Europe in + South Asia security dilemma, proxy warfare and nuclear arms race are consistent + features of the regional politics. The identity of Pakistan as security-centric + state gives its army disproportional power, which created institutional imbalance + that directly affected constitutionalism in the country. The constitution + of Pakistan is based on principles of civilian supremacy and separation of + power but in reality Pakistan\u2019s army is the most powerful institution + in country. This paper argues that the structure of Pakistani politics; created + institutional imbalances by the disproportionate distribution of resources + is the key variable in creating dichotomy. The structure of domestic politics + is based upon the principles of hostility to India, use of Islam for national + unity and strategic alliances with major powers to finance defense against + the neighboring countries. Keyswords: Constitutionalism, Identity, Islam, + South Asia Pages: 650-661 Article: 53 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)53 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)53 Download Pdf: download + pdf view article Creative Commons License National Integration and Regionalism + in Pakistan: Government\u2019s Strategy and Response toward Regionalist Demands + 1947-77 Authors: Najeeb ur Rehman Mohammad Dilshad Mohabbat Muhammad Wahid + Abstract: The countries of South Asian region have pluralistic societies with + different language, religious, and ethnic identities. Pakistan is no exception + who is facing the challenge of regionalism since its inception. Different + ethnic groups have been consistently raising their voices for separatism or + autonomy within the frame work of an existing territorial state. The issues + of provincialism, ethnicity, and regionalism is posing a serious challenge + to the integrity of the country. This paper aims to explore the causes of + the regionalism in Pakistan and intends to analyze the policies and strategies + of different political governments which they launched to tackle this all + important issue. The paper follows the historical method of research and analyzes + different types of qualitative data to conclude the finding of the research. + The paper develops the theory of \u201cRegionalists Demand and Government + Response\u201d which shows how different regionalist forces put their demands + and how the governments react on these demands. It recommends the grant of + greater regional autonomy to the regionalists to enhance internal security + and to protect the country from disintegration. Keyswords: Demands, Ethnicity, + Government Strategy, National Integrity, Nationalism, Regionalism Pages: 662-678 + Article: 54 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)54 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)54 Download Pdf: download pdf + view article Creative Commons License Fostering Entrepreneurial Mindset through + Entrepreneurial Education: A Qualitative Study Authors: Saira Maqbool Dr. + Qaisara Parveen Dr. Muhammad Hanif Abstract: Research on entrepreneurial mindset + has flourished in these recent years. Its significance lies in a critical + suspicion and its matters for inventive behavior. Entrepreneurship joined + with innovative abilities, seen as one of the most wanted in this day and + age. This study aims to determine the perceptions about entrepreneurial mindset, + its importance, and the role of entrepreneurship education and Training in + developing the entrepreneurial mindset. This is a qualitative study based + on interviews conducted by professors of Pakistan and Germany. The analysis + was determined through content analysis. The results determine that ''Making + Entrepreneurial Mindset'' assists with seeing better all parts of business + venture, which will undoubtedly influence their view of business venture, + pioneering abilities, and mentalities. Keyswords: Entrepreneurship Education, + Entrepreneurial Mindset Pages: 679-691 Article: 55 , Volume 2 , Issue 4 DOI + Number: 10.47205/jdss.2021(2-IV)55 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)55 + Download Pdf: download pdf view article Creative Commons License Benefits + of Implementing Single National Curriculum in Special Schools of Lahore city + for Children with Intellectual Disability: Teachers\u2019 Perception Authors: + Dr. Hina Fazil Khurram Rameez Sidra Ansar Abstract: Single national curriculum + (SNC) is an important issue across the Punjab Province of Pakistan. Making + and implementing SNC is not only focusing the education of normal pupils, + but also focusing students with disabilities (SWD). The field of special education + experienced an increased discussion of curriculum for students with intellectual + disabilities (SID). The present research aimed to know the benefits to implement + first stage of single national curriculum for students with Intellectual disability + and to know the differences about the benefits between public and private + schools regarding SNC for students with ID based on demographic characteristics. + Likert type researchers-made questionnaire with reliability) Cronbach alpha + .922) was used. 90 special educationists from public and private schools were + chosen through random sampling technique. The findings raised some benefits + such as: SNC will bridge the social and economic disparities which will increase + the acceptance of ID students. It was recommended that SNC should include + areas of adaptive skills, motor, and vocational skills to get involved in + work activities. Keyswords: Benefits, Children with Intellectual Disability, + Single National Curriculum Pages: 692-703 Article: 56 , Volume 2 , Issue 4 + DOI Number: 10.47205/jdss.2021(2-IV)56 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)56 + Download Pdf: download pdf view article Creative Commons License Last Rituals + and Problems Faced by the Hindu Community in Punjab: A Case Study of Lahore + Authors: Sabir Naz Abstract: Lahore is the provincial capital of Punjab, where + a sizeable population of the Hindus has been residing there since the inception + of Pakistan. There had been many crematoriums in the city but with the passage + of time, one after another, disappeared from the land after partition of the + Sub-continent. Those places were replaced by commercial or residential sites. + There is also a graveyard in the city which is in the use of Hindu Valmik + Sect. However, it was encroached by some Muslims due to very small size of + population and indolence of the Hindus. Later on, the encroachments were removed + by the District Government Lahore in compliance of order of the Supreme Court + of Pakistan. Presently, there is a graveyard as well as a crematorium in the + city. The community remained deprived of a place to dispose of a dead body + according to their faith for a long period which is contravention with the + guidelines of the Quaid-e-Azam, founder of the nation Keyswords: Crematorium, + Graveyard, Hindu community, Last Rituals Pages: 704-713 Article: 57 , Volume + 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)57 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)57 + Download Pdf: download pdf view article Creative Commons License Estimating + Growth Model by Non-Nested Encompassing: A Cross Country Analysis Authors: + Benish Rashid Dr. Shahid Razzaque Dr. Atiq ur Rehman Abstract: Whether models + are nested or non-nested it is important to be able to compare them and evaluate + their comparative results. In this study six growth models have been used + for analyzing the main determinants of economic growth in case of cross countries, + therefore by using these six models we have tested them for non-nested and + nested encompassing through Cox test and F-test respectively. Data from 1980 + to 2020 were used to analyze the cross country growth factors so therefore, + the current study looked at about forty four countries with modelling these + different comparative studies based on growth modelling. So, we can make these + six individual models and we can estimate the General Unrestricted Model with + the use of econometric technique of Non-Nested Encompassing. By evaluating + the data using the Non-Nested Encompassing econometric technique, different + sets of economic variables has been used to evaluate which sets of the economic + variables are important to boost up the growth level of the country. And found + that in case of nested model or full model it is concluded that model with + lag value of GDP, trade openness, population, real export, and gross fix capital + formation are the main and potential determinants to boost up the Economic + Growth in most of the countries. Keyswords: Cross Country, Economic Growth, + Encompassing, Nested, Non-nested Pages: 714-727 Article: 58 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)58 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)58 + Download Pdf: download pdf view article Creative Commons License Assessment + of Youth Buying Behaviour for Organic Food Products in Southern Punjab: Perceptions + and Hindrances Authors: Ayousha Rahman Asif Yaseen Muhammad Arif Nawaz Abstract: + This research examined the cognitive antecedental effects on organic food + purchase behaviour for understanding the perceptions and hindrances associated + with purchasing organic food products. Theory of Planned Behaviour (TPB) was + adopted as a theoretical framework. A total of 250 young consumers in the + two cities of Southern Punjab, Pakistan was randomly sampled and data were + collected via a face-to-face survey method. Partial least square technique + was employed to test the model. The results showed that attitude towards organic + food purchasing motivated when moral norms were activated to consume organic + food products. Further, environmental knowledge moderated the relationship + of organic food purchase intentions and behaviour significantly. The findings + highlighted the importance of moral norms as a meaningful antecedent that + could increase the TP-based psychosocial processes if consumers have sufficient + environmental knowledge. Therefore, farmers, organic products marketers, government + administrators, and food retailers should take initiatives not only to highlight + the norms and values but also when promoting organic food production and consumption. + Keyswords: Environmental Knowledge, Organic Food Purchase Behaviour, Personal + Attitude, PLS-SEM, Subjective & Moral Norms Pages: 728-748 Article: 59 + , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)59 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)59 + Download Pdf: download pdf view article Creative Commons License An Analysis + on Students Ideas about English and Urdu as Medium of Instructions in the + Subjects of Social Sciences studying in the Colleges of the Punjab, Pakistan + Authors: Ashiq Hussain Asma Amanat Abstract: The worth and usefulness of English + education as a foreign language is of great concern to language rule and planning + (LRP) researchers compared to teaching their native language globally in higher + education. The study under research examines the perspectives of two similar + groups of the final year students of at Higher Education Institutions of Pakistan. + The first group consists of art students who received the Urdu medium of instruction + (UMI), and the second group received the English medium of instruction (EMI). + An empirical methodology was carried out in the present year, students answered + questionnaires to find out the benefits and challenges of learning subject-based + knowledge, what subject-based knowledge means to them, and their understanding + of language as a teaching language. Interviews were conducted with the selected + group of students who wished to participate in research. Additional information + is available from the tests and results obtained in the two equivalent courses. + Although many similarities have been identified between the two groups, the + overall knowledge of disciplinary knowledge of English medium instruction + students was not very effective, while that of UMI students was very effective. + It explains the implications of the findings to continue the language rule + as policy experience for teaching in higher education institutions. Keyswords: + English as Medium of Instruction (EMI), Higher Education Institutions (HEIs), + Urdu as Medium of Instruction (UMI) Pages: 749-760 Article: 60 , Volume 2 + , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)60 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)60 + Download Pdf: download pdf view article Creative Commons License Environment + and Women in Kurt Vonnegut\u2019s \u2018Happy Birthday Wanda Juny\u2019: An + Eco- Critical and Feminist Analysis Authors: Dr. Muhammad Asif Safana Hashmat + Khan Muhammad Afzal Khan Janjua Abstract: This is an Eco-feminist study of + Vonnegut\u2019s \u2018Happy Birthday Wanda Juny\u2019 and focuses on how both + women and environment are exploited by patriarchy. Ecofeminism critiques masculine + dominance highlighting its role in creating and perpetuating gender discrimination, + social inequity and environmental degradation. Women suffer more because of + power disparity in society. Environmental crises affect women more than men + because of their already precarious existence and subaltern position. There + is affinity between women and nature are victims of climate change and other + environmental hazards. Cheryl Glotfelty introduced interdisciplinary approach + to the study of literature and environment. Literary ecology as an emerging + discipline explores the intriguing relationship between environment and literature. + Ecofeminism draws on feminist critique of gender inequality showing how gender + categories inscribed in power structure exploit both women and nature. Francoise + d\u2018Eaubonne coined the term ecofeminism to critique the prevalent exploitation + of both women and environment. Ecofeminism asserts that exploitation of women + and degradation of the environment are the direct result of male dominance + and capitalism. Ecofeminism argues for redressing the plight of women and + protection of environment. Vonnegut\u2019s play \u2018Happy Birthday Wanda + June\u2019 was written at a time when the movement for the right of women + and protection of environment were gaining momentum. The play shows how toxic + masculinity rooted in power and capitalism exploit both women and environment. + Keyswords: Eco-Feminism, Eco-Criticism, Ecology, Environment, Exploitation + Pages: 761-773 Article: 61 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)61 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)61 Download Pdf: download + pdf view article Creative Commons License Critical Analysis of Social Equity + and Economic Opportunities in the Light of Quranic Message Authors: Prof. + Dr. Muhammad Yousuf Sharjeel Mahnaz Aslam Zahida Shah Abstract: This study + critically evaluated the key verses of Surah Al-Baqarah -the second chapter + of Quran, a sacred scripture of Islam- which specifically relates to social + equity opportunities and a code of conduct in the context of economics. The + Quran claims that it is a book which explains every situation; therefore, + the aim of this study remained to extract those verses of Surah Al-Baqarah + which can guide us in Economics. The authentic and approved Islamic clerics + and their translations were consulted for the interpretations of the Holy + verses. The researchers chiefly focused and studied Surah Baqarah with regards + to social equity and economic opportunities. The translations were primarily + in the regional language Urdu so the interpretations must not be related exactly + equitable in English. The study engaged the document analysis research strategy. + This study is only an endeavour to decipher Holy Quran\u2019s message from + Allah for the mankind so it must not be considered as the full and complete + solution to the all the economic issues, challenges and opportunities. Ahadees + and the saying of the Holy prophet were referred to where ever required and + available. The researcher also considered the Tafasir (detail intellectual + interpretations) of the Quran done by the well-known scholars of Islam for + the verses studied therein and any statements and/or material - such as ideas, + studies, articles, documentation, data, reports, facts, statistics etc. For + the study, data was collected and analyzed qualitatively. On the basis of + the study, recommendations were also primed. Keyswords: Economic Issues and + Challenges, Social Equity, Surah Al-Baqarah, Al Quran Pages: 774-790 Article: + 62 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)62 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)62 + Download Pdf: download pdf view article Creative Commons License A Critical + Discourse Analysis of Dastak by Mirza Adeeb Authors: Muhammad Afzal Dr. Syed + Kazim Shah Umar Hayat Abstract: The present research aims to explore ideology + in Pakistani drama. The drama, \u201cDastak\u201d, written by Mirza Adeeb, + has been taken for exploration ideologically. Fairclough\u2019s (1992) three-dimensional + model has been used for analyzing the text of the above-mentioned drama which + includes textual, discursive practice and social practice analyses. The linguistic + and social analyses of the drama reveal the writer\u2019s ideology about socio-cultural, + conventional and professional aspects of life. The study has also explored + the past and present states of mind of Dr. Zaidi, the central and principal + character of the drama, Dastak. The text implies that the writer has conveyed + personal as well as social aspects of his times through the drama of Dastak. + Keyswords: Dastak, Drama, Ideology, Semiotics Pages: 791-807 Article: 63 , + Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)63 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)63 + Download Pdf: download pdf view article Creative Commons License Linking Job + Satisfaction to Employee Performance: The Moderating Role of Islamic Work + Ethics Authors: Dr. Shakira Huma Siddiqui Dr. Hira Salah ud din Khan Dr. Nabeel + Younus Ansari Abstract: The most pervasive concern in public sector organizations + is declining employee performance and workforce of these organizations are + less satisfied with their jobs. The aim of this study is to investigate the + impact of Job Satisfaction on employee\u2019s performance and how Islamic + work ethics moderates the above mentioned direct relationship in the public + sector organizations of Pakistan. The data were collected from the sample + of 193 permanent employees working in public sector organizations through + stratified sampling technique. The results revealed that employees Job satisfaction + is significantly related to higher performance. Further, the findings indicated + that Islamic work ethics moderates the relationship between job satisfaction + and employee performance. The present research has some theoretical and empirical + implications for academicians, policymakers, especially of public sector organizations, + for the improvement of performance of their workforce. Keyswords: Employee + Performance, Islamic Work Ethics, Job Satisfaction, Person-Environment Fit + Theory Pages: 808-821 Article: 64 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)64 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)64 Download Pdf: download + pdf view article Creative Commons License Semantics of Qawwali: Poetry, Perception, + and Cultural Consumption Authors: Rao Nadeem Alam Tayyaba Khalid Abstract: + Semantics is about meanings and meanings are arbitrary and shared. Understanding + qawwali context requires comprehension of semantics or process of meaning + creation and meaning sharing among the qawwal party and the audience. This + interactive activity might frequently be hindered when interrupted by subjective + meanings creation during cultural consumption. Qawwali is a cultural tradition, + its semantics are conditioned by axiological premises of poetry and perceptions + which are transforming. The previous researches revealed that qawwali is associated + with religion which provides the religious message by singing hamd and naat. + It was a means to experience Divine; therefore, semantics are multi-layered + and often crossroad with values and subjective experiences. It is novel due + to its ritual of Sama. It has the therapeutic power that helps mentally disturbed + people and they find refuge. This study is exploratory having a small sample + size of twenty purposively selected audiences. This phenomenological inquiry + used ethnographic method of conversational interviews at selected shrines + and cultural spaces in Islamabad. The results indicate that qawwali is a strong + refuge for people facing miseries of life and they attend Sama with a belief + that attending and listening will consequently resolve their issues, either + psychological or physiological. They participate in Sama which teaches them + how to be optimistic in a negative situation; this paper brings forth this + nodal phenomenon using the verbatim explanations by the interlocutors. Semantics + of Qawwali are conditioned and some of these elements are highlighted including + poetry and axiology based perceptions and cultural consumption of a cultural + realm. Keyswords: Cognition, Culture, Poetry, Qawwal, Qawwali, Semantics Pages: + 822-834 Article: 65 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)65 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)65 Download Pdf: download + pdf view article Creative Commons License Political Economy of Smuggling: + The Living Source for the Natives (A Case Study of Jiwani-Iran Border, Baluchistan) + Authors: Abdul Raheem Dr. Ikram Badshah Wasia Arshed Abstract: This study + explores the political economy of smuggling on Jiwani-Iran border. The natives + are majorly involved in illegal transportation of goods and objects, therefore; + the study sets to explain how significant smuggling for the local people is. + It describes the kinship role in reciprocity of their trade and transportation. + The qualitative methods such as purposive sampling and interview guide were + employed for data collection. The research findings revealed that local people + were satisfied with their illegal trading which is depended largely on their + expertise and know-how of smuggling at borders. They disclosed that their + total economy was predominantly based on smuggling of stuff like drugs, diesel, + oil, gas, petrol, ration food from Iran, and human trafficking. They also + enjoyed the privilege of possessing Sajjil (Iranian identity card), thus; + the dual nationality helped them in their daily business and rahdari (border + crossing agreement), enabling them to travel to Iran for multiple purposes. + Keyswords: Drugs, Human, Navigation, Political Economy, Reciprocity, Smuggling, + Trafficking Pages: 835-848 Article: 66 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)66 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)66 Download Pdf: download + pdf view article Creative Commons License The Vicious Circles of System: A + Kafkaesque Study of Kobo Abe\u2019s The Woman in the Dunes Authors: Imran + Aslam Kainat Azhar Abstract: This paper analyses the Kafkaesque/Kafkan features + of Kobo Abe\u2019s novel The Woman in the as formulated by Kundera in \u201cKafka\u2019s + World.\u201d For Kundera, in a Kafkaesque work human existence is bleakly + represented through intermingling of tragedy and comedy in an indifferent + world dominated by hegemonic systems. The Kafkaesque is characterised by the + following: World is a huge forking labyrinthine institution where the man + has been thrown to suffer its complexities, confrontation with the labyrinth + makes his existence meaningless because freedom is a taboo in no man\u2019s + land, he is punished for an unknown sin for which he seeks justification from + the superior authorities, but his efforts are viewed as ludicrous or comic + despite the underlying sense of tragedy. (5) The Kafkaesque tendency to present + tragic situation comically is also explored in Abe\u2019s novel. The paper + studies the effect of higher authorities exercising their power over man and + the inscrutability of cosmic structures continuously undermining human freedom + in nightmarish conditions. The paper establishes Kobo Abe in the literary + world as a writer who portrays the hollowness and futility of human lives + with a Kafkaesque touch. Keyswords: Authority, Institutions, Kafka, Kafkaesque, + Kafkan, Kobo Abe, Kundera, The Trial, The Woman in the Dune Pages: 849-861 + Article: 67 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)67 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)67 Download Pdf: download pdf + view article Creative Commons License Subjectivity and Ideological Interpellation: + An Investigation of Omar Shahid Hamid\u2019s The Spinner\u2019s Tale Authors: + Hina Iqbal Dr. Muhammad Asif Asia Saeed Abstract: Louis Althusser\u2019s concept + of interpellation is a process in which individuals internalize cultural values + and ideology and becomes subject. Althusser believes that ideology is a belief + system of a society in which ideological agencies establish hierarchies in + society through reinforcement and discrimination for cultural conditioning. + These agencies function through ideological state apparatuses. These ideological + agencies help to construct individual identity in society. The undesirable + ideologies promote repressive political agendas. The non-repressive ideologies + are inhaled by the individuals as a natural way of looking at the culture + and society. This research seeks to investigate Omar Shahid Hamid\u2019s novel + The Spinners Tales through the lens of Althusser\u2019s ideology and interpellation. + This study examines how the characters of Shahid\u2019s novel inhaled ideology + and became its subjects. This research also depicts the alarming effects of + cultural hegemony that creates cultural infidelity and hierarchies between + the bourgeoisie and proletariat classes. Keyswords: Cultural Hegemony, Ideological + State Apparatus, Ideology, Interpellation, Repressive Factors Pages: 862-872 + Article: 68 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)68 DOI + Link: http://doi.org/10.47205/jdss.2021(2-IV)68 Download Pdf: download pdf + view article Creative Commons License Blessing in Disguise: Recommendations + of Indian Education Commission (1882) and Christian Missionaries\u2019 Educational + Policy in the Colonial Punjab Authors: Mohammad Dilshad Mohabbat Muhammad + Hassan Muhammad Ayaz Rafi Abstract: Woods Education Despatch is considered + to be the Magna Carta of Indian Education. It controlled the Indian education + field till the establishment of Indian Education Commission, 1882. The Despatch + provided space to Christian missionaries by promising government\u2019s gradual + withdrawal from the education in favour of missionaries. It also facilitated + the missionaries by offering system of \u2018grants on aid\u2019 to the private + bodies. Consequently, the missionaries fancied to replace the government institutions + in the Punjab and initiated their efforts to increase the number of their + educational institutions. They tried to occupy the educational field by establishing + more and more educational institutions. But after the Recommendations of the + Indian Education Commission 1882, a change in their policy of numeric increase + of educational institutions is quite visible. With the turn of the century, + they are found to be eager to establish a few institutions with good quality + of education. This paper intends to analyse different factors behind the change + of their policy of quantitative dominance to qualitative improvement. It also + attempts to evaluate how their change of policy worked and what steps were + taken to improve the quality of their educational institutions. Following + the historical method qualitative data comprising educational reports, missionaries\u2019 + autobiographies, Reports of missionaries\u2019 conferences, and the other + relevant primary and secondary sources has been collected from different repositories. + The analysis of the data suggests that the attitude of the administration + of the education department and the recommendations of Indian Education Commission + were the major driving forces behind the change of missionaries\u2019 educational + policy in the 20th century. The missionaries, after adopting the new policy, + worked on the quality of education in their institutions and became successful. + Keyswords: Christian Missionaries, Indian Education Commission, Missionary + Schools, Numeric Increase, Quality of Education. The Punjab, Woods Education + Despatch Pages: 873-887 Article: 69 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)69 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)69 Download Pdf: download + pdf view article Creative Commons License Basic Life Values of Prospective + Special Education Teachers Authors: Dr. Maria Sohaib Qureshi Dr. Syeda Samina + Tahira Dr. Muhammad Irfan Arif Abstract: Future teachers'' preconceived values + about how to live their lives and how that affects the lives of their students + were the focus of this study. Descriptive research was used by the researchers. + The study was carried out by using Morris''s Ways to Live Scale. Researchers + used this scale to study prospective special education teachers'' gender, + social status, personal relationships, aesthetics and mental approach using + purposive sampling method. Descriptive and inferential stats were used to + analyse the data collected from those who participated in the study on basic + life values of prospective teachers. Results indicated that being social and + sympathetic are the most important values among prospective special education + teachers. It was also found that male and female prospective special education + teachers living in urban and rural areas had no significant differences in + their basic life values. Keyswords: Special Education, Teacher, Values Pages: + 888-896 Article: 70 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)70 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)70 Download Pdf: download + pdf view article Creative Commons License Perception of Dowry: Effects on + Women Rights in Punjab Authors: Dr. Bushra Yasmeen Dr. Muhammad Ramzan Dr. + Asma Seemi Malik Abstract: Dowry is a common tradition in south Asian countries, + especially in Pakistan and India. Daughters became curses and liability for + parents causing serious consequences. For control, there are legal ban/restrictions + (Dowry and Wedding Gifts (Restriction) Act, 1976; Amendment in Act, 1993) + on its practice in Pakistan. Despite the legal cover, the custom has been + extended. Dowry amount seems to be increasing due to changing lifestyle and + trends of society. To understand males\u2019 and females\u2019 perceptions + about dowry; impacts of dowry; why dowry is essential; and how it is affecting + women\u2019s rights and eventually affecting women\u2019s autonomy. A qualitative + study was conducted. Data was collected by using unstructured interviews from + males and females including social activists, economists, and married couples + about wedding expenses, demands, society pressure, men\u2019s support, and + perception against dowry especially with regards to women\u2019s rights and + autonomy. The study concluded heavy dowry especially in terms of furniture, + electronics, kitchenware, car, furnished houses, and cash highly associated + with women\u2019s development and their rights. General people\u2019s perception + showed that dowry is no longer remained a custom or tradition in Asian countries. + It is just a trend and people follow it as a symbol of respect for parents + and women as well. Keyswords: Dowry, Effects, Impacts Of Dowry, Perceptions, + Women Autonomy, Women Rights Pages: 897-909 Article: 71 , Volume 2 , Issue + 4 DOI Number: 10.47205/jdss.2021(2-IV)71 DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)71 + Download Pdf: download pdf view article Creative Commons License NCOC-An Emblem + of Effective Governance: An analysis of Pakistan\u2019s Counter Strategy for + Covid-19 as a Non-Traditional Security Challenge Authors: Dr. Iram Khalid + Abstract: COVID -19 affected the world unprecedentedly. Lack of capacity and + poor standards of governance caused nontraditional security challenges to + Pakistan too. The NCOC is the central nerve center to guide the national response + to COVID-19 by Pakistan and can be best analyzed in the light of the decision-making + theory of Naturalist Decision Making (NDM). The study points out the effective + role performed by NCOC at policy formation through a more prosaic combination + of science, data, decision making and execution of decisions at the level + of federalism. The study highlights the changing patterns of government\u2019s + approach during the pandemic at various levels. Pakistan faced economic, political + and social crisis during this phase. This study uses a survey and key informant + interviews as the source of analysis for qualitative data collection. By applying + the decision- making theory, the paper extends that there is a need to use + a model to balance the existing gap within the system, to meet challenges. + The study suggests a coordinating approach among various units and center; + that might raise the level of performance to meet the nontraditional security + challenges with innovation, creativity and boldness. Keyswords: COVID-19, + Decision Making Theory, Governance, Nontraditional Threats, Strategy Pages: + 910-930 Article: 72 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)72 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)72 Download Pdf: download + pdf view article Creative Commons License Comparative Implications of Wednesbury + Principle in England and Pakistan Authors: Safarat Ahmad Ali Shah Dr. Sara + Qayum Arzoo Farhad Abstract: Wednesbury principle is one of the most important + and useful grounds of the Judicial Review. Judicial review is a remedy provided + by the public law and is exercised by the superior and higher courts to supervise + administrative authorities'' powers and functions. The main objective of the + judicial review is to ensure the fair and transparent treatment of individuals + by public authorities. The ground of the judicial review, i.e., Unreasonableness + or irrationality or popularly known as Wednesbury Unreasonableness was introduced + by lord Greene in the Wednesbury Corporation case in 1948. Initially, the + scope of this ground of judicial review was very narrow and was allowed only + in rare cases. However, with the development of administrative law and Human + rights, it also developed. Its development resulted in different controversies + and issues about the application of this ground. The main issue is about its + encroachment in the jurisdiction of other branches of the government i.e., + the parliament and executive. The free and loose application of this principle + results in confusion and conflict between different organs of the government. + The present paper is based on the implications of the limitations on the ground + of Wednesbury Unreasonableness both on the judicial and administrative bodies + in Pakistan to avoid the chaos and confusion that results in the criticisms + on this ground of judicial review. Keyswords: Administrative Authorities, + Critical Analysis, Illegality, Judicial Review, Pakistan, Wednesbury Unreasonableness + Pages: 931-946 Article: 73 , Volume 2 , Issue 4 DOI Number: 10.47205/jdss.2021(2-IV)73 + DOI Link: http://doi.org/10.47205/jdss.2021(2-IV)73 Download Pdf: download + pdf view article Creative Commons License Water Sharing Issues in Pakistan: + Impacts on Inter-Provincial Relations","updated":"2022-09-19T20:05:00.111001","year":2021,"z_authors":null},"score":8.0090365e-07,"snippet":"(2021) + Volume 2, Issue 4 Cultural Implications of China Pakistan Economic Corridor + (CPEC Authors: Dr"}]} + + ' headers: Access-Control-Allow-Headers: - - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, - Accept-Ranges, Cache-Control + - origin, content-type, accept, x-requested-with + Access-Control-Allow-Methods: + - POST, GET, OPTIONS, PUT, DELETE, PATCH Access-Control-Allow-Origin: - "*" - Access-Control-Expose-Headers: - - Link Connection: - - close + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - "39585" + Content-Type: + - application/json Date: - - Tue, 13 Aug 2024 18:33:59 GMT + - Wed, 04 Sep 2024 22:54:22 GMT + Nel: + - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' + Report-To: + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1725490461&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=j0vwwBQJBKSjl7xJkMYB4ksAyr6H3jKPUYltKx2FaWA%3D"}]}' + Reporting-Endpoints: + - heroku-nel=https://nel.heroku.com/reports?ts=1725490461&sid=c46efe9b-d3d2-4a0c-8c76-bfafa16c5add&s=j0vwwBQJBKSjl7xJkMYB4ksAyr6H3jKPUYltKx2FaWA%3D Server: - - Jetty(9.4.40.v20210413) - Transfer-Encoding: - - chunked - permissions-policy: - - interest-cohort=() - x-api-pool: - - plus - x-rate-limit-interval: - - 1s - x-rate-limit-limit: - - "150" - x-ratelimit-interval: - - 1s - x-ratelimit-limit: - - "150" + - gunicorn + Vary: + - Accept-Encoding + Via: + - 1.1 vegur status: code: 200 message: OK @@ -417,7 +1969,7 @@ interactions: "11116106", "DOI": "10.1038/s42256-024-00832-8", "CorpusId": 258059792, "PubMed": "38799228"}, "url": "https://www.semanticscholar.org/paper/354dcdebf3f8b5feeed5c62090e0bc1f0c28db06", "title": "Augmenting large language models with chemistry tools", "venue": - "Nat. Mac. Intell.", "year": 2023, "citationCount": 187, "influentialCitationCount": + "Nat. Mac. Intell.", "year": 2023, "citationCount": 191, "influentialCitationCount": 12, "isOpenAccess": true, "openAccessPdf": {"url": "https://www.nature.com/articles/s42256-024-00832-8.pdf", "status": "HYBRID"}, "publicationTypes": ["JournalArticle"], "publicationDate": "2023-04-11", "journal": {"name": "Nature Machine Intelligence", "pages": @@ -430,7 +1982,7 @@ interactions: M Bran"}, {"authorId": "2161337138", "name": "Sam Cox"}, {"authorId": "1820929773", "name": "Oliver Schilter"}, {"authorId": "2251414370", "name": "Carlo Baldassari"}, {"authorId": "2150199535", "name": "Andrew D. White"}, {"authorId": "1379965853", - "name": "P. Schwaller"}], "matchScore": 181.37788}]} + "name": "P. Schwaller"}], "matchScore": 179.3787}]} ' headers: @@ -439,31 +1991,77 @@ interactions: Connection: - keep-alive Content-Length: - - "1551" + - "1550" Content-Type: - application/json Date: - - Tue, 13 Aug 2024 18:34:01 GMT + - Wed, 04 Sep 2024 22:54:22 GMT Via: - - 1.1 2896f6be77233cf3f24b7a1aaae1c6f2.cloudfront.net (CloudFront) + - 1.1 62c71b579b931f194fbc7abcc843d132.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - jArWyr3rFLmJjudRoMU1SmrmySKA3uObh6sqBSPt_3sUKPl5Rxam4A== + - VwbnXhY_A5M45lRneO76qNYDmL4RnBGAb0pjfkEcjJruMewyZUvaQQ== X-Amz-Cf-Pop: - - IAD55-P4 + - SFO53-C1 X-Cache: - Miss from cloudfront x-amz-apigw-id: - - cdcLsGaQvHcEOxA= + - dmi8sEt4vHcEvHw= x-amzn-Remapped-Connection: - keep-alive x-amzn-Remapped-Content-Length: - - "1551" + - "1550" x-amzn-Remapped-Date: - - Tue, 13 Aug 2024 18:34:01 GMT + - Wed, 04 Sep 2024 22:54:22 GMT x-amzn-Remapped-Server: - gunicorn x-amzn-RequestId: - - 2347c943-5dab-4e0f-b1e3-d3d152e38b92 + - 797ca22d-1ce8-48ae-b214-3271d15e4a9e + status: + code: 200 + message: OK + - request: + body: null + headers: {} + method: GET + uri: https://api.crossref.org/works/10.1038%2Fs42256-024-00832-8/transform/application/x-bibtex + response: + body: + string: + " @article{M_Bran_2024, title={Augmenting large language models with + chemistry tools}, volume={6}, ISSN={2522-5839}, url={http://dx.doi.org/10.1038/s42256-024-00832-8}, + DOI={10.1038/s42256-024-00832-8}, number={5}, journal={Nature Machine Intelligence}, + publisher={Springer Science and Business Media LLC}, author={M. Bran, Andres + and Cox, Sam and Schilter, Oliver and Baldassari, Carlo and White, Andrew + D. and Schwaller, Philippe}, year={2024}, month=may, pages={525\u2013535} + }\n" + headers: + Access-Control-Allow-Headers: + - X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, + Accept-Ranges, Cache-Control + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Link + Connection: + - close + Date: + - Wed, 04 Sep 2024 22:54:22 GMT + Server: + - Jetty(9.4.40.v20210413) + Transfer-Encoding: + - chunked + permissions-policy: + - interest-cohort=() + x-api-pool: + - plus + x-rate-limit-interval: + - 1s + x-rate-limit-limit: + - "150" + x-ratelimit-interval: + - 1s + x-ratelimit-limit: + - "150" status: code: 200 message: OK diff --git a/tests/conftest.py b/tests/conftest.py index f52d7170..c307e3d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,17 +1,123 @@ +from __future__ import annotations + +import logging +import os +import shutil +from collections.abc import Generator, Iterator +from pathlib import Path +from typing import Any +from unittest.mock import patch + import pytest from dotenv import load_dotenv from paperqa.clients.crossref import CROSSREF_HEADER_KEY from paperqa.clients.semantic_scholar import SEMANTIC_SCHOLAR_HEADER_KEY +from paperqa.settings import Settings +from paperqa.types import Answer +from paperqa.utils import setup_default_logs + +PAPER_DIRECTORY = Path(__file__).parent @pytest.fixture(autouse=True, scope="session") -def _load_env(): +def _load_env() -> None: load_dotenv() -@pytest.fixture(scope="session") -def vcr_config(): +@pytest.fixture(autouse=True) +def _setup_default_logs() -> None: + setup_default_logs() + + +@pytest.fixture(scope="session", name="vcr_config") +def fixture_vcr_config() -> dict[str, Any]: return { "filter_headers": [CROSSREF_HEADER_KEY, SEMANTIC_SCHOLAR_HEADER_KEY], + "record_mode": "none", + "match_on": ["method", "host", "path", "query"], + "allow_playback_repeats": True, + "cassette_library_dir": "tests/cassettes", } + + +@pytest.fixture +def tmp_path_cleanup(tmp_path: Path) -> Iterator[Path]: + yield tmp_path + # Cleanup after the test + if tmp_path.exists(): + shutil.rmtree(tmp_path, ignore_errors=True) + + +@pytest.fixture +def agent_home_dir( + tmp_path_cleanup: str | os.PathLike, +) -> Generator[str | os.PathLike, None, None]: + """Set up a unique temporary folder for the agent module.""" + with patch.dict("os.environ", {"PQA_HOME": str(tmp_path_cleanup)}): + yield tmp_path_cleanup + + +@pytest.fixture +def agent_index_dir(agent_home_dir: Path) -> Path: + return agent_home_dir / ".pqa" / "indexes" + + +@pytest.fixture(scope="session", name="stub_data_dir") +def fixture_stub_data_dir() -> Path: + return Path(__file__).parent / "stub_data" + + +@pytest.fixture +def agent_test_settings(agent_index_dir: Path, stub_data_dir: Path) -> Settings: + settings = Settings( + paper_directory=stub_data_dir, + index_directory=agent_index_dir, + embedding="sparse", + ) + settings.agent.search_count = 2 + settings.answer.answer_max_sources = 2 + settings.answer.evidence_k = 10 + return settings + + +@pytest.fixture +def agent_stub_answer() -> Answer: + return Answer(question="What is is a self-explanatory model?") + + +@pytest.fixture +def stub_data_dir_w_near_dupes(stub_data_dir: Path, tmp_path: Path) -> Iterator[Path]: + + # add some near duplicate files then removes them after testing + for filename in ("bates.txt", "obama.txt"): + if not (tmp_path / f"{filename}_modified.txt").exists(): + with (stub_data_dir / filename).open() as f: + content = f.read() + with (tmp_path / f"{Path(filename).stem}_modified.txt").open("w") as f: + f.write(content) + f.write("## MODIFIED FOR HASH") + + yield tmp_path + + if tmp_path.exists(): + shutil.rmtree(tmp_path, ignore_errors=True) + + +@pytest.fixture +def reset_log_levels(caplog): + logging.getLogger().setLevel(logging.DEBUG) + + for name in logging.root.manager.loggerDict: + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + logger.propagate = True + + caplog.set_level(logging.DEBUG) + + yield + + for name in logging.root.manager.loggerDict: + logger = logging.getLogger(name) + logger.setLevel(logging.NOTSET) + logger.propagate = True diff --git a/tests/stub_data/bates.txt b/tests/stub_data/bates.txt new file mode 100644 index 00000000..5662babe --- /dev/null +++ b/tests/stub_data/bates.txt @@ -0,0 +1,838 @@ + + + + +Frederick Bates (politician) - Wikipedia + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Jump to content +
+
+
+ + + + +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +

Frederick Bates (politician)

+ +
+ + + +
+
+
+
+
+ +
+
+ + + +
+
+
+
+
+ + +
+
+
+
+
+
+ +
From Wikipedia, the free encyclopedia
+
+
+ + +
+
Frederick Bates
2nd Governor of Missouri
In office
November 15, 1824 – August 4, 1825
LieutenantBenjamin Harrison Reeves
Preceded byAlexander McNair
Succeeded byAbraham J. Williams
+
Personal details
Born(1777-06-23)June 23, 1777
Belmont, Goochland County, Virginia, U.S.
DiedAugust 4, 1825(1825-08-04) (aged 48)
Chesterfield, Missouri, U.S.
SpouseNancy Opie Ball
RelationsEdward (brother), James (brother); see Bates extended family
ChildrenEmily Caroline (1820–1891), Lucius Lee (1821–1898), Woodville (1823–1840) and Frederick Jr. (1826–1862)
+

Frederick Bates (June 23, 1777 – August 4, 1825), was an American attorney and politician. He was elected in 1824 as the second governor of Missouri and died in office in 1825. Before that he had served as a justice of the Territorial Supreme Court for Michigan Territory, was appointed by Thomas Jefferson as Secretary of the Louisiana Territory and started to build his political base in St. Louis. +

+ +

Early life and education[edit]

+

Frederick Bates was born in 1777 into the slave-holding class in Goochland County, Virginia. Though a Quaker, Bates and his family were lifelong slaveholders. Bates was schooled privately at his family's Belmont plantation by tutors. Later he went to college and read the law with an established firm. He settled in Detroit in 1797 and became its first postmaster in 1803.[1] He was a member of the Bates family along with his brothers Edward and James Woodson Bates.[2] +

+

Career[edit]

+

After working as an attorney, Bates started his political career when appointed as a justice of the Territorial Supreme Court for Michigan Territory in Detroit. He received a significant promotion when the Aaron Burr conspiracy was uncovered. In February 1807, while Bates was in Washington, President Thomas Jefferson appointed him to be Secretary of the Louisiana Territory, as well as a recorder of land titles.[3] As secretary, he was also commander in chief of the militia.[4] He held this position in St. Louis until 1812. Bates helped determine whether conflicting French, Spanish, and American land claims in the territory would be upheld, as it had been subject to three differing political systems.[5] +

Jefferson had already decided on the returning explorer and fellow Virginian Meriwether Lewis as governor of the huge new Louisiana Territory, which approximately equaled the size of the existing United States. Bates preceded Lewis to St. Louis and became a powerful political force in the new territory; he was a political rival of Lewis until the latter's death while traveling from St. Louis to Washington on business in 1809. Later, as Secretary of the newly formed Missouri Territory (1812–1821), he became acting governor in the frequent absences of Territorial Governor William Clark.[3] +

In the August 1824 election, Bates was elected the second governor of Missouri. He died in office in August 1825 in Chesterfield, Missouri, due to a short illness thought to be pneumonia. Bates was buried at the family cemetery on the Thornhill estate near St. Louis. +

+

Marriage and family[edit]

+
Main house at Thornhill, Governor Bates estate.
+

In 1819, Bates married Nancy Opie Ball (1802–1877), daughter of a wealthy Virginia colonel. The couple had four children, Emily Caroline (1820–1891), Lucius Lee (1821–1898), Woodville (1823–1840) and Frederick Jr. (1826–1862).[6] +

During his time in Missouri, Bates acquired nearly 1000 acres (4 km2) of land, which he called Thornhill. He also acquired several enslaved men, women, and children. He had built a Federal-style home with high ceilings for summer ventilation, fine woodwork and a sophisticated floor plan; all this would have been familiar to Bates from his childhood home, Belmont, in Goochland County, Virginia. The Thornhill estate still exists today and can be viewed by the public. It is located in Faust County Park in Chesterfield, Missouri. +

+

Legacy and honors[edit]

+

Governor Bates is the namesake of Bates County, Missouri.[7] +

+

References[edit]

+
+
    +
  1. ^ Dunbar, Willis F. & May, George S. (3d ed. 1995). Michigan: A History of the Wolverine State, p. 113. Wm. B. Eerdmans Publishing Co. +
  2. +
  3. ^ Mercer, Thomas (1990). Bates: A Familial Journey. Boston Herald Publ. +
  4. +
  5. ^ a b William E. Foley (12 March 2014). The Genesis of Missouri: From Wilderness Outpost to Statehood. University of Missouri Press. pp. 215–. ISBN 978-0-8262-6053-6. +
  6. +
  7. ^ Salter, William. "The life of Henry Dodge, from 1782 to 1833: with portrait by George Catlin and maps of the battles of the Pecatonica and Wisconsin Heights in the Black Hawk War". American West. Adam Matthew Digital. p. 3. Retrieved 5 April 2023. +
  8. +
  9. ^ Henry Putney Beers (1 December 1989). French and Spanish Records of Louisiana: A Bibliographical Guide to Archive and Manuscript Sources. LSU Press. pp. 281–. ISBN 978-0-8071-2793-3. +
  10. +
  11. ^ William Smith Bryan; Macgunnigle (1 June 2009). Rhode Island Freemen, 1747-1755. Genealogical Publishing Com. pp. 131–. ISBN 978-0-8063-0753-4. +
  12. +
  13. ^ Eaton, David Wolfe (1916). How Missouri Counties, Towns and Streams Were Named. The State Historical Society of Missouri. pp. 208. +
  14. +
+

External links[edit]

+ + + + + + + + + + + + +
Political offices +
Preceded by + Governor of Missouri
1824–1825 +
Succeeded by +
+ + + + + + +
+
+ +
+
+ +
+ +
+
+
+
    +
  • +
  • +
+
+ + + + diff --git a/tests/stub_data/flag_day.html b/tests/stub_data/flag_day.html new file mode 100644 index 00000000..d272df5b --- /dev/null +++ b/tests/stub_data/flag_day.html @@ -0,0 +1,3047 @@ + + + + + National Flag of Canada Day - Wikipedia + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jump to content +
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +

+ National Flag of Canada Day +

+ +
+ + +
+
+ + +
+
+
+
+
+
+
+ +
+
+ + + +
+
+
+
+
+ + +
+
+
+
+
+ +
+ From Wikipedia, the free encyclopedia +
+
+
+ +
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ National Flag of Canada Day +
+ +
+ The national flag of Canada +
+
+ Observed by + + Canada +
Date + February 15 +
+ Next time + + February 15, 2025 (2025-02-15) +
FrequencyAnnual
+

+ National Flag of Canada Day (French: Jour du drapeau national du Canada), + commonly shortened to Flag Day, is observed annually + on February 15 to commemorate the inauguration of the + flag of Canada + on that date in 1965.[1] + The day is marked by flying the flag, occasional public + ceremonies and educational programs in schools. It is not a + public holiday, although there has been discussion about creating one. +

+ +
+

History

+ [edit] +
+
+

Background

+ [edit] +
+

+ Amid + much controversy, the + Parliament of Canada + in 1964 voted to adopt a new design for the + Canadian flag + and issued a call for submissions.[2] +

+

+ This flag would replace the + Canadian Red Ensign, which had been, with various successive alterations, in + conventional use as the national flag of + Canada since 1868. + Nearly 4,000 designs were submitted by Canadians.[2] + On October 22, 1964, the + Maple Leaf flag—designed by historian + George Stanley—won with a unanimous vote.[3] + Under the leadership of + Prime Minister + Lester Pearson, resolutions recommending the new design were passed by + the + House of Commons + on December 15, 1964, and by the + Senate + two days later.[4] +

+

+ The flag was proclaimed by + Elizabeth II, + Queen of Canada, on January 28, 1965,[3][5] + and took effect "upon, from and after" February 15 that + year.[6] +

+
+

Flag Day

+ [edit] +
+

+ National Flag of Canada Day was instituted in 1996 by an + Order in Council + from + Governor General + Roméo LeBlanc, on the initiative of Prime Minister + Jean Chrétien.[7] + At the first Flag Day ceremony in + Hull, Quebec, Chrétien was confronted by demonstrators against proposed + cuts to the + unemployment insurance + system, and while walking through the crowd he was + grabbed by the neck and pushed aside + a protester who had approached him. +

+

+ In 2010, on the flag's 45th anniversary, federal ceremonies + were held to mark Flag Day at + Ottawa, + Winnipeg, + St. John's, and at + Whistler + and + Vancouver in + conjunction with the + 2010 Winter Olympics + in Vancouver.[8] + In 2011, Prime Minister + Stephen Harper + observed Flag Day by presenting two citizens, whose work + honoured the + military, with Canadian flags that had flown over the + Peace Tower. It was announced as inaugurating an annual recognition of + patriotism.[9] +

+
+

See also

+ [edit] +
+ + + +
+

Footnotes

+ [edit] +
+ +
+
    +
  1. + ^ + Department of Canadian Heritage. + "Ceremonial and Canadian Symbols Promotion > + The National Flag of Canada". Queen's Printer for Canada. Archived from + the original + on April 23, 2010. Retrieved + March 21, 2010. +
  2. +
  3. + ^ + a + b + Government of Canada, Public Services and + Procurement Canada (July 31, 2015). + "Infographic: National Flag of Canada Day – + February 15 – Canada's Parliamentary Precinct – + PWGSC". www.tpsgc-pwgsc.gc.ca. Retrieved + February 5, 2022. +
  4. +
  5. + ^ + a + b + "What is the National Flag Day of Canada?". westernfinancialgroup.ca. Retrieved + February 5, 2022. +
  6. +
  7. + ^ + Department of Canadian Heritage. + "Ceremonial and Canadian Symbols Promotion > + The National Flag of Canada > Birth of the + Canadian flag". Queen's Printer for Canada. + Archived + from the original on February 24, 2010. Retrieved + March 21, 2010. +
  8. +
  9. + ^ + "Birth of the Canadian flag". + Department of Canadian Heritage. + Archived + from the original on December 20, 2008. Retrieved + December 16, + 2008. +
  10. +
  11. + ^ + Conserving the Proclamation of the Canadian Flag + Archived + October 21, 2012, at the + Wayback Machine, Library and Archives of Canada, from John Grace in + The Archivist, National Archives, Ottawa, 1990. + Retrieved February 15, 2011. +
  12. +
  13. + ^ + Department of Canadian Heritage. + "National Flag of Canada Day". Queen's Printer for Canada. + Archived + from the original on February 17, 2010. Retrieved + March 21, 2010. +
  14. +
  15. + ^ + Dept. of Canadian Heritage news release + Archived + July 6, 2011, at the + Wayback Machine, February 15, 2010. Retrieved February 15, + 2011. +
  16. +
  17. + ^ + PM pays tribute to outstanding Canadians on Flag + Day + Archived + July 6, 2011, at the + Wayback Machine, Prime Minister's Office news release. Retrieved + February 16, 2011. +
  18. +
+
+
+ + [edit] +
+ + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
    +
  • +
+
+ + + + diff --git a/tests/stub_data/obama.txt b/tests/stub_data/obama.txt new file mode 100644 index 00000000..fb0d19eb --- /dev/null +++ b/tests/stub_data/obama.txt @@ -0,0 +1,5259 @@ + + + + +Barack Obama - Wikipedia + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Jump to content +
+
+
+ + + + +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +

Barack Obama

+ +
+ + +
+ +
+ + + +
+ +
+
+
+
+
+
+ +
+
+ + + +
+
+
+
+
+ + +
+
+
+
+
+
Page semi-protected
+
+ +
From Wikipedia, the free encyclopedia
+
+
+ + +
+ +

+ + +

+
Barack Obama
Obama standing in the Oval Office with his arms folded and smiling
Official portrait, 2012
44th President of the United States
In office
January 20, 2009 – January 20, 2017
Vice PresidentJoe Biden
Preceded byGeorge W. Bush
Succeeded byDonald Trump
United States Senator
from Illinois
In office
January 3, 2005 – November 16, 2008
Preceded byPeter Fitzgerald
Succeeded byRoland Burris
Member of the Illinois Senate
from the 13th district
In office
January 8, 1997 – November 4, 2004
Preceded byAlice Palmer
Succeeded byKwame Raoul
+
Personal details
Born
Barack Hussein Obama II

(1961-08-04) August 4, 1961 (age 62)
Honolulu, Hawaii, U.S.
Political partyDemocratic
Spouse +
(m. 1992)
Children
Parents
RelativesObama family
Education
Occupation
+
  • Politician
  • +
  • lawyer
  • +
  • author
+
AwardsFull list
SignatureCursive signature in ink
Website
+ + +

Barack Hussein Obama II[a] (born August 4, 1961) is an American politician who served as the 44th president of the United States from 2009 to 2017. As a member of the Democratic Party, he was the first African-American president in United States history. Obama previously served as a U.S. senator representing Illinois from 2005 to 2008, as an Illinois state senator from 1997 to 2004, and as a community service organizer, civil rights lawyer, and university lecturer. +

Obama was born in Honolulu, Hawaii. He graduated from Columbia University in 1983 with a Bachelor of Arts degree in political science and later worked as a community organizer in Chicago. In 1988, Obama enrolled in Harvard Law School, where he was the first black president of the Harvard Law Review. He became a civil rights attorney and an academic, teaching constitutional law at the University of Chicago Law School from 1992 to 2004. He also went into elective politics; Obama represented the 13th district in the Illinois Senate from 1997 until 2004, when he successfully ran for the U.S. Senate. In 2008, after a close primary campaign against Hillary Clinton, he was nominated by the Democratic Party for president and chose Delaware Senator Joe Biden as his running mate. Obama was elected president, defeating Republican Party nominee John McCain in the presidential election, and was inaugurated on January 20, 2009. Nine months later he was named the 2009 Nobel Peace Prize laureate, a decision that drew a mixture of criticism and praise. +

Obama's first-term actions addressed the global financial crisis and included a major stimulus package to guide the economy in recovering from the Great Recession, a partial extension of George W. Bush's tax cuts, legislation to reform health care, a major financial regulation reform bill, and the end of a major U.S. military presence in Iraq. Obama also appointed Supreme Court justices Sonia Sotomayor and Elena Kagan, the former being the first Hispanic American on the Supreme Court. He ordered the counterterrorism raid which killed Osama bin Laden and downplayed Bush's counterinsurgency model, expanding air strikes and making extensive use of special forces, while encouraging greater reliance on host-government militaries. Obama also ordered military involvement in Libya in order to implement UN Security Council Resolution 1973, contributing to the overthrow of Muammar Gaddafi. +

After winning re-election by defeating Republican opponent Mitt Romney, Obama was sworn in for a second term on January 20, 2013. In his second term, Obama took steps to combat climate change, signing a major international climate agreement and an executive order to limit carbon emissions. Obama also presided over the implementation of the Affordable Care Act and other legislation passed in his first term. He negotiated a nuclear agreement with Iran and normalized relations with Cuba. The number of American soldiers in Afghanistan fell dramatically during Obama's second term, though U.S. soldiers remained in the country throughout Obama's presidency. Obama promoted inclusion for LGBT Americans, becoming the first sitting U.S. president to publicly support same-sex marriage. +

Obama left office on January 20, 2017, and continues to reside in Washington, D.C. His presidential library in Chicago began construction in 2021. Since leaving office, Obama has remained politically active, campaigning for candidates in various American elections, including Biden's successful bid for president in 2020. Outside of politics, Obama has published three books: Dreams from My Father (1995), The Audacity of Hope (2006), and A Promised Land (2020). Historians and political scientists rank him among the upper tier in historical rankings of American presidents. +

+ +

Early life and career

+ +
Photo of a young Obama sitting on grass with his grandfather, mother, and half-sister.
Obama (right) with grandfather Stanley Armour Dunham, mother Ann Dunham, and half-sister Maya Soetoro, mid-1970s in Honolulu
+

Obama was born on August 4, 1961,[1] at Kapiolani Medical Center for Women and Children in Honolulu, Hawaii.[2][3][4][5] He is the only president born outside the contiguous 48 states.[6] He was born to an American mother and a Kenyan father. His mother, Ann Dunham (1942–1995), was born in Wichita, Kansas and was of English, Welsh, German, Swiss, and Irish descent. In 2007 it was discovered her great-great-grandfather Falmouth Kearney emigrated from the village of Moneygall, Ireland to the US in 1850.[7] In July 2012, Ancestry.com found a strong likelihood that Dunham was descended from John Punch, an enslaved African man who lived in the Colony of Virginia during the seventeenth century.[8][9] Obama's father, Barack Obama Sr. (1934–1982),[10][11] was a married[12][13][14] Luo Kenyan from Nyang'oma Kogelo.[12][15] His last name, Obama, was derived from his Luo descent.[16] Obama's parents met in 1960 in a Russian language class at the University of Hawaiʻi at Mānoa, where his father was a foreign student on a scholarship.[17][18] The couple married in Wailuku, Hawaii, on February 2, 1961, six months before Obama was born.[19][20] +

In late August 1961, a few weeks after he was born, Barack and his mother moved to the University of Washington in Seattle, where they lived for a year. During that time, Barack's father completed his undergraduate degree in economics in Hawaii, graduating in June 1962. He left to attend graduate school on a scholarship at Harvard University, where he earned an M.A. in economics. Obama's parents divorced in March 1964.[21] Obama Sr. returned to Kenya in 1964, where he married for a third time and worked for the Kenyan government as the Senior Economic Analyst in the Ministry of Finance.[22] He visited his son in Hawaii only once, at Christmas 1971,[23] before he was killed in an automobile accident in 1982, when Obama was 21 years old.[24] Recalling his early childhood, Obama said: "That my father looked nothing like the people around me—that he was black as pitch, my mother white as milk—barely registered in my mind."[18] He described his struggles as a young adult to reconcile social perceptions of his multiracial heritage.[25] +

In 1963, Dunham met Lolo Soetoro at the University of Hawaii; he was an Indonesian East–West Center graduate student in geography. The couple married on Molokai on March 15, 1965.[26] After two one-year extensions of his J-1 visa, Lolo returned to Indonesia in 1966. His wife and stepson followed sixteen months later in 1967. The family initially lived in the Menteng Dalam neighborhood in the Tebet district of South Jakarta. From 1970, they lived in a wealthier neighborhood in the Menteng district of Central Jakarta.[27] +

+

Education

+
Scan of Obama's elementary school record, where he is wrongly recorded as Indonesian and Muslim.
Obama's Indonesian school record in St. Francis of Assisi Catholic Elementary School. Obama was enrolled as "Barry Soetoro" (no. 1), and was wrongly recorded as an Indonesian citizen (no. 3) and a Muslim (no. 4).[28]
+

At the age of six, Obama and his mother had moved to Indonesia to join his stepfather. From age six to ten, he was registered in school as "Barry"[28] and attended local Indonesian-language schools: Sekolah Dasar Katolik Santo Fransiskus Asisi (St. Francis of Assisi Catholic Elementary School) for two years and Sekolah Dasar Negeri Menteng 01 (State Elementary School Menteng 01) for one and a half years, supplemented by English-language Calvert School homeschooling by his mother.[29][30] As a result of his four years in Jakarta, he was able to speak Indonesian fluently as a child.[31] During his time in Indonesia, Obama's stepfather taught him to be resilient and gave him "a pretty hardheaded assessment of how the world works".[32] +

In 1971, Obama returned to Honolulu to live with his maternal grandparents, Madelyn and Stanley Dunham. He attended Punahou School—a private college preparatory school—with the aid of a scholarship from fifth grade until he graduated from high school in 1979.[33] In high school, Obama continued to use the nickname "Barry" which he kept until making a visit to Kenya in 1980.[34] Obama lived with his mother and half-sister, Maya Soetoro, in Hawaii for three years from 1972 to 1975 while his mother was a graduate student in anthropology at the University of Hawaii.[35] Obama chose to stay in Hawaii when his mother and half-sister returned to Indonesia in 1975, so his mother could begin anthropology field work.[36] His mother spent most of the next two decades in Indonesia, divorcing Lolo Soetoro in 1980 and earning a PhD degree in 1992, before dying in 1995 in Hawaii following unsuccessful treatment for ovarian and uterine cancer.[37] +

Of his years in Honolulu, Obama wrote: "The opportunity that Hawaii offered — to experience a variety of cultures in a climate of mutual respect — became an integral part of my world view, and a basis for the values that I hold most dear."[38] Obama has also written and talked about using alcohol, marijuana, and cocaine during his teenage years to "push questions of who I was out of my mind".[39] Obama was also a member of the "Choom Gang" (the slang term for smoking marijuana), a self-named group of friends who spent time together and smoked marijuana.[40][41] +

College and research jobs +

After graduating from high school in 1979, Obama moved to Los Angeles to attend Occidental College on a full scholarship. In February 1981, Obama made his first public speech, calling for Occidental to participate in the disinvestment from South Africa in response to that nation's policy of apartheid.[42] In mid-1981, Obama traveled to Indonesia to visit his mother and half-sister Maya, and visited the families of college friends in Pakistan for three weeks.[42] Later in 1981, he transferred to Columbia University in New York City as a junior, where he majored in political science with a specialty in international relations[43] and in English literature[44] and lived off-campus on West 109th Street.[45] He graduated with a Bachelor of Arts degree in 1983 and a 3.7 GPA. After graduating, Obama worked for about a year at the Business International Corporation, where he was a financial researcher and writer,[46][47] then as a project coordinator for the New York Public Interest Research Group on the City College of New York campus for three months in 1985.[48][49][50] +

Community organizer and Harvard Law School +

Two years after graduating from Columbia, Obama moved from New York to Chicago when he was hired as director of the Developing Communities Project, a faith-based community organization originally comprising eight Catholic parishes in Roseland, West Pullman, and Riverdale on Chicago's South Side. He worked there as a community organizer from June 1985 to May 1988.[49][51] He helped set up a job training program, a college preparatory tutoring program, and a tenants' rights organization in Altgeld Gardens.[52] Obama also worked as a consultant and instructor for the Gamaliel Foundation, a community organizing institute.[53] In mid-1988, he traveled for the first time in Europe for three weeks and then for five weeks in Kenya, where he met many of his paternal relatives for the first time.[54][55] +

+
External videos
video icon Derrick Bell threatens to leave Harvard, April 24, 1990, 11:34, Boston TV Digital Archive[56] Student Barack Obama introduces Professor Derrick Bell starting at 6:25.
+

Despite being offered a full scholarship to Northwestern University School of Law, Obama enrolled at Harvard Law School in the fall of 1988, living in nearby Somerville, Massachusetts.[57] He was selected as an editor of the Harvard Law Review at the end of his first year,[58] president of the journal in his second year,[52][59] and research assistant to the constitutional scholar Laurence Tribe while at Harvard.[60] During his summers, he returned to Chicago, where he worked as a summer associate at the law firms of Sidley Austin in 1989 and Hopkins & Sutter in 1990.[61] Obama's election as the first black president of the Harvard Law Review gained national media attention[52][59] and led to a publishing contract and advance for a book about race relations,[62] which evolved into a personal memoir. The manuscript was published in mid-1995 as Dreams from My Father.[62] Obama graduated from Harvard Law in 1991 with a Juris Doctor magna cum laude.[63][58] +

University of Chicago Law School +

In 1991, Obama accepted a two-year position as Visiting Law and Government Fellow at the University of Chicago Law School to work on his first book.[62][64] He then taught constitutional law at the University of Chicago Law School for twelve years, first as a lecturer from 1992 to 1996, and then as a senior lecturer from 1996 to 2004.[65] +

From April to October 1992, Obama directed Illinois's Project Vote, a voter registration campaign with ten staffers and seven hundred volunteer registrars; it achieved its goal of registering 150,000 of 400,000 unregistered African Americans in the state, leading Crain's Chicago Business to name Obama to its 1993 list of "40 under Forty" powers to be.[66] +

+

Family and personal life

+ +

In a 2006 interview, Obama highlighted the diversity of his extended family: "It's like a little mini-United Nations," he said. "I've got relatives who look like Bernie Mac, and I've got relatives who look like Margaret Thatcher."[67] Obama has a half-sister with whom he was raised (Maya Soetoro-Ng) and seven other half-siblings from his Kenyan father's family, six of them living.[68] Obama's mother was survived by her Kansas-born mother, Madelyn Dunham,[69] until her death on November 2, 2008,[70] two days before his election to the presidency. Obama also has roots in Ireland; he met with his Irish cousins in Moneygall in May 2011.[71] In Dreams from My Father, Obama ties his mother's family history to possible Native American ancestors and distant relatives of Jefferson Davis, President of the Confederate States of America during the American Civil War. He also shares distant ancestors in common with George W. Bush and Dick Cheney, among others.[72][73][74] +

Obama lived with anthropologist Sheila Miyoshi Jager while he was a community organizer in Chicago in the 1980s.[75] He proposed to her twice, but both Jager and her parents turned him down.[75][76] The relationship was not made public until May 2017, several months after his presidency had ended.[76] +

+
Picture of Obama, his wife, and their two daughters smiling at the camera. Obama wears a dress shirt and tie.
Obama poses in the Green Room of the White House with wife Michelle and daughters Sasha and Malia, 2009.
+

In June 1989, Obama met Michelle Robinson when he was employed at Sidley Austin.[77] Robinson was assigned for three months as Obama's adviser at the firm, and she joined him at several group social functions but declined his initial requests to date.[78] They began dating later that summer, became engaged in 1991, and were married on October 3, 1992.[79] After suffering a miscarriage, Michelle underwent in vitro fertilization to conceive their children.[80] The couple's first daughter, Malia Ann, was born in 1998,[81] followed by a second daughter, Natasha ("Sasha"), in 2001.[82] The Obama daughters attended the University of Chicago Laboratory Schools. When they moved to Washington, D.C., in January 2009, the girls started at the Sidwell Friends School.[83] The Obamas had two Portuguese Water Dogs; the first, a male named Bo, was a gift from Senator Ted Kennedy.[84] In 2013, Bo was joined by Sunny, a female.[85] Bo died of cancer on May 8, 2021.[86] +

Obama is a supporter of the Chicago White Sox, and he threw out the first pitch at the 2005 ALCS when he was still a senator.[87] In 2009, he threw out the ceremonial first pitch at the All-Star Game while wearing a White Sox jacket.[88] He is also primarily a Chicago Bears football fan in the NFL, but in his childhood and adolescence was a fan of the Pittsburgh Steelers, and rooted for them ahead of their victory in Super Bowl XLIII 12 days after he took office as president.[89] In 2011, Obama invited the 1985 Chicago Bears to the White House; the team had not visited the White House after their Super Bowl win in 1986 due to the Space Shuttle Challenger disaster.[90] He plays basketball, a sport he participated in as a member of his high school's varsity team,[91] and he is left-handed.[92] +

In 2005, the Obama family applied the proceeds of a book deal and moved from a Hyde Park, Chicago condominium to a $1.6 million house (equivalent to $2.5 million in 2023) in neighboring Kenwood, Chicago.[93] The purchase of an adjacent lot—and sale of part of it to Obama by the wife of developer, campaign donor and friend Tony Rezko—attracted media attention because of Rezko's subsequent indictment and conviction on political corruption charges that were unrelated to Obama.[94] +

In December 2007, Money Magazine estimated Obama's net worth at $1.3 million (equivalent to $1.9 million in 2023).[95] Their 2009 tax return showed a household income of $5.5 million—up from about $4.2 million in 2007 and $1.6 million in 2005—mostly from sales of his books.[96][97] On his 2010 income of $1.7 million, he gave 14 percent to non-profit organizations, including $131,000 to Fisher House Foundation, a charity assisting wounded veterans' families, allowing them to reside near where the veteran is receiving medical treatments.[98][99] Per his 2012 financial disclosure, Obama may be worth as much as $10 million.[100] +

+

Religious views

+

Obama is a Protestant Christian whose religious views developed in his adult life.[101] He wrote in The Audacity of Hope that he "was not raised in a religious household." He described his mother, raised by non-religious parents, as being detached from religion, yet "in many ways the most spiritually awakened person ... I have ever known", and "a lonely witness for secular humanism." He described his father as a "confirmed atheist" by the time his parents met, and his stepfather as "a man who saw religion as not particularly useful." Obama explained how, through working with black churches as a community organizer while in his twenties, he came to understand "the power of the African-American religious tradition to spur social change."[102] +

+
Obama and his wife standing in a crowded Church, looking forward, with their mouths open mid-sentence while reciting a prayer.
The Obamas worship at African Methodist Episcopal Church in Washington, D.C., January 2013
+

In January 2008, Obama told Christianity Today: "I am a Christian, and I am a devout Christian. I believe in the redemptive death and resurrection of Jesus Christ. I believe that faith gives me a path to be cleansed of sin and have eternal life."[103] On September 27, 2010, Obama released a statement commenting on his religious views, saying: +

+

I'm a Christian by choice. My family didn't—frankly, they weren't folks who went to church every week. And my mother was one of the most spiritual people I knew, but she didn't raise me in the church. So I came to my Christian faith later in life, and it was because the precepts of Jesus Christ spoke to me in terms of the kind of life that I would want to lead—being my brothers' and sisters' keeper, treating others as they would treat me.[104][105]

+

Obama met Trinity United Church of Christ pastor Jeremiah Wright in October 1987 and became a member of Trinity in 1992.[106] During Obama's first presidential campaign in May 2008, he resigned from Trinity after some of Wright's statements were criticized.[107] Since moving to Washington, D.C., in 2009, the Obama family has attended several Protestant churches, including Shiloh Baptist Church and St. John's Episcopal Church, as well as Evergreen Chapel at Camp David, but the members of the family do not attend church on a regular basis.[108][109][110] +

In 2016, he said that he gets inspiration from a few items that remind him "of all the different people I've met along the way", adding: "I carry these around all the time. I'm not that superstitious, so it's not like I think I necessarily have to have them on me at all times." The items, "a whole bowl full", include rosary beads given to him by Pope Francis, a figurine of the Hindu deity Hanuman, a Coptic cross from Ethiopia, a small Buddha statue given by a monk, and a metal poker chip that used to be the lucky charm of a motorcyclist in Iowa.[111][112] +

+

Legal career

+

Civil rights attorney

+

He joined Davis, Miner, Barnhill & Galland, a 13-attorney law firm specializing in civil rights litigation and neighborhood economic development, where he was an associate for three years from 1993 to 1996, then of counsel from 1996 to 2004. In 1994, he was listed as one of the lawyers in Buycks-Roberson v. Citibank Fed. Sav. Bank, 94 C 4094 (N.D. Ill.). This class action lawsuit was filed in 1994 with Selma Buycks-Roberson as lead plaintiff and alleged that Citibank Federal Savings Bank had engaged in practices forbidden under the Equal Credit Opportunity Act and the Fair Housing Act. The case was settled out of court. +

From 1994 to 2002, Obama served on the boards of directors of the Woods Fund of Chicago—which in 1985 had been the first foundation to fund the Developing Communities Project—and of the Joyce Foundation.[49] He served on the board of directors of the Chicago Annenberg Challenge from 1995 to 2002, as founding president and chairman of the board of directors from 1995 to 1999.[49] Obama's law license became inactive in 2007.[113][114] +

+

Legislative career

+

Illinois Senate (1997–2004)

+ +
Photo of Obama and others carrying a streetsign that reads "Honorary: Milton Davis Blvd."
State Senator Obama and others celebrate the naming of a street in Chicago after ShoreBank co-founder Milton Davis in 1998.
+

Obama was elected to the Illinois Senate in 1996, succeeding Democratic State Senator Alice Palmer from Illinois's 13th District, which, at that time, spanned Chicago South Side neighborhoods from Hyde Park–Kenwood south to South Shore and west to Chicago Lawn.[115] Once elected, Obama gained bipartisan support for legislation that reformed ethics and health care laws.[116][117] He sponsored a law that increased tax credits for low-income workers, negotiated welfare reform, and promoted increased subsidies for childcare.[118] In 2001, as co-chairman of the bipartisan Joint Committee on Administrative Rules, Obama supported Republican Governor George Ryan's payday loan regulations and predatory mortgage lending regulations aimed at averting home foreclosures.[119][120] +

He was reelected to the Illinois Senate in 1998, defeating Republican Yesse Yehudah in the general election, and was re-elected again in 2002.[121][122] In 2000, he lost a Democratic primary race for Illinois's 1st congressional district in the United States House of Representatives to four-term incumbent Bobby Rush by a margin of two to one.[123] +

In January 2003, Obama became chairman of the Illinois Senate's Health and Human Services Committee when Democrats, after a decade in the minority, regained a majority.[124] He sponsored and led unanimous, bipartisan passage of legislation to monitor racial profiling by requiring police to record the race of drivers they detained, and legislation making Illinois the first state to mandate videotaping of homicide interrogations.[118][125][126][127] During his 2004 general election campaign for the U.S. Senate, police representatives credited Obama for his active engagement with police organizations in enacting death penalty reforms.[128] Obama resigned from the Illinois Senate in November 2004 following his election to the U.S. Senate.[129] +

+

2004 U.S. Senate campaign

+ +

In May 2002, Obama commissioned a poll to assess his prospects in a 2004 U.S. Senate race. He created a campaign committee, began raising funds, and lined up political media consultant David Axelrod by August 2002. Obama formally announced his candidacy in January 2003.[130] +

Obama was an early opponent of the George W. Bush administration's 2003 invasion of Iraq.[131] On October 2, 2002, the day President Bush and Congress agreed on the joint resolution authorizing the Iraq War,[132] Obama addressed the first high-profile Chicago anti-Iraq War rally,[133] and spoke out against the war.[134] He addressed another anti-war rally in March 2003 and told the crowd "it's not too late" to stop the war.[135] +

Decisions by Republican incumbent Peter Fitzgerald and his Democratic predecessor Carol Moseley Braun not to participate in the election resulted in wide-open Democratic and Republican primary contests involving 15 candidates.[136] In the March 2004 primary election, Obama won in an unexpected landslide—which overnight made him a rising star within the national Democratic Party, started speculation about a presidential future, and led to the reissue of his memoir, Dreams from My Father.[137] In July 2004, Obama delivered the keynote address at the 2004 Democratic National Convention,[138] seen by nine million viewers. His speech was well received and elevated his status within the Democratic Party.[139] +

Obama's expected opponent in the general election, Republican primary winner Jack Ryan, withdrew from the race in June 2004.[140] Six weeks later, Alan Keyes accepted the Republican nomination to replace Ryan.[141] In the November 2004 general election, Obama won with 70 percent of the vote, the largest margin of victory for a Senate candidate in Illinois history.[142] He took 92 of the state's 102 counties, including several where Democrats traditionally do not do well. +

+

U.S. Senate (2005–2008)

+ +
Photo of Obama smiling with his arms crossed, with the Capitol building and the sky in the background
Official portrait of Obama as a member of the United States Senate
+

Obama was sworn in as a senator on January 3, 2005,[143] becoming the only Senate member of the Congressional Black Caucus.[144] He introduced two initiatives that bore his name: Lugar–Obama, which expanded the Nunn–Lugar Cooperative Threat Reduction concept to conventional weapons;[145] and the Federal Funding Accountability and Transparency Act of 2006, which authorized the establishment of USAspending.gov, a web search engine on federal spending.[146] On June 3, 2008, Senator Obama—along with Senators Tom Carper, Tom Coburn, and John McCain—introduced follow-up legislation: Strengthening Transparency and Accountability in Federal Spending Act of 2008.[147] He also cosponsored the Secure America and Orderly Immigration Act.[148] +

In December 2006, President Bush signed into law the Democratic Republic of the Congo Relief, Security, and Democracy Promotion Act, marking the first federal legislation to be enacted with Obama as its primary sponsor.[149][150] In January 2007, Obama and Senator Feingold introduced a corporate jet provision to the Honest Leadership and Open Government Act, which was signed into law in September 2007.[151][152] +

Later in 2007, Obama sponsored an amendment to the Defense Authorization Act to add safeguards for personality-disorder military discharges.[153] This amendment passed the full Senate in the spring of 2008.[154] He sponsored the Iran Sanctions Enabling Act supporting divestment of state pension funds from Iran's oil and gas industry, which was never enacted but later incorporated in the Comprehensive Iran Sanctions, Accountability, and Divestment Act of 2010;[155] and co-sponsored legislation to reduce risks of nuclear terrorism.[156] Obama also sponsored a Senate amendment to the State Children's Health Insurance Program, providing one year of job protection for family members caring for soldiers with combat-related injuries.[157] +

Obama held assignments on the Senate Committees for Foreign Relations, Environment and Public Works and Veterans' Affairs through December 2006.[158] In January 2007, he left the Environment and Public Works committee and took additional assignments with Health, Education, Labor and Pensions and Homeland Security and Governmental Affairs.[159] He also became Chairman of the Senate's subcommittee on European Affairs.[160] As a member of the Senate Foreign Relations Committee, Obama made official trips to Eastern Europe, the Middle East, Central Asia and Africa. He met with Mahmoud Abbas before Abbas became President of the Palestinian National Authority, and gave a speech at the University of Nairobi in which he condemned corruption within the Kenyan government.[161] +

Obama resigned his Senate seat on November 16, 2008, to focus on his transition period for the presidency.[162] +

+

Presidential campaigns

+

2008

+ +
Electoral college map, depicting Obama winning many states in the Northeast, Midwest, and Pacific West, and Florida, and McCain winning many states in the South and Rocky Mountains.
2008 electoral vote results. Obama won 365–173.
+
Official portrait, 2009
+

On February 10, 2007, Obama announced his candidacy for President of the United States in front of the Old State Capitol building in Springfield, Illinois.[163][164] The choice of the announcement site was viewed as symbolic, as it was also where Abraham Lincoln delivered his "House Divided" speech in 1858.[163][165] Obama emphasized issues of rapidly ending the Iraq War, increasing energy independence, and reforming the health care system.[166] +

Numerous candidates entered the Democratic Party presidential primaries. The field narrowed to Obama and Senator Hillary Clinton after early contests, with the race remaining close throughout the primary process, but with Obama gaining a steady lead in pledged delegates due to better long-range planning, superior fundraising, dominant organizing in caucus states, and better exploitation of delegate allocation rules.[167] +On June 2, 2008, Obama had received enough votes to clinch his nomination. After an initial hesitation to concede, on June 7, Clinton ended her campaign and endorsed Obama.[168] On August 23, 2008, Obama announced his selection of Delaware Senator Joe Biden as his vice presidential running mate.[169] Obama selected Biden from a field speculated to include former Indiana Governor and Senator Evan Bayh and Virginia Governor Tim Kaine.[169] At the Democratic National Convention in Denver, Colorado, Hillary Clinton called for her supporters to endorse Obama, and she and Bill Clinton gave convention speeches in his support.[170][171] Obama delivered his acceptance speech at Invesco Field at Mile High stadium to a crowd of about eighty-four thousand; the speech was viewed by over three million people worldwide.[172][173][174] During both the primary process and the general election, Obama's campaign set numerous fundraising records, particularly in the quantity of small donations.[175] On June 19, 2008, Obama became the first major-party presidential candidate to turn down public financing in the general election since the system was created in 1976.[176] +

John McCain was nominated as the Republican candidate, and he selected Sarah Palin as his running mate. Obama and McCain engaged in three presidential debates in September and October 2008.[177] On November 4, Obama won the presidency with 365 electoral votes to 173 received by McCain.[178] Obama won 52.9 percent of the popular vote to McCain's 45.7 percent.[179] He became the first African-American to be elected president.[180] Obama delivered his victory speech before hundreds of thousands of supporters in Chicago's Grant Park.[181][182] He is one of the three United States senators moved directly from the U.S. Senate to the White House, the others being Warren G. Harding and John F. Kennedy.[183] +

+

2012

+ +
Electoral college map, depicting Obama winning many states in the Northeast, Midwest, and Pacific West, and Florida, and Romney winning many states in the South and Rocky Mountains.
2012 electoral vote results. Obama won 332–206.
+

On April 4, 2011, Obama filed election papers with the Federal Election Commission and then announced his reelection campaign for 2012 in a video titled "It Begins with Us" that he posted on his website.[184][185][186] As the incumbent president, he ran virtually unopposed in the Democratic Party presidential primaries,[187] and on April 3, 2012, Obama secured the 2778 convention delegates needed to win the Democratic nomination.[188] At the Democratic National Convention in Charlotte, North Carolina, Obama and Joe Biden were formally nominated by former President Bill Clinton as the Democratic Party candidates for president and vice president in the general election. Their main opponents were Republicans Mitt Romney, the former governor of Massachusetts, and Representative Paul Ryan of Wisconsin.[189] +

On November 6, 2012, Obama won 332 electoral votes, exceeding the 270 required for him to be reelected as president.[190][191][192] With 51.1 percent of the popular vote,[193] Obama became the first Democratic president since Franklin D. Roosevelt to win the majority of the popular vote twice.[194][195] Obama addressed supporters and volunteers at Chicago's McCormick Place after his reelection and said: "Tonight you voted for action, not politics as usual. You elected us to focus on your jobs, not ours. And in the coming weeks and months, I am looking forward to reaching out and working with leaders of both parties."[196][197] +

+

Presidency (2009–2017)

+ + +

First 100 days

+ +
Photo of Obama raising his left hand for the oath of office
Obama takes the oath of office administered by Chief Justice John G. Roberts Jr. at the Capitol, January 20, 2009.
+

The inauguration of Barack Obama as the 44th president took place on January 20, 2009. In his first few days in office, Obama issued executive orders and presidential memoranda directing the U.S. military to develop plans to withdraw troops from Iraq.[198] He ordered the closing of the Guantanamo Bay detention camp,[199] but Congress prevented the closure by refusing to appropriate the required funds[200][201] and preventing moving any Guantanamo detainee.[202] Obama reduced the secrecy given to presidential records.[203] He also revoked President George W. Bush's restoration of President Ronald Reagan's Mexico City policy which prohibited federal aid to international family planning organizations that perform or provide counseling about abortion.[204] +

+

Domestic policy

+ +

The first bill signed into law by Obama was the Lilly Ledbetter Fair Pay Act of 2009, relaxing the statute of limitations for equal-pay lawsuits.[205] Five days later, he signed the reauthorization of the State Children's Health Insurance Program to cover an additional four million uninsured children.[206] In March 2009, Obama reversed a Bush-era policy that had limited funding of embryonic stem cell research and pledged to develop "strict guidelines" on the research.[207] +

+
Photo of Obama giving a speech to Congress, with Pelosi and Biden clapping behind him
Obama delivers a speech at a joint session of Congress with Vice President Joe Biden and House Speaker Nancy Pelosi on February 24, 2009.
+

Obama appointed two women to serve on the Supreme Court in the first two years of his presidency. He nominated Sonia Sotomayor on May 26, 2009, to replace retiring Associate Justice David Souter. She was confirmed on August 6, 2009,[208] becoming the first Supreme Court Justice of Hispanic descent.[209] Obama nominated Elena Kagan on May 10, 2010, to replace retiring Associate Justice John Paul Stevens. She was confirmed on August 5, 2010, bringing the number of women sitting simultaneously on the Court to three for the first time in American history.[210] +

On March 11, 2009, Obama created the White House Council on Women and Girls, which formed part of the Office of Intergovernmental Affairs, having been established by Executive Order 13506 with a broad mandate to advise him on issues relating to the welfare of American women and girls. The council was chaired by Senior Advisor to the President Valerie Jarrett. Obama also established the White House Task Force to Protect Students from Sexual Assault through a government memorandum on January 22, 2014, with a broad mandate to advise him on issues relating to sexual assault on college and university campuses throughout the United States. The co-chairs of the Task Force were Vice President Joe Biden and Jarrett. The Task Force was a development out of the White House Council on Women and Girls and Office of the Vice President of the United States, and prior to that the 1994 Violence Against Women Act first drafted by Biden. +

In July 2009, Obama launched the Priority Enforcement Program, an immigration enforcement program that had been pioneered by George W. Bush, and the Secure Communities fingerprinting and immigration status data-sharing program.[211] +

In a major space policy speech in April 2010, Obama announced a planned change in direction at NASA, the U.S. space agency. He ended plans for a return of human spaceflight to the moon and development of the Ares I rocket, Ares V rocket and Constellation program, in favor of funding earth science projects, a new rocket type, research and development for an eventual crewed mission to Mars, and ongoing missions to the International Space Station.[212] +

+
Photo of Obama smiling at a hospital patient while hugging her friend
Obama visits an Aurora shooting victim at University of Colorado Hospital, 2012.
+

On January 16, 2013, one month after the Sandy Hook Elementary School shooting, Obama signed 23 executive orders and outlined a series of sweeping proposals regarding gun control.[213] He urged Congress to reintroduce an expired ban on military-style assault weapons, such as those used in several recent mass shootings, impose limits on ammunition magazines to 10 rounds, introduce background checks on all gun sales, pass a ban on possession and sale of armor-piercing bullets, introduce harsher penalties for gun-traffickers, especially unlicensed dealers who buy arms for criminals and approving the appointment of the head of the federal Bureau of Alcohol, Tobacco, Firearms and Explosives for the first time since 2006.[214] On January 5, 2016, Obama announced new executive actions extending background check requirements to more gun sellers.[215] In a 2016 editorial in The New York Times, Obama compared the struggle for what he termed "common-sense gun reform" to women's suffrage and other civil rights movements in American history. +

In 2011, Obama signed a four-year renewal of the Patriot Act.[216] Following the 2013 global surveillance disclosures by whistleblower Edward Snowden, Obama condemned the leak as unpatriotic,[217] but called for increased restrictions on the National Security Agency (NSA) to address violations of privacy.[218][219] Obama continued and expanded surveillance programs set up by George W. Bush, while implementing some reforms.[220] He supported legislation that would have limited the NSA's ability to collect phone records in bulk under a single program and supported bringing more transparency to the Foreign Intelligence Surveillance Court (FISC).[220] +

+

Racial issues

+ +

In his speeches as president, Obama did not make more overt references to race relations than his predecessors,[221][222] but according to one study, he implemented stronger policy action on behalf of African-Americans than any president since the Nixon era.[223] +

Following Obama's election, many pondered the existence of a "postracial America".[224][225] However, lingering racial tensions quickly became apparent,[224][226] and many African-Americans expressed outrage over what they saw as an intense racial animosity directed at Obama.[227] The acquittal of George Zimmerman following the killing of Trayvon Martin sparked national outrage, leading to Obama giving a speech in which he noted that "Trayvon Martin could have been me 35 years ago."[228] The shooting of Michael Brown in Ferguson, Missouri sparked a wave of protests.[229] These and other events led to the birth of the Black Lives Matter movement, which campaigns against violence and systemic racism toward black people.[229] Though Obama entered office reluctant to talk about race, by 2014 he began openly discussing the disadvantages faced by many members of minority groups.[230] +

Several incidents during Obama's presidency generated disapproval from the African-American community and with law enforcement, and Obama sought to build trust between law enforcement officials and civil rights activists, with mixed results. Some in law enforcement criticized Obama's condemnation of racial bias after incidents in which police action led to the death of African-American men, while some racial justice activists criticized Obama's expressions of empathy for the police.[231] In a March 2016 Gallup poll, nearly one third of Americans said they worried "a great deal" about race relations, a higher figure than in any previous Gallup poll since 2001.[232] +

+

LGBT rights

+

On October 8, 2009, Obama signed the Matthew Shepard and James Byrd Jr. Hate Crimes Prevention Act, a measure that expanded the 1969 United States federal hate-crime law to include crimes motivated by a victim's actual or perceived gender, sexual orientation, gender identity, or disability.[233] On October 30, 2009, Obama lifted the ban on travel to the United States by those infected with HIV. The lifting of the ban was celebrated by Immigration Equality.[234] On December 22, 2010, Obama signed the Don't Ask, Don't Tell Repeal Act of 2010, which fulfilled a promise made in the 2008 presidential campaign[235][236] to end the don't ask, don't tell policy of 1993 that had prevented gay and lesbian people from serving openly in the United States Armed Forces. In 2016, the Pentagon ended the policy that barred transgender people from serving openly in the military.[237] +

+
Same-sex marriage
+

As a candidate for the Illinois state senate in 1996, Obama stated he favored legalizing same-sex marriage.[238] During his Senate run in 2004, he said he supported civil unions and domestic partnerships for same-sex partners but opposed same-sex marriages.[239] In 2008, he reaffirmed this position by stating "I believe marriage is between a man and a woman. I am not in favor of gay marriage."[240] On May 9, 2012, shortly after the official launch of his campaign for re-election as president, Obama said his views had evolved, and he publicly affirmed his personal support for the legalization of same-sex marriage, becoming the first sitting U.S. president to do so.[241][242] During his second inaugural address on January 21, 2013,[197] Obama became the first U.S. president in office to call for full equality for gay Americans, and the first to mention gay rights or the word "gay" in an inaugural address.[243][244] In 2013, the Obama administration filed briefs that urged the Supreme Court to rule in favor of same-sex couples in the cases of Hollingsworth v. Perry (regarding same-sex marriage)[245] and United States v. Windsor (regarding the Defense of Marriage Act).[246] +

+

Economic policy

+ +

On February 17, 2009, Obama signed the American Recovery and Reinvestment Act of 2009, a $787 billion (equivalent to $1118 billion in 2023) economic stimulus package aimed at helping the economy recover from the deepening worldwide recession.[247] The act includes increased federal spending for health care, infrastructure, education, various tax breaks and incentives, and direct assistance to individuals.[248] In March 2009, Obama's Treasury Secretary, Timothy Geithner, took further steps to manage the financial crisis, including introducing the Public–Private Investment Program for Legacy Assets, which contains provisions for buying up to $2 trillion in depreciated real estate assets.[249] +

+
Graph showing large deficit increases in 2008 and 2009, followed by a decline
Deficit and debt increases, 2001–2016
+

Obama intervened in the troubled automotive industry[250] in March 2009, renewing loans for General Motors (GM) and Chrysler to continue operations while reorganizing. Over the following months the White House set terms for both firms' bankruptcies, including the sale of Chrysler to Italian automaker Fiat[251] and a reorganization of GM giving the U.S. government a temporary 60 percent equity stake in the company.[252] In June 2009, dissatisfied with the pace of economic stimulus, Obama called on his cabinet to accelerate the investment.[253] He signed into law the Car Allowance Rebate System, known colloquially as "Cash for Clunkers", which temporarily boosted the economy.[254][255][256] +

The Bush and Obama administrations authorized spending and loan guarantees from the Federal Reserve and the Department of the Treasury. These guarantees totaled about $11.5 trillion, but only $3 trillion had been spent by the end of November 2009.[257] On August 2, 2011, after a lengthy congressional debate over whether to raise the nation's debt limit, Obama signed the bipartisan Budget Control Act of 2011. The legislation enforced limits on discretionary spending until 2021, established a procedure to increase the debt limit, created a Congressional Joint Select Committee on Deficit Reduction to propose further deficit reduction with a stated goal of achieving at least $1.5 trillion in budgetary savings over 10 years, and established automatic procedures for reducing spending by as much as $1.2 trillion if legislation originating with the new joint select committee did not achieve such savings.[258] By passing the legislation, Congress was able to prevent a U.S. government default on its obligations.[259] +

The unemployment rate rose in 2009, reaching a peak in October at 10.0 percent and averaging 10.0 percent in the fourth quarter. Following a decrease to 9.7 percent in the first quarter of 2010, the unemployment rate fell to 9.6 percent in the second quarter, where it remained for the rest of the year.[260] Between February and December 2010, employment rose by 0.8 percent, which was less than the average of 1.9 percent experienced during comparable periods in the past four employment recoveries.[261] By November 2012, the unemployment rate fell to 7.7 percent,[262] decreasing to 6.7 percent in the last month of 2013.[263] During 2014, the unemployment rate continued to decline, falling to 6.3 percent in the first quarter.[264] GDP growth returned in the third quarter of 2009, expanding at a rate of 1.6 percent, followed by a 5.0 percent increase in the fourth quarter.[265] Growth continued in 2010, posting an increase of 3.7 percent in the first quarter, with lesser gains throughout the rest of the year.[265] In July 2010, the Federal Reserve noted that economic activity continued to increase, but its pace had slowed, and chairman Ben Bernanke said the economic outlook was "unusually uncertain".[266] Overall, the economy expanded at a rate of 2.9 percent in 2010.[267] +

+
Graph showing increased unemployment in Obama's first year, followed by consistent jobs growth
U.S. unemployment rate and monthly changes in net employment during Obama's tenure as president[268][269]
Graph showing lower jobs growth under Obama was lower than previous presidents, except George W. Bush
Job growth during the presidency of Obama compared to other presidents, as measured as a cumulative percentage change from month after inauguration to end of his term
+

The Congressional Budget Office (CBO) and a broad range of economists credit Obama's stimulus plan for economic growth.[270][271] The CBO released a report stating that the stimulus bill increased employment by 1–2.1 million,[271][272][273] while conceding that "it is impossible to determine how many of the reported jobs would have existed in the absence of the stimulus package."[270] Although an April 2010, survey of members of the National Association for Business Economics showed an increase in job creation (over a similar January survey) for the first time in two years, 73 percent of 68 respondents believed the stimulus bill has had no impact on employment.[274] The economy of the United States has grown faster than the other original NATO members by a wider margin under President Obama than it has anytime since the end of World War II.[275] The Organisation for Economic Co-operation and Development credits the much faster growth in the United States to the stimulus plan of the U.S. and the austerity measures in the European Union.[276] +

Within a month of the 2010 midterm elections, Obama announced a compromise deal with the Congressional Republican leadership that included a temporary, two-year extension of the 2001 and 2003 income tax rates, a one-year payroll tax reduction, continuation of unemployment benefits, and a new rate and exemption amount for estate taxes.[277] The compromise overcame opposition from some in both parties, and the resulting $858 billion (equivalent to $1.2 trillion in 2023) Tax Relief, Unemployment Insurance Reauthorization, and Job Creation Act of 2010 passed with bipartisan majorities in both houses of Congress before Obama signed it on December 17, 2010.[278] +

In December 2013, Obama declared that growing income inequality is a "defining challenge of our time" and called on Congress to bolster the safety net and raise wages. This came on the heels of the nationwide strikes of fast-food workers and Pope Francis' criticism of inequality and trickle-down economics.[279] Obama urged Congress to ratify a 12-nation free trade pact called the Trans-Pacific Partnership.[280] +

+

Environmental policy

+ +
Photo of Obama listening to a briefing, surrounded by senior staffers
Obama at a 2010 briefing on the BP oil spill at the Coast Guard Station Venice in Venice, Louisiana
+

On April 20, 2010, an explosion destroyed an offshore drilling rig at the Macondo Prospect in the Gulf of Mexico, causing a major sustained oil leak. Obama visited the Gulf, announced a federal investigation, and formed a bipartisan commission to recommend new safety standards, after a review by Secretary of the Interior Ken Salazar and concurrent Congressional hearings. He then announced a six-month moratorium on new deepwater drilling permits and leases, pending regulatory review.[281] As multiple efforts by BP failed, some in the media and public expressed confusion and criticism over various aspects of the incident, and stated a desire for more involvement by Obama and the federal government.[282] Prior to the oil spill, on March 31, 2010, Obama ended a ban on oil and gas drilling along the majority of the East Coast of the United States and along the coast of northern Alaska in an effort to win support for an energy and climate bill and to reduce foreign imports of oil and gas.[283] +

In July 2013, Obama expressed reservations and said he "would reject the Keystone XL pipeline if it increased carbon pollution [or] greenhouse emissions."[284][285] On February 24, 2015, Obama vetoed a bill that would have authorized the pipeline.[286] It was the third veto of Obama's presidency and his first major veto.[287] +

In December 2016, Obama permanently banned new offshore oil and gas drilling in most United States-owned waters in the Atlantic and Arctic Oceans using the 1953 Outer Continental Shelf Act.[288][289][290] +

Obama emphasized the conservation of federal lands during his term in office. He used his power under the Antiquities Act to create 25 new national monuments during his presidency and expand four others, protecting a total of 553,000,000 acres (224,000,000 ha) of federal lands and waters, more than any other U.S. president.[291][292][293] +

+

Health care reform

+ +

Obama called for Congress to pass legislation reforming health care in the United States, a key campaign promise and a top legislative goal.[294] He proposed an expansion of health insurance coverage to cover the uninsured, cap premium increases, and allow people to retain their coverage when they leave or change jobs. His proposal was to spend $900 billion over ten years and include a government insurance plan, also known as the public option, to compete with the corporate insurance sector as a main component to lowering costs and improving quality of health care. It would also make it illegal for insurers to drop sick people or deny them coverage for pre-existing conditions, and require every American to carry health coverage. The plan also includes medical spending cuts and taxes on insurance companies that offer expensive plans.[295][296] +

+
Graph of maximum out-of-pocket premiums by poverty level, showing single-digit premiums for everyone under 400% of the federal poverty level.
Maximum Out-of-Pocket Premium as Percentage of Family Income and federal poverty level, under Patient Protection and Affordable Care Act, starting in 2014 (Source: CRS)[297]
+

On July 14, 2009, House Democratic leaders introduced a 1,017-page plan for overhauling the U.S. health care system, which Obama wanted Congress to approve by the end of 2009.[294] After public debate during the Congressional summer recess of 2009, Obama delivered a speech to a joint session of Congress on September 9 where he addressed concerns over the proposals.[298] In March 2009, Obama lifted a ban on using federal funds for stem cell research.[299] +

On November 7, 2009, a health care bill featuring the public option was passed in the House.[300][301] On December 24, 2009, the Senate passed its own bill—without a public option—on a party-line vote of 60–39.[302] On March 21, 2010, the Patient Protection and Affordable Care Act (ACA, colloquially "Obamacare") passed by the Senate in December was passed in the House by a vote of 219 to 212. Obama signed the bill into law on March 23, 2010.[303] +

The ACA includes health-related provisions, most of which took effect in 2014, including expanding Medicaid eligibility for people making up to 133 percent of the federal poverty level (FPL) starting in 2014,[304] subsidizing insurance premiums for people making up to 400 percent of the FPL ($88,000 for family of four in 2010) so their maximum "out-of-pocket" payment for annual premiums will be from 2 percent to 9.5 percent of income,[305] providing incentives for businesses to provide health care benefits, prohibiting denial of coverage and denial of claims based on pre-existing conditions, establishing health insurance exchanges, prohibiting annual coverage caps, and support for medical research. According to White House and CBO figures, the maximum share of income that enrollees would have to pay would vary depending on their income relative to the federal poverty level.[306] +

+
Graph showing significant decreases in uninsured rates after the creation of Medicare and Medicaid, and after the creation of Obamacare
Percentage of Individuals in the United States without Health Insurance, 1963–2015 (Source: JAMA)[307]
+

The costs of these provisions are offset by taxes, fees, and cost-saving measures, such as new Medicare taxes for those in high-income brackets, taxes on indoor tanning, cuts to the Medicare Advantage program in favor of traditional Medicare, and fees on medical devices and pharmaceutical companies;[308] there is also a tax penalty for those who do not obtain health insurance, unless they are exempt due to low income or other reasons.[309] In March 2010, the CBO estimated that the net effect of both laws will be a reduction in the federal deficit by $143 billion over the first decade.[310] +

The law faced several legal challenges, primarily based on the argument that an individual mandate requiring Americans to buy health insurance was unconstitutional. On June 28, 2012, the Supreme Court ruled by a 5–4 vote in National Federation of Independent Business v. Sebelius that the mandate was constitutional under the U.S. Congress's taxing authority.[311] In Burwell v. Hobby Lobby the Court ruled that "closely-held" for-profit corporations could be exempt on religious grounds under the Religious Freedom Restoration Act from regulations adopted under the ACA that would have required them to pay for insurance that covered certain contraceptives. In June 2015, the Court ruled 6–3 in King v. Burwell that subsidies to help individuals and families purchase health insurance were authorized for those doing so on both the federal exchange and state exchanges, not only those purchasing plans "established by the State", as the statute reads.[312] +

+

Foreign policy

+ +
refer to caption
June 4, 2009 − after his speech A New Beginning at Cairo University, U.S. President Obama participates in a roundtable interview in 2009 with among others Jamal Khashoggi, Bambang Harymurti and Nahum Barnea.
+

In February and March 2009, Vice President Joe Biden and Secretary of State Hillary Clinton made separate overseas trips to announce a "new era" in U.S. foreign relations with Russia and Europe, using the terms "break" and "reset" to signal major changes from the policies of the preceding administration.[313] Obama attempted to reach out to Arab leaders by granting his first interview to an Arab satellite TV network, Al Arabiya.[314] On March 19, Obama continued his outreach to the Muslim world, releasing a New Year's video message to the people and government of Iran.[315][316] On June 4, 2009, Obama delivered a speech at Cairo University in Egypt calling for "A New Beginning" in relations between the Islamic world and the United States and promoting Middle East peace.[317] On June 26, 2009, Obama condemned the Iranian government's actions towards protesters following Iran's 2009 presidential election.[318] +

In 2011, Obama ordered a drone strike in Yemen which targeted and killed Anwar al-Awlaki, an American imam suspected of being a leading Al-Qaeda organizer. al-Awlaki became the first U.S. citizen to be targeted and killed by a U.S. drone strike. The Department of Justice released a memo justifying al-Awlaki's death as a lawful act of war,[319] while civil liberties advocates described it as a violation of al-Awlaki's constitutional right to due process. The killing led to significant controversy.[320] His teenage son and young daughter, also Americans, were later killed in separate US military actions, although they were not targeted specifically.[321][322] +

+
Obama, King Salman of Saudi Arabia, Saudi Crown Prince Mohammed bin Salman and other leaders at the GCC summit in Saudi Arabia, April 2016
+

In March 2015, Obama declared that he had authorized U.S. forces to provide logistical and intelligence support to the Saudis in their military intervention in Yemen, establishing a "Joint Planning Cell" with Saudi Arabia.[323][324] In 2016, the Obama administration proposed a series of arms deals with Saudi Arabia worth $115 billion.[325] Obama halted the sale of guided munition technology to Saudi Arabia after Saudi warplanes targeted a funeral in Yemen's capital Sanaa, killing more than 140 people.[326] +

In September 2016 Obama was snubbed by Xi Jinping and the Chinese Communist Party as he descended from Air Force One to the tarmac of Hangzhou International Airport for the 2016 G20 Hangzhou summit without the usual red carpet welcome.[327] +

+

War in Iraq

+ +

On February 27, 2009, Obama announced that combat operations in Iraq would end within 18 months.[328] The Obama administration scheduled the withdrawal of combat troops to be completed by August 2010, decreasing troop's levels from 142,000 while leaving a transitional force of about 50,000 in Iraq until the end of 2011. On August 19, 2010, the last U.S. combat brigade exited Iraq. Remaining troops transitioned from combat operations to counter-terrorism and the training, equipping, and advising of Iraqi security forces.[329][330] On August 31, 2010, Obama announced that the United States combat mission in Iraq was over.[331] On October 21, 2011, President Obama announced that all U.S. troops would leave Iraq in time to be "home for the holidays."[332] +

In June 2014, following the capture of Mosul by ISIL, Obama sent 275 troops to provide support and security for U.S. personnel and the U.S. Embassy in Baghdad. ISIS continued to gain ground and to commit widespread massacres and ethnic cleansing.[333][334] In August 2014, during the Sinjar massacre, Obama ordered a campaign of U.S. airstrikes against ISIL.[335] By the end of 2014, 3,100 American ground troops were committed to the conflict[336] and 16,000 sorties were flown over the battlefield, primarily by U.S. Air Force and Navy pilots.[337] In early 2015, with the addition of the "Panther Brigade" of the 82nd Airborne Division the number of U.S. ground troops in Iraq increased to 4,400,[338] and by July American-led coalition air forces counted 44,000 sorties over the battlefield.[339] +

+

Afghanistan and Pakistan

+ +
Photo of Obama and other heads of state walking along the Colonnade outside the White House
Obama after a trilateral meeting with Afghan President Hamid Karzai (left) and Pakistani President Asif Ali Zardari (right), May 2009
+

In his election campaign, Obama called the war in Iraq a "dangerous distraction" and that emphasis should instead be put on the war in Afghanistan,[340] the region he cites as being most likely where an attack against the United States could be launched again.[341] Early in his presidency, Obama moved to bolster U.S. troop strength in Afghanistan. He announced an increase in U.S. troop levels to 17,000 military personnel in February 2009 to "stabilize a deteriorating situation in Afghanistan", an area he said had not received the "strategic attention, direction and resources it urgently requires."[342] He replaced the military commander in Afghanistan, General David D. McKiernan, with former Special Forces commander Lt. Gen. Stanley A. McChrystal in May 2009, indicating that McChrystal's Special Forces experience would facilitate the use of counterinsurgency tactics in the war.[343] On December 1, 2009, Obama announced the deployment of an additional 30,000 military personnel to Afghanistan and proposed to begin troop withdrawals 18 months from that date;[344] this took place in July 2011. David Petraeus replaced McChrystal in June 2010, after McChrystal's staff criticized White House personnel in a magazine article.[345] In February 2013, Obama said the U.S. military would reduce the troop level in Afghanistan from 68,000 to 34,000 U.S. troops by February 2014.[346] In October 2015, the White House announced a plan to keep U.S. Forces in Afghanistan indefinitely in light of the deteriorating security situation.[347] +

Regarding neighboring Pakistan, Obama called its tribal border region the "greatest threat" to the security of Afghanistan and Americans, saying that he "cannot tolerate a terrorist sanctuary." In the same speech, Obama claimed that the U.S. "cannot succeed in Afghanistan or secure our homeland unless we change our Pakistan policy."[348] +

+
Death of Osama bin Laden
+ +
Photo of Obama, Biden, and national security staffers in the Situation Room, somberly listening to updates on the bin Laden raid
Obama and members of the national security team receive an update on Operation Neptune's Spear in the White House Situation Room, May 1, 2011. See also: Situation Room.
+

Starting with information received from Central Intelligence Agency operatives in July 2010, the CIA developed intelligence over the next several months that determined what they believed to be the hideout of Osama bin Laden. He was living in seclusion in a large compound in Abbottabad, Pakistan, a suburban area 35 miles (56 km) from Islamabad.[349] CIA head Leon Panetta reported this intelligence to President Obama in March 2011.[349] Meeting with his national security advisers over the course of the next six weeks, Obama rejected a plan to bomb the compound, and authorized a "surgical raid" to be conducted by United States Navy SEALs.[349] The operation took place on May 1, 2011, and resulted in the shooting death of bin Laden and the seizure of papers, computer drives and disks from the compound.[350][351] DNA testing was one of five methods used to positively identify bin Laden's corpse,[352] which was buried at sea several hours later.[353] Within minutes of the President's announcement from Washington, DC, late in the evening on May 1, there were spontaneous celebrations around the country as crowds gathered outside the White House, and at New York City's Ground Zero and Times Square.[350][354] Reaction to the announcement was positive across party lines, including from former presidents Bill Clinton and George W. Bush.[355] +

+
+

Relations with Cuba

+ +
Photo of Obama shaking hands with the Cuban president
President Obama meeting with Cuban President Raúl Castro in Panama, April 2015
+

Since the spring of 2013, secret meetings were conducted between the United States and Cuba in the neutral locations of Canada and Vatican City.[356] The Vatican first became involved in 2013 when Pope Francis advised the U.S. and Cuba to exchange prisoners as a gesture of goodwill.[357] On December 10, 2013, Cuban President Raúl Castro, in a significant public moment, greeted and shook hands with Obama at the Nelson Mandela memorial service in Johannesburg.[358] +

In December 2014, after the secret meetings, it was announced that Obama, with Pope Francis as an intermediary, had negotiated a restoration of relations with Cuba, after nearly sixty years of détente.[359] Popularly dubbed the Cuban Thaw, The New Republic deemed the Cuban Thaw to be "Obama's finest foreign policy achievement."[360] On July 1, 2015, President Obama announced that formal diplomatic relations between Cuba and the United States would resume, and embassies would be opened in Washington and Havana.[361] The countries' respective "interests sections" in one another's capitals were upgraded to embassies on July 20 and August 13, 2015, respectively.[362] Obama visited Havana, Cuba for two days in March 2016, becoming the first sitting U.S. president to arrive since Calvin Coolidge in 1928.[363] +

+

Israel

+
Photo of Obama shaking hands with Israeli President Shimon Peres, with Biden overlooking
Obama meeting with Israeli President Shimon Peres in the Oval Office, May 2009
+

During the initial years of the Obama administration, the U.S. increased military cooperation with Israel, including increased military aid, re-establishment of the U.S.-Israeli Joint Political Military Group and the Defense Policy Advisory Group, and an increase in visits among high-level military officials of both countries.[364] The Obama administration asked Congress to allocate money toward funding the Iron Dome program in response to the waves of Palestinian rocket attacks on Israel.[365] In March 2010, Obama took a public stance against plans by the government of Israeli Prime Minister Benjamin Netanyahu to continue building Jewish housing projects in predominantly Arab neighborhoods of East Jerusalem.[366][367] In 2011, the United States vetoed a Security Council resolution condemning Israeli settlements, with the United States being the only nation to do so.[368] Obama supports the two-state solution to the Arab–Israeli conflict based on the 1967 borders with land swaps.[369] +

In 2013, Jeffrey Goldberg reported that, in Obama's view, "with each new settlement announcement, Netanyahu is moving his country down a path toward near-total isolation."[370] In 2014, Obama likened the Zionist movement to the civil rights movement in the United States. He said both movements seek to bring justice and equal rights to historically persecuted peoples, explaining: "To me, being pro-Israel and pro-Jewish is part and parcel with the values that I've been fighting for since I was politically conscious and started getting involved in politics."[371] Obama expressed support for Israel's right to defend itself during the 2014 Israel–Gaza conflict.[372] In 2015, Obama was harshly criticized by Israel for advocating and signing the Iran Nuclear Deal; Israeli Prime Minister Benjamin Netanyahu, who had advocated the U.S. congress to oppose it, said the deal was "dangerous" and "bad."[373] +

On December 23, 2016, under the Obama Administration, the United States abstained from United Nations Security Council Resolution 2334, which condemned Israeli settlement building in the occupied Palestinian territories as a violation of international law, effectively allowing it to pass.[374] Netanyahu strongly criticized the Obama administration's actions,[375][376] and the Israeli government withdrew its annual dues from the organization, which totaled $6 million, on January 6, 2017.[377] On January 5, 2017, the United States House of Representatives voted 342–80 to condemn the UN Resolution.[378][379] +

+

Libya

+ +

In February 2011, protests in Libya began against long-time dictator Muammar Gaddafi as part of the Arab Spring. They soon turned violent. In March, as forces loyal to Gaddafi advanced on rebels across Libya, calls for a no-fly zone came from around the world, including Europe, the Arab League, and a resolution[380] passed unanimously by the U.S. Senate.[381] In response to the passage of United Nations Security Council Resolution 1973 on March 17, the Foreign Minister of Libya Moussa Koussa announced a ceasefire. However Gaddafi's forces continued to attack the rebels.[382] +

On March 19 a multinational coalition lead by France and the United Kingdom with Italian and U.S. support, approved by Obama, took part in air strikes to destroy the Libyan government's air defense capabilities to protect civilians and enforce a no-fly-zone,[383] including the use of Tomahawk missiles, B-2 Spirits, and fighter jets.[384][385][386] Six days later, on March 25, by unanimous vote of all its 28 members, NATO took over leadership of the effort, dubbed Operation Unified Protector.[387] Some members of Congress[388] questioned whether Obama had the constitutional authority to order military action in addition to questioning its cost, structure and aftermath.[389][390] In 2016 Obama said “Our coalition could have and should have done more to fill a vacuum left behind” and that it was "a mess".[391] He has stated that the lack of preparation surrounding the days following the government's overthrow was the "worst mistake" of his presidency.[392] +

+

Syrian civil war

+ +

On August 18, 2011, several months after the start of the Syrian civil war, Obama issued a written statement that said: "The time has come for President Assad to step aside."[393] This stance was reaffirmed in November 2015.[394] In 2012, Obama authorized multiple programs run by the CIA and the Pentagon to train anti-Assad rebels.[395] The Pentagon-run program was later found to have failed and was formally abandoned in October 2015.[396][397] +

In the wake of a chemical weapons attack in Syria, formally blamed by the Obama administration on the Assad government, Obama chose not to enforce the "red line" he had pledged[398] and, rather than authorize the promised military action against Assad, went along with the Russia-brokered deal that led to Assad giving up chemical weapons; however attacks with chlorine gas continued.[399][400] In 2014, Obama authorized an air campaign aimed primarily at ISIL.[401] +

+

Iran nuclear talks

+ +
refer to caption
Obama talks with Israeli prime minister Benjamin Netanyahu in Jerusalem, March 2013.
+

On October 1, 2009, the Obama administration went ahead with a Bush administration program, increasing nuclear weapons production. The "Complex Modernization" initiative expanded two existing nuclear sites to produce new bomb parts. In November 2013, the Obama administration opened negotiations with Iran to prevent it from acquiring nuclear weapons, which included an interim agreement. Negotiations took two years with numerous delays, with a deal being announced on July 14, 2015. The deal titled the "Joint Comprehensive Plan of Action" saw sanctions removed in exchange for measures that would prevent Iran from producing nuclear weapons. While Obama hailed the agreement as being a step towards a more hopeful world, the deal drew strong criticism from Republican and conservative quarters, and from Israeli Prime Minister Benjamin Netanyahu.[402][403][404] In addition, the transfer of $1.7 billion in cash to Iran shortly after the deal was announced was criticized by the Republican party. The Obama administration said that the payment in cash was because of the "effectiveness of U.S. and international sanctions."[405] In order to advance the deal, the Obama administration shielded Hezbollah from the Drug Enforcement Administration's Project Cassandra investigation regarding drug smuggling and from the Central Intelligence Agency.[406][407] +On a side note, the very same year, in December 2015, Obama started a $348 billion worth program to back the biggest U.S. buildup of nuclear arms since Ronald Reagan left the White House.[408] +

+

Russia

+ +
Photo of Obama shaking hands with Vladimir Putin in front of Russian and American flags
Obama meets Russian President Vladimir Putin in September 2015.
+

In March 2010, an agreement was reached with the administration of Russian President Dmitry Medvedev to replace the 1991 Strategic Arms Reduction Treaty with a new pact reducing the number of long-range nuclear weapons in the arsenals of both countries by about a third.[409] Obama and Medvedev signed the New START treaty in April 2010, and the U.S. Senate ratified it in December 2010.[410] In December 2011, Obama instructed agencies to consider LGBT rights when issuing financial aid to foreign countries.[411] In August 2013, he criticized Russia's law that discriminates against gays,[412] but he stopped short of advocating a boycott of the upcoming 2014 Winter Olympics in Sochi, Russia.[413] +

After Russia's invasion of Crimea in 2014, military intervention in Syria in 2015, and the interference in the 2016 U.S. presidential election,[414] George Robertson, a former UK defense secretary and NATO secretary-general, said Obama had "allowed Putin to jump back on the world stage and test the resolve of the West", adding that the legacy of this disaster would last.[415] +

+

Cultural and political image

+ + +

Obama's family history, upbringing, and Ivy League education differ markedly from those of African-American politicians who launched their careers in the 1960s through participation in the civil rights movement.[416] Expressing puzzlement over questions about whether he is "black enough", Obama told an August 2007 meeting of the National Association of Black Journalists that "we're still locked in this notion that if you appeal to white folks then there must be something wrong."[417] Obama acknowledged his youthful image in an October 2007 campaign speech, saying: "I wouldn't be here if, time and again, the torch had not been passed to a new generation."[418] Additionally, Obama has frequently been referred to as an exceptional orator.[419] During his pre-inauguration transition period and continuing into his presidency, Obama delivered a series of weekly Internet video addresses.[420] +

+

Job approval

+
refer to adjacent text
Graph of Obama's approval ratings per Gallup
+

According to the Gallup Organization, Obama began his presidency with a 68 percent approval rating,[421] the fifth highest for a president following their swearing in.[422] His ratings remained above the majority level until November 2009[423] and by August 2010 his approval was in the low 40s,[424] a trend similar to Ronald Reagan's and Bill Clinton's first years in office.[425] Following the death of Osama bin Laden on May 2, 2011, Obama experienced a small poll bounce and steadily maintained 50–53 percent approval for about a month, until his approval numbers dropped back to the low 40s.[426][427][428] +

His approval rating fell to 38 percent on several occasions in late 2011[429] before recovering in mid-2012 with polls showing an average approval of 50 percent.[430] After his second inauguration in 2013, Obama's approval ratings remained stable around 52 percent[431] before declining for the rest of the year and eventually bottoming out at 39 percent in December.[426] In polling conducted before the 2014 midterm elections, Obama's approval ratings were at their lowest[432][433] with his disapproval rating reaching a high of 57 percent.[426][434][435] His approval rating continued to lag throughout most of 2015 but began to reach the high 40s by the end of the year.[426][436] According to Gallup, Obama's approval rating reached 50 percent in March 2016, a level unseen since May 2013.[426][437] In polling conducted January 16–19, 2017, Obama's final approval rating was 59 percent, which placed him on par with George H. W. Bush and Dwight D. Eisenhower, whose final Gallup ratings also measured in the high 50s.[438] +

Obama has maintained relatively positive public perceptions after his presidency.[439] In Gallup's retrospective approval polls of former presidents, Obama garnered a 63 percent approval rating in 2018 and again in 2023, ranking him the fourth most popular president since World War II.[440][441] +

+

Foreign perceptions

+

Polls showed strong support for Obama in other countries both before and during his presidency.[442][443][444] In a February 2009 poll conducted in Western Europe and the U.S. by Harris Interactive for France 24 and the International Herald Tribune, Obama was rated as the most respected world leader, as well as the most powerful.[445] In a similar poll conducted by Harris in May 2009, Obama was rated as the most popular world leader, as well as the one figure most people would pin their hopes on for pulling the world out of the economic downturn.[446][447] +

On October 9, 2009—only nine months into his first term—the Norwegian Nobel Committee announced that Obama had won the 2009 Nobel Peace Prize "for his extraordinary efforts to strengthen international diplomacy and cooperation between peoples",[448] which drew a mixture of praise and criticism from world leaders and media figures.[449][450][451][452] He became the fourth U.S. president to be awarded the Nobel Peace Prize, and the third to become a Nobel laureate while in office.[453] He himself called it a "call to action" and remarked: "I do not view it as a recognition of my own accomplishments but rather an affirmation of American leadership on behalf of aspirations held by people in all nations".[454] +

+

Thanks, Obama

+ +

In 2009 the saying "thanks, Obama" first appeared in a Twitter hashtag "#thanks Obama" and was later used in a demotivational poster. It was later adopted satirically to blame Obama for any socio-economic ills. Obama himself used the phrase in video in 2015 and 2016. In 2017 the phrase was used by Stephen Colbert to express gratitude to Obama on his last day in office. +

+

Post-presidency (2017–present)

+
refer to caption
Obama playing golf with Argentinian president Mauricio Macri, October 2017
+

Obama's presidency ended on January 20, 2017, upon the inauguration of his successor, Donald Trump.[455][456] The family moved to a house they rented in Kalorama, Washington, D.C.[457] On March 2, the John F. Kennedy Presidential Library and Museum awarded the Profile in Courage Award to Obama "for his enduring commitment to democratic ideals and elevating the standard of political courage."[458] His first public appearance since leaving the office was a seminar at the University of Chicago on April 24, where he appealed for a new generation to participate in politics.[459] On September 7, Obama partnered with former presidents Jimmy Carter, George H. W. Bush, Bill Clinton, and George W. Bush to work with One America Appeal to help the victims of Hurricane Harvey and Hurricane Irma in the Gulf Coast and Texas communities.[460] From October 31 to November 1, Obama hosted the inaugural summit of the Obama Foundation,[461] which he intended to be the central focus of his post-presidency and part of his ambitions for his subsequent activities following his presidency to be more consequential than his time in office.[462] +

Barack and Michelle Obama signed a deal on May 22, 2018, to produce docu-series, documentaries and features for Netflix under the Obamas' newly formed production company, Higher Ground Productions.[463][464] Higher Ground's first film, American Factory, won the Academy Award for Best Documentary Feature in 2020.[465] On October 24, a pipe bomb addressed to Obama was intercepted by the Secret Service. It was one of several pipe-bombs that had been mailed out to Democratic lawmakers and officials.[466] In 2019, Barack and Michelle Obama bought a home on Martha's Vineyard from Wyc Grousbeck.[467] On October 29, Obama criticized "wokeness" and call-out culture at the Obama Foundation's annual summit.[468][469] +

Obama was reluctant to make an endorsement in the 2020 Democratic presidential primaries because he wanted to position himself to unify the party, regardless of the nominee.[470] On April 14, 2020, Obama endorsed Biden, the presumptive nominee, for president in the presidential election, stating that he has "all the qualities we need in a president right now."[471][472] In May, Obama criticized President Trump for his handling of the COVID-19 pandemic, calling his response to the crisis "an absolute chaotic disaster", and stating that the consequences of the Trump presidency have been "our worst impulses unleashed, our proud reputation around the world badly diminished, and our democratic institutions threatened like never before."[473] On November 17, Obama's presidential memoir, A Promised Land, was released.[474][475][476] +

In February 2021, Obama and musician Bruce Springsteen started a podcast called Renegades: Born in the USA where the two talk about "their backgrounds, music and their 'enduring love of America.'"[477][478] Later that year, Regina Hicks had signed a deal with Netflix, in a venture with his and Michelle's Higher Ground to develop comedy projects.[479] +

+
Photo of Obama standing behind a lectern, giving a speech at the White House, with Biden and Harris smiling in the background
Obama with president Joe Biden and vice president Kamala Harris in the White House, April 5, 2022
+

On March 4, 2022, Obama won an Audio Publishers Association (APA) Award in the best narration by the author category for the narration of his memoir A Promised Land.[480] On April 5, Obama visited the White House for the first time since leaving office, in an event celebrating the 12th annual anniversary of the signing of the Affordable Care Act.[481][482][483] In June, it was announced that the Obamas and their podcast production company, Higher Ground, signed a multi-year deal with Audible.[484][485] In September, Obama visited the White House to unveil his and Michelle's official White House portraits.[486] Around the same time, he won a Primetime Emmy Award for Outstanding Narrator[487] for his narration in the Netflix documentary series Our Great National Parks.[488] +

In 2022, Obama opposed expanding the Supreme Court beyond the present nine Justices.[489] +

In March 2023, Obama traveled to Australia as a part of his speaking tour of the country. During the trip, Obama met with Australian Prime Minister Anthony Albanese and visited Melbourne for the first time.[490] Obama was reportedly paid more than $1 million for two speeches.[491][492] +

In October 2023, during the Israel–Hamas war, Obama declared that Israel must dismantle Hamas in the wake of the October 7 massacre.[493] Weeks later, Obama warned Israel that its actions could "harden Palestinian attitudes for generations" and weaken international support for Israel; any military strategy that ignored the war's human costs "could ultimately backfire."[494] +

+

Legacy and recognition

+

Polls of historians and political scientists rank Obama among the upper tier of American presidents.[495] He has been described as one of the most effective campaigners in American history (his 2008 campaign being particularly highlighted) as well as one of the most talented political orators of the 21st century.[496][497][498] Historian Julian Zelizer credits Obama with "a keen sense of how the institutions of government work and the ways that his team could design policy proposals." Zeitzer notes Obama's policy successes included the economic stimulus package which ended the Great Recession and the Dodd-Frank financial and consumer protection reforms, as well as the Affordable Care Act. Zeitzer also notes the Democratic Party lost power and numbers of elected officials during Obama's term, saying that the consensus among historians is that Obama "turned out to be a very effective policymaker but not a tremendously successful party builder." Zeitzer calls this the "defining paradox of Obama's presidency".[499] +

The Brookings Institution noted that Obama passed "only one major legislative achievement (Obamacare)—and a fragile one at that—the legacy of Obama's presidency mainly rests on its tremendous symbolic importance and the fate of a patchwork of executive actions."[500] David W. Wise noted that Obama fell short "in areas many Progressives hold dear", including the continuation of drone strikes, not going after big banks during the Great Recession, and failing to strengthen his coalition before pushing for Obamacare. Wise called Obama's legacy that of "a disappointingly conventional president".[501] +

Obama's most significant accomplishment is generally considered to be the Affordable Care Act (ACA), provisions of which went into effect from 2010 to 2020. Many attempts by Senate Republicans to repeal the ACA, including a "skinny repeal", have thus far failed.[502] However, in 2017, the penalty for violating the individual mandate was repealed effective 2019.[503] Together with the Health Care and Education Reconciliation Act amendment, it represents the U.S. healthcare system's most significant regulatory overhaul and expansion of coverage since the passage of Medicare and Medicaid in 1965.[504][505][506][507] +

Many commentators credit Obama with averting a threatened depression and pulling the economy back from the Great Recession.[502] According to the U.S. Bureau of Labor Statistics, the Obama administration created 11.3 million jobs from the month after his first inauguration to the end of his second term.[508] In 2010, Obama signed into effect the Dodd–Frank Wall Street Reform and Consumer Protection Act. Passed as a response to the financial crisis of 2007–2008, it brought the most significant changes to financial regulation in the United States since the regulatory reform that followed the Great Depression under Democratic President Franklin D. Roosevelt.[509] +

In 2009, Obama signed into law the National Defense Authorization Act for Fiscal Year 2010, which contained in it the Matthew Shepard and James Byrd Jr. Hate Crimes Prevention Act, the first addition to existing federal hate crime law in the United States since Democratic President Bill Clinton signed into law the Church Arson Prevention Act of 1996. The act expanded existing federal hate crime laws in the United States, and made it a federal crime to assault people based on sexual orientation, gender identity, or disability.[510] +

As president, Obama advanced LGBT rights.[511] In 2010, he signed the Don't Ask, Don't Tell Repeal Act, which brought an end to "don't ask, don't tell" policy in the U.S. armed forces that banned open service from LGBT people; the law went into effect the following year.[512] In 2016, his administration brought an end to the ban on transgender people serving openly in the U.S. armed forces.[513][237] A Gallup poll, taken in the final days of Obama's term, showed that 68 percent of Americans believed the U.S. had made progress on LGBT rights during Obama's eight years in office.[514] +

Obama substantially escalated the use of drone strikes against suspected militants and terrorists associated with al-Qaeda and the Taliban.[515] In 2016, the last year of his presidency, the U.S. dropped 26,171 bombs on seven different countries.[516][517] Obama left about 8,400 U.S. troops in Afghanistan, 5,262 in Iraq, 503 in Syria, 133 in Pakistan, 106 in Somalia, seven in Yemen, and two in Libya at the end of his presidency.[518] +

According to Pew Research Center and United States Bureau of Justice Statistics, from December 31, 2009, to December 31, 2015, inmates sentenced in U.S. federal custody declined by five percent. This is the largest decline in sentenced inmates in U.S. federal custody since Democratic President Jimmy Carter. By contrast, the federal prison population increased significantly under presidents Ronald Reagan, George H. W. Bush, Bill Clinton, and George W. Bush.[519] +

Human Rights Watch (HRW) called Obama's human rights record "mixed", adding that "he has often treated human rights as a secondary interest—nice to support when the cost was not too high, but nothing like a top priority he championed."[220] +

Obama left office in January 2017 with a 60 percent approval rating.[520][521] He gained 10 spots from the same survey in 2015 from the Brookings Institution that ranked him the 18th-greatest American president.[522] In Gallup's 2018 job approval poll for the past 10 U.S. presidents, he received a 63 percent approval rating.[523] +

+

Presidential library

+ +

The Barack Obama Presidential Center is Obama's planned presidential library. It will be hosted by the University of Chicago and located in Jackson Park on the South Side of Chicago.[524] +

+

Awards and honors

+ +

Obama received the Norwegian Nobel Committee's Nobel Peace Prize in 2009, The Shoah Foundation Institute for Visual History and Education's Ambassador of Humanity Award in 2014, the John F. Kennedy Profile in Courage Award in 2017, and the Robert F. Kennedy Center for Justice and Human Rights Ripple of Hope Award in 2018. He was named TIME Magazine's Time Person of the Year in 2008 and 2012. He also received two Grammy Awards for Best Spoken Word Album for Dreams from My Father (2006), and The Audacity of Hope (2008) as well as two Primetime Emmy Awards for Outstanding Narrator for Our Great National Parks (2022), and Working: What We Do All Day (2023). He also won two Children's and Family Emmy Awards. +

+

Eponymy

+ +

Bibliography

+ +
+

Books

+
  • Obama, Barack (July 18, 1995). Dreams from My Father (1st ed.). New York: Times Books. ISBN 978-0-8129-2343-8.
  • +
  • ——————— (October 17, 2006). The Audacity of Hope (1st ed.). New York: Crown Publishing Group. ISBN 978-0-307-23769-9.
  • +
  • ——————— (November 16, 2010). Of Thee I Sing (1st ed.). New York: Alfred A. Knopf. ISBN 978-0-375-83527-8.
  • +
  • ——————— (November 17, 2020). A Promised Land (1st ed.). New York: Crown Publishing Group. ISBN 978-1-5247-6316-9.[525]
+

Audiobooks

+ +

Articles

+ +
+

See also

+ +

Politics

+ +

Other

+ + +

Lists

+ +

References

+
+
    +
  1. ^ "President Barack Obama". The White House. 2008. Archived from the original on October 26, 2009. Retrieved December 12, 2008. +
  2. +
  3. ^ "President Obama's Long Form Birth Certificate". whitehouse.gov. April 27, 2011. Archived from the original on July 31, 2023. Retrieved August 4, 2023. +
  4. +
  5. ^ "Certificate of Live Birth: Barack Hussein Obama II, August 4, 1961, 7:24 pm, Honolulu" (PDF). whitehouse.gov. April 27, 2011. Archived from the original (PDF) on March 3, 2017. Retrieved March 11, 2017 – via National Archives. +
  6. +
  7. ^ Maraniss, David (August 24, 2008). "Though Obama had to leave to find himself, it is Hawaii that made his rise possible". The Washington Post. p. A22. Archived from the original on March 28, 2019. Retrieved October 28, 2008. +
  8. +
  9. ^ Nakaso, Dan (December 22, 2008). "Twin sisters, Obama on parallel paths for years". The Honolulu Advertiser. p. B1. Archived from the original on January 29, 2011. Retrieved January 22, 2011. +
  10. +
  11. ^ Barreto, Amílcar Antonio; O'Bryant, Richard L. (November 12, 2013). "Introduction". American Identity in the Age of Obama. Taylor & Francis. pp. 18–19. ISBN 978-1-317-93715-9. Retrieved May 8, 2017. +
  12. +
  13. ^ "On This Day: US President Barack Obama arrives in Ireland for a visit". IrishCentral.com. May 23, 2022. Archived from the original on May 16, 2022. Retrieved August 2, 2022. +
  14. +
  15. ^ "Ancestry.com Discovers Ph Suggests" Archived April 2, 2015, at the Wayback Machine, The New York Times. July 30, 2012. +
  16. +
  17. ^ Hennessey, Kathleen. "Obama related to legendary Virginia slave, genealogists say" Archived March 5, 2016, at the Wayback Machine, Los Angeles Times. July 30, 2012. +
  18. +
  19. ^ Maraniss (2012), p. 65 Archived March 5, 2024, at the Wayback Machine: He had been born inside the euphorbia hedges of the K'obama homestead on June 18, 1934. +
  20. +
  21. ^ Liberties (2012), p. 202 Archived March 5, 2024, at the Wayback Machine: The age of his father is questionable since June 18, 1934, is on most of the documents Obama Sr. filled out for his United States student visa; however, Obama II's book Dreams of My Father states his father's birth date was June 18, 1936. Immigration and Naturalization Service records indicate the birth date to be June 18, 1934, thereby making Obama Sr. twenty-seven at the birth of Obama II instead of the annotated twenty-five on the birth certificate. +
  22. +
  23. ^ a b Jacobs, Sally (July 6, 2011). "President Obama's Father: A 'Bold And Reckless Life'". NPR. Archived from the original on December 23, 2019. Retrieved January 16, 2020. +
  24. +
  25. ^ Swaine, Jon (April 29, 2011). "Barack Obama's father 'forced out of US in 1960s'". Telegraph. Archived from the original on January 10, 2022. Retrieved January 16, 2020. +
  26. +
  27. ^ Swarns, Rachel L. (June 18, 2016). "Words of Obama's Father Still Waiting to Be Read by His Son". The New York Times. Archived from the original on June 18, 2016. Retrieved January 16, 2020. +
  28. +
  29. ^ David R Arnott. "From Obama's old school to his ancestral village, world reacts to US presidential election". NBC News. Archived from the original on October 28, 2020. Retrieved January 16, 2020. +
  30. +
  31. ^ Bearak, Max (June 19, 2016). "The fascinating tribal tradition that gave Obama his last name". Washington Post. Archived from the original on November 7, 2022. Retrieved November 20, 2022. +
  32. +
  33. ^ Jones, Tim (March 27, 2007). "Barack Obama: Mother not just a girl from Kansas; Stanley Ann Dunham shaped a future senator". Chicago Tribune. p. 1 (Tempo). Archived from the original on February 7, 2017. +
  34. +
  35. ^ a b Obama (1995, 2004), pp. 9–10. +
    • Scott (2011), pp. 80–86.
    • +
    • Jacobs (2011), pp. 115–118.
    • +
    • Maraniss (2012), pp. 154–160.
    +
  36. +
  37. ^ Ripley, Amanda (April 9, 2008). "The story of Barack Obama's mother". Time. Archived from the original on August 28, 2013. Retrieved April 9, 2007. +
  38. +
  39. ^ Scott (2011), p. 86. +
    • Jacobs (2011), pp. 125–127.
    • +
    • Maraniss (2012), pp. 160–163.
    +
  40. +
  41. ^ Scott (2011), pp. 87–93. +
    • Jacobs (2011), pp. 115–118, 125–127, 133–161.
    • +
    • Maraniss (2012), pp. 170–183, 188–189.
    +
  42. +
  43. ^ Obama "Dreams from My Father a Story of Race and Inheritance" +
  44. +
  45. ^ Scott (2011), pp. 142–144. +
    • Jacobs (2011), pp. 161–177, 227–230.
    • +
    • Maraniss (2012), pp. 190–194, 201–209, 227–230.
    +
  46. +
  47. ^ Ochieng, Philip (November 1, 2004). "From home squared to the US Senate: how Barack Obama was lost and found". The EastAfrican. Nairobi. Archived from the original on September 27, 2007. +
    • Merida, Kevin (December 14, 2007). "The ghost of a father". The Washington Post. p. A12. Archived from the original on August 29, 2008. Retrieved June 25, 2008.
    • +
    • Jacobs (2011), pp. 251–255.
    • +
    • Maraniss (2012), pp. 411–417.
    +
  48. +
  49. ^ Serrano, Richard A. (March 11, 2007). "Obama's peers didn't see his angst". Los Angeles Times. p. A20. Archived from the original on November 8, 2008. Retrieved March 13, 2007. +
    • Obama (1995, 2004), Chapters 4 and 5.
    +
  50. +
  51. ^ Scott (2011), pp. 97–103. +
    • Maraniss (2012), pp. 195–201, 225–230.
    +
  52. +
  53. ^ Maraniss (2012), pp. 195–201, 209–223, 230–244. +
  54. +
  55. ^ a b Suhartono, Anton (March 19, 2010). "Sekolah di SD Asisi, Obama Berstatus Agama Islam". Okezone (in Indonesian). Archived from the original on January 28, 2021. Retrieved January 21, 2021. +
  56. +
  57. ^ Maraniss (2012), pp. 216, 221, 230, 234–244. +
  58. +
  59. ^ "Barack Obama: Calvert Homeschooler?—Calvert Education Blog". calverteducation.com. January 25, 2014. Archived from the original on March 13, 2017. Retrieved November 25, 2015. +
  60. +
  61. ^ Zimmer, Benjamin (2009). "Obama's Indonesian Redux". Language Log. Archived from the original on March 3, 2009. Retrieved March 12, 2009. + +
  62. +
  63. ^ Meacham, Jon (August 22, 2008). "What Barack Obama Learned from His Father". Newsweek. Archived from the original on January 7, 2017. Retrieved January 9, 2017. +
  64. +
  65. ^ Serafin, Peter (March 21, 2004). "Punahou grad stirs up Illinois politics". Honolulu Star-Bulletin. Archived from the original on March 28, 2019. Retrieved March 20, 2008. +
    • Scott, Janny (March 14, 2008). "A free-spirited wanderer who set Obama's path". The New York Times. p. A1. Archived from the original on March 14, 2008. Retrieved November 18, 2011.
    • +
    • Obama (1995, 2004), Chapters 3 and 4.
    • +
    • Scott (2012), pp. 131–134.
    • +
    • Maraniss (2012), pp. 264–269.
    +
  66. +
  67. ^ Wolffe, Richard (March 22, 2008). "When Barry Became Barack". Newsweek. Archived from the original on April 18, 2010. Retrieved March 21, 2016. +
  68. +
  69. ^ Scott (2011), pp. 139–157. +
    • Maraniss (2012), pp. 279–281.
    +
  70. +
  71. ^ Scott (2011), pp. 157–194. +
    • Maraniss (2012), pp. 279–281, 324–326.
    +
  72. +
  73. ^ Scott (2011), pp. 214, 294, 317–346. +
  74. +
  75. ^ Reyes, B.J. (February 8, 2007). "Punahou left lasting impression on Obama". Honolulu Star-Bulletin. Archived from the original on March 28, 2019. Retrieved February 10, 2007. As a teenager, Obama went to parties and sometimes sought out gatherings on military bases or at the University of Hawaii that were attended mostly by blacks. +
  76. +
  77. ^ Elliott, Philip (November 21, 2007). "Obama gets blunt with N.H. students". The Boston Globe. Associated Press. p. 8A. Archived from the original on April 7, 2012. Retrieved May 18, 2012. +
  78. +
  79. ^ Karl, Jonathan (May 25, 2012). "Obama and His Pot-Smoking 'Choom Gang'". ABC News. Archived from the original on May 25, 2012. Retrieved May 25, 2012. + +
  80. +
  81. ^ "FRONTLINE The Choice 2012". PBS. October 9, 2012. Archived from the original on October 10, 2017. Retrieved October 29, 2012. +
  82. +
  83. ^ a b Gordon, Larry (January 29, 2007). "Occidental recalls 'Barry' Obama". Los Angeles Times. p. B1. Archived from the original on May 24, 2010. Retrieved May 12, 2010. + +
  84. +
  85. ^ Boss-Bicak, Shira (January 2005). "Barack Obama '83". Columbia College Today. ISSN 0572-7820. Archived from the original on September 5, 2008. Retrieved October 1, 2006. +
  86. +
  87. ^ "Remarks by the President in Town Hall". whitehouse.gov. June 26, 2014. Archived from the original on February 16, 2017. Retrieved October 15, 2016 – via National Archives. +
  88. +
  89. ^ "The Approval Matrix". New York. August 27, 2012. Archived from the original on May 19, 2020. Retrieved February 18, 2020. +
  90. +
  91. ^ Horsley, Scott (July 9, 2008). "Obama's Early Brush With Financial Markets". NPR. Archived from the original on August 3, 2017. Retrieved July 17, 2017. +
  92. +
  93. ^ Obama, Barack (1998). "Curriculum vitae". The University of Chicago Law School. Archived from the original on May 9, 2001. Retrieved October 1, 2006. + +
  94. +
  95. ^ Scott, Janny (July 30, 2007). "Obama's account of New York often differs from what others say". The New York Times. p. B1. Archived from the original on October 31, 2007. Retrieved July 31, 2007. +
    • Obama (1995, 2004), pp. 133–140.
    • +
    • Mendell (2007), pp. 62–63.
    +
  96. +
  97. ^ a b c d Chassie, Karen, ed. (2007). Who's Who in America, 2008. New Providence, NJ: Marquis Who's Who. p. 3468. ISBN 978-0-8379-7011-0. +
  98. +
  99. ^ Fink, Jason (November 9, 2008). "Obama stood out, even during brief 1985 NYPIRG job". Newsday. Archived from the original on May 6, 2011. Retrieved March 13, 2014. +
  100. +
  101. ^ Lizza, Ryan (March 19, 2007). "The agitator: Barack Obama's unlikely political education". The New Republic. Vol. 236, no. 12. pp. 22–26, 28–29. ISSN 0028-6583. Archived from the original on November 12, 2010. Retrieved August 21, 2007. +
    • Secter, Bob; McCormick, John (March 30, 2007). "Portrait of a pragmatist". Chicago Tribune. p. 1. Archived from the original on December 14, 2009. Retrieved May 18, 2012.
    • +
    • Obama (1995, 2004), pp. 140–295.
    • +
    • Mendell (2007), pp. 63–83.
    +
  102. +
  103. ^ a b c Matchan, Linda (February 15, 1990). "A Law Review breakthrough". The Boston Globe. p. 29. Archived from the original on January 22, 2009. Retrieved June 15, 2008. + +
  104. +
  105. ^ Obama, Barack (August–September 1988). "Why organize? Problems and promise in the inner city". Illinois Issues. Vol. 14, no. 8–9. pp. 40–42. ISSN 0738-9663. reprinted in:
    Knoepfle, Peg, ed. (1990). After Alinsky: community organizing in Illinois. Springfield, IL: Sangamon State University. pp. 35–40. ISBN 978-0-9620873-3-2. He has also been a consultant and instructor for the Gamaliel Foundation, an organizing institute working throughout the Midwest.
    +
  106. +
  107. ^ Obama, Auma (2012). And then life happens: a memoir. New York: St. Martin's Press. pp. 189–208, 212–216. ISBN 978-1-250-01005-6. +
  108. +
  109. ^ Obama (1995, 2004), pp. 299–437. +
    • Maraniss (2012), pp. 564–570.
    +
  110. +
  111. ^ "Ten O'Clock News; Derrick Bell threatens to leave Harvard". WGBH, American Archive of Public Broadcasting. Boston and Washington, D.C.: WGBH and the Library of Congress. April 24, 1990. Archived from the original on November 8, 2016. Retrieved September 23, 2016. +
  112. +
  113. ^ Joey Del Ponte; Somerville Scout Staff. "Something in the Water". Somerville Scout. No. January/February 2014. p. 26. Archived from the original on January 1, 2020. Retrieved January 1, 2020. Barack Obama lived in the big, ivy-covered brick building at 365 Broadway ... From 1988 to 1991, the future president resided in a basement apartment while attending Harvard Law School. +
  114. +
  115. ^ a b Levenson, Michael; Saltzman, Jonathan (January 28, 2007). "At Harvard Law, a unifying voice". Boston Globe. p. 1A. Archived from the original on August 3, 2016. Retrieved June 15, 2008. + +
  116. +
  117. ^ a b Butterfield, Fox (February 6, 1990). "First black elected to head Harvard's Law Review". The New York Times. p. A20. Archived from the original on April 10, 2008. Retrieved June 15, 2008. + +
  118. +
  119. ^ "Obama Made A Strong First Impression At Harvard". NPR. Archived from the original on December 20, 2022. Retrieved December 20, 2022. +
  120. +
  121. ^ Aguilar, Louis (July 11, 1990). "Survey: Law firms slow to add minority partners". Chicago Tribune. p. 1 (Business). Archived from the original on September 29, 2008. Retrieved June 15, 2008. +
  122. +
  123. ^ a b c Scott, Janny (May 18, 2008). "The story of Obama, written by Obama". The New York Times. p. A1. Archived from the original on April 1, 2009. Retrieved June 15, 2008. +
    • Obama (1995, 2004), pp. xiii–xvii.
    +
  124. +
  125. ^ "Obama joins list of seven presidents with Harvard degrees". news.harvard.edu. November 6, 2008. Retrieved October 23, 2017. Adams, Richard (May 9, 2007). "Barack Obama". The Guardian. London. Archived from the original on October 13, 2008. Retrieved October 26, 2008. +
  126. +
  127. ^ Merriner, James L. (June 2008). "The friends of O". Chicago. Vol. 57, no. 6. pp. 74–79, 97–99. ISSN 0362-4595. Retrieved January 30, 2010. + +
  128. +
  129. ^ "Statement regarding Barack Obama". University of Chicago Law School. March 27, 2008. Archived from the original on June 8, 2008. Retrieved June 5, 2008. + +
  130. +
  131. ^ White, Jesse, ed. (2000). Illinois Blue Book, 2000, Millennium ed (PDF). Springfield, IL: Illinois Secretary of State. p. 83. OCLC 43923973. Archived from the original on April 16, 2004. Retrieved June 6, 2008. +
    • Jarrett, Vernon (August 11, 1992). "'Project Vote' brings power to the people" (paid archive). Chicago Sun-Times. p. 23. Retrieved June 6, 2008.
    • +
    • Reynolds, Gretchen (January 1993). "Vote of confidence". Chicago Magazine. Vol. 42, no. 1. pp. 53–54. ISSN 0362-4595. Archived from the original on May 14, 2008. Retrieved June 6, 2008.
    • +
    • Anderson, Veronica (October 3, 1993). "40 under Forty: Barack Obama, Director, Illinois Project Vote". Crain's Chicago Business. Vol. 16, no. 39. p. 43. ISSN 0149-6956.
    +
  132. +
  133. ^ "Keeping Hope Alive: Barack Obama Puts Family First". The Oprah Winfrey Show. October 18, 2006. Archived from the original on April 17, 2009. Retrieved June 24, 2008. +
  134. +
  135. ^ Fornek, Scott (September 9, 2007). "Half Siblings: 'A Complicated Family'". Chicago Sun-Times. Archived from the original on January 18, 2010. Retrieved June 24, 2008. See also: "Interactive Family Tree". Chicago Sun-Times. September 9, 2007. Archived from the original on July 3, 2008. Retrieved June 24, 2008. +
  136. +
  137. ^ Fornek, Scott (September 9, 2007). "Madelyn Payne Dunham: 'A Trailblazer'". Chicago Sun-Times. Archived from the original on March 4, 2009. Retrieved June 24, 2008. +
  138. +
  139. ^ "Obama's grandmother dies after battle with cancer". CNN. November 3, 2008. Archived from the original on November 3, 2008. Retrieved November 4, 2008. +
  140. +
  141. ^ Smolenyak, Megan (May 9, 2011). "Tracing Barack Obama's Roots to Moneygall". The Huffington Post. Archived from the original on September 15, 2018. Retrieved February 18, 2020. +
  142. +
  143. ^ Obama (1995, 2004), p. 13. For reports on Obama's maternal genealogy, including slave owners, Irish connections, and common ancestors with George W. Bush, Dick Cheney, and Harry S. Truman, see: Nitkin, David; Merritt, Harry (March 2, 2007). "A New Twist to an Intriguing Family History". The Baltimore Sun. Archived from the original on September 30, 2007. Retrieved June 24, 2008. +
  144. +
  145. ^ Jordan, Mary (May 13, 2007). "Tiny Irish Village Is Latest Place to Claim Obama as Its Own". The Washington Post. Archived from the original on April 5, 2019. Retrieved June 24, 2008. +
  146. +
  147. ^ "Obama's Family Tree Has a Few Surprises". CBS 2 (Chicago). Associated Press. September 8, 2007. Archived from the original on June 2, 2008. Retrieved June 24, 2008. +
  148. +
  149. ^ a b Hosie, Rachel (May 3, 2017). "Before Michelle: The story of Barack Obama's proposal to Sheila Miyoshi Jager". The Independent. Archived from the original on May 9, 2017. Retrieved May 11, 2017. +
  150. +
  151. ^ a b Tobias, Andrew J. (May 3, 2017). "Oberlin College professor received unsuccessful marriage proposal from Barack Obama in 1980s, new biography reveals". The Plain Dealer. Archived from the original on May 3, 2017. Retrieved May 11, 2017. +
  152. +
  153. ^ Obama (2006), pp. 327–332. See also: Brown, Sarah (December 7, 2005). "Obama '85 masters balancing act". The Daily Princetonian. Archived from the original on February 20, 2009. Retrieved February 9, 2009. +
  154. +
  155. ^ Obama (2006), p. 329. +
  156. +
  157. ^ Fornek, Scott (October 3, 2007). "Michelle Obama: 'He Swept Me Off My Feet'". Chicago Sun-Times. Archived from the original on December 8, 2009. Retrieved April 28, 2008. +
  158. +
  159. ^ Riley-Smith, Ben (November 9, 2018). "Michelle Obama had miscarriage, used IVF to conceive girls". The Telegraph. Archived from the original on January 10, 2022. Retrieved November 15, 2018. +
  160. +
  161. ^ Martin, Jonathan (July 4, 2008). "Born on the 4th of July". Politico. Archived from the original on July 10, 2008. Retrieved July 10, 2008. +
  162. +
  163. ^ Obama (1995, 2004), p. 440, and Obama (2006), pp. 339–340. See also: "Election 2008 Information Center: Barack Obama". Gannett News Service. Archived from the original on February 21, 2009. Retrieved April 28, 2008. +
  164. +
  165. ^ "Obamas choose private Sidwell Friends School". International Herald Tribune. November 22, 2008. Archived from the original on January 29, 2009. Retrieved July 2, 2015. +
  166. +
  167. ^ Cooper, Helene (April 13, 2009). "One Obama Search Ends With a Puppy Named Bo". The New York Times. Archived from the original on April 16, 2009. Retrieved December 22, 2010. +
  168. +
  169. ^ Feldmann, Linda (August 20, 2013). "New little girl arrives at White House. Meet Sunny Obama. (+video)". Christian Science Monitor. Archived from the original on December 19, 2013. Retrieved August 20, 2013. +
  170. +
  171. ^ Wang, Amy (May 8, 2021). "Obamas announce death of dog Bo, 'a true friend and loyal companion'". The Washington Post. Archived from the original on May 9, 2021. Retrieved May 8, 2021. +
  172. +
  173. ^ Silva, Mark (August 25, 2008). "Barack Obama: White Sox 'serious' ball". Chicago Tribune. Archived from the original on August 29, 2008. +
  174. +
  175. ^ "Obama throws ceremonial first pitch at All-Star game". CNN Politics. Archived from the original on December 22, 2022. Retrieved December 20, 2022. +
  176. +
  177. ^ Branigin, William (January 30, 2009). "Steelers Win Obama's Approval". The Washington Post. Archived from the original on August 5, 2017. Retrieved August 21, 2017. But other than the Bears, the Steelers are probably the team that's closest to my heart. +
  178. +
  179. ^ Mayer, Larry (October 7, 2011). "1985 Bears honored by President Obama". Chicago Bears. Archived from the original on May 7, 2013. Retrieved November 4, 2012. +
  180. +
  181. ^ Kantor, Jodi (June 1, 2007). "One Place Where Obama Goes Elbow to Elbow". The New York Times. Archived from the original on April 1, 2009. Retrieved April 28, 2008. See also: "The Love of the Game" (video). Real Sports with Bryant Gumbel. HBO. April 15, 2008. Archived from the original on October 16, 2011. Retrieved October 12, 2011. +
  182. +
  183. ^ Stolberg, Sheryl Gay; Kirkpatrick, David D.; Shane, Scott (January 22, 2009). "On First Day, Obama Quickly Sets a New Tone". The New York Times. p. 1. Archived from the original on January 23, 2009. Retrieved September 7, 2012. +
  184. +
  185. ^ Zeleny, Jeff (December 24, 2005). "The first time around: Sen. Obama's freshman year". Chicago Tribune. Archived from the original on May 13, 2011. Retrieved April 28, 2008. +
  186. +
  187. ^ Slevin, Peter (December 17, 2006). "Obama says he regrets land deal with fundraiser". The Washington Post. Retrieved June 10, 2008. + +
  188. +
  189. ^ Harris, Marlys (December 7, 2007). "Obama's Money". CNNMoney. Archived from the original on April 24, 2008. Retrieved April 28, 2008.
    See also:Goldfarb, Zachary A (March 24, 2007). "Measuring Wealth of the '08 Candidates". The Washington Post. Archived from the original on December 12, 2018. Retrieved April 28, 2008.
    +
  190. +
  191. ^ Zeleny, Jeff (April 17, 2008). "Book Sales Lifted Obamas' Income in 2007 to a Total of $4.2 Million". The New York Times. Archived from the original on April 16, 2009. Retrieved April 28, 2008. +
  192. +
  193. ^ Shear, Michael D.; Hilzenrath, David S. (April 16, 2010). "Obamas report $5.5 million in income on 2009 tax return". The Washington Post. Archived from the original on January 26, 2011. Retrieved December 22, 2010. +
  194. +
  195. ^ Solman, Paul (April 18, 2011). "How Much Did President Obama Make in 2010?". PBS NewsHour. Archived from the original on May 2, 2011. Retrieved January 27, 2012. +
  196. +
  197. ^ Solman, Paul (April 27, 2011). "The Obamas Gave $131,000 to Fisher House Foundation in 2010; What Is It?". PBS NewsHour. Archived from the original on January 29, 2014. Retrieved January 27, 2012. +
  198. +
  199. ^ Wolf, Richard (May 16, 2012). "Obama worth as much as $10 million". USA Today. Archived from the original on May 16, 2012. Retrieved June 16, 2012. +
  200. +
  201. ^ "American President: Barack Obama". Miller Center of Public Affairs, University of Virginia. 2009. Archived from the original on January 23, 2009. Retrieved January 23, 2009. Religion: Christian + +
  202. +
  203. ^ Obama (2006), pp. 202–208. Portions excerpted in: Obama, Barack (October 16, 2006). "My Spiritual Journey". Time. Archived from the original on April 30, 2008. Retrieved April 28, 2008. + +
  204. +
  205. ^ Pulliam, Sarah; Olsen, Ted (January 23, 2008). "Q&A: Barack Obama". Christianity Today. Archived from the original on April 28, 2019. Retrieved January 4, 2013. +
  206. +
  207. ^ Babington, Charles; Superville, Darlene (September 28, 2010). "Obama 'Christian By Choice': President Responds To Questioner". The Huffington Post. Associated Press. Archived from the original on May 11, 2011. +
  208. +
  209. ^ "President Obama: 'I am a Christian By Choice ... The Precepts of Jesus Spoke to Me'". ABC News. September 29, 2010. Archived from the original on July 13, 2012. Retrieved December 27, 2016. +
  210. +
  211. ^ Garrett, Major; Obama, Barack (March 14, 2008). "Obama talks to Major Garrett on 'Hannity & Colmes'". RealClearPolitics. Retrieved November 10, 2012. Major Garrett, Fox News correspondent: So the first question, how long have you been a member in good standing of that church? Sen. Barack Obama (D-IL), presidential candidate: You know, I've been a member since 1991 or '92. And—but I have known Trinity even before then when I was a community organizer on the South Side, helping steel workers find jobs ... Garrett: As a member in good standing, were you a regular attendee of Sunday services? Obama: You know, I won't say that I was a perfect attendee. I was regular in spurts, because there was times when, for example, our child had just been born, our first child. And so we didn't go as regularly then. +
    • "Obama strongly denounces former pastor". NBC News. Associated Press. April 29, 2008. Retrieved November 10, 2012. I have been a member of Trinity United Church of Christ since 1992, and have known Reverend Wright for 20 years. The person I saw yesterday was not the person [whom] I met 20 years ago.
    • +
    • Miller, Lisa (July 11, 2008). "Finding his faith". Newsweek. Archived from the original on July 20, 2013. Retrieved November 10, 2012. He is now a Christian, having been baptized in the early 1990s at Trinity United Church of Christ in Chicago.
    • +
    • Remnick, David (2010). The Bridge: The Life and Rise of Barack Obama. New York: Alfred A. Knopf. p. 177. ISBN 978-1-4000-4360-6. In late October 1987, his third year as an organizer, Obama went with Kellman to a conference on the black church and social justice at the Harvard Divinity School.
    • +
    • Maraniss (2012), p. 557: It would take time for Obama to join and become fully engaged in Wright's church, a place where he would be baptized and married; that would not happen until later, during his second time around in Chicago, but the process started then, in October 1987 ... Jerry Kellman: "He wasn't a member of the church during those first three years, but he was drawn to Jeremiah."
    • +
    • Peter, Baker (2017). Obama: The Call of History. New York: The New York Times Company/Callaway. ISBN 978-0-935112-90-0. OCLC 1002264033.
    +
  212. +
  213. ^ "Obama's church choice likely to be scrutinized". NBC News. Associated Press. November 17, 2008. Archived from the original on March 21, 2013. Retrieved January 20, 2009. +
  214. +
  215. ^ Parker, Ashley (December 28, 2013). "As the Obamas Celebrate Christmas, Rituals of Faith Become Less Visible". The New York Times. Archived from the original on December 30, 2013. Retrieved January 15, 2017. +
  216. +
  217. ^ Gilgoff, Dan (June 30, 2009). "TIME Report, White House Reaction Raise More Questions About Obama's Church Hunt". U.S. News & World Report. Archived from the original on June 25, 2017. Retrieved January 15, 2017. +
  218. +
  219. ^ "First Lady: We Use Sundays For Naps If We're Not Going To Church". CBS DC. Associated Press. April 22, 2014. Archived from the original on January 16, 2017. Retrieved January 15, 2017. +
  220. +
  221. ^ "Revealed: Obama always carries Hanuman statuette in pocket". The Hindu. January 16, 2016. Archived from the original on April 14, 2021. Retrieved April 8, 2021. +
  222. +
  223. ^ "Obama Reveals Personal Faith-Related Items, Including Rosary Beads, Buddha Statuette". NBC News. January 15, 2016. Archived from the original on December 20, 2022. Retrieved December 20, 2022. +
  224. +
  225. ^ Gore, D'Angelo (June 14, 2012). "The Obamas' Law Licenses". FactCheck.org. Archived from the original on July 18, 2012. Retrieved July 16, 2012. +
  226. +
  227. ^ Robinson, Mike (February 20, 2007). "Obama got start in civil rights practice". The Boston Globe. Associated Press. Retrieved June 15, 2008. + +
  228. +
  229. ^ Jackson, David; Long, Ray (April 3, 2007). "Obama Knows His Way Around a Ballot". Chicago Tribune. Archived from the original on October 11, 2008. Retrieved May 18, 2012. + +
  230. +
  231. ^ Slevin, Peter (February 9, 2007). "Obama Forged Political Mettle in Illinois Capitol". The Washington Post. Archived from the original on May 16, 2008. Retrieved April 20, 2008. +
  232. +
  233. ^ Helman, Scott (September 23, 2007). "In Illinois, Obama dealt with Lobbyists". The Boston Globe. Archived from the original on April 16, 2008. Retrieved April 20, 2008. See also:"Obama Record May Be Gold Mine for Critics". CBS News. Associated Press. January 17, 2007. Archived from the original on April 12, 2008. Retrieved April 20, 2008. +
  234. +
  235. ^ a b Scott, Janny (July 30, 2007). "In Illinois, Obama Proved Pragmatic and Shrewd". The New York Times. Archived from the original on December 10, 2008. Retrieved April 20, 2008. +
  236. +
  237. ^ Allison, Melissa (December 15, 2000). "State takes on predatory lending; Rules would halt single-premium life insurance financing" (paid archive). Chicago Tribune. p. 1 (Business). Archived from the original on June 17, 2008. Retrieved June 1, 2008. +
  238. +
  239. ^ Long, Ray; Allison, Melissa (April 18, 2001). "Illinois OKs predatory loan curbs; State aims to avert home foreclosures". Chicago Tribune. p. 1. Archived from the original (paid archive) on December 18, 2008. Retrieved June 1, 2008. +
  240. +
  241. ^ "13th District: Barack Obama". Illinois State Senate Democrats. August 24, 2000. Archived from the original on August 24, 2000. Retrieved April 20, 2008. +
  242. +
  243. ^ "13th District: Barack Obama". Illinois State Senate Democrats. October 9, 2004. Archived from the original on August 2, 2004. Retrieved April 20, 2008. +
  244. +
  245. ^ "Federal Elections 2000: U.S. House Results—Illinois". Federal Election Commission. Archived from the original on March 28, 2008. Retrieved April 24, 2008. + +
  246. +
  247. ^ Calmes, Jackie (February 23, 2007). "Statehouse Yields Clues to Obama". The Wall Street Journal. Archived from the original on September 18, 2008. Retrieved April 20, 2008. +
  248. +
  249. ^ Tavella, Anne Marie (April 14, 2003). "Profiling, taping plans pass Senate" (paid archive). Daily Herald. p. 17. Archived from the original on January 1, 2020. Retrieved June 1, 2008. +
  250. +
  251. ^ Haynes, V. Dion (June 29, 2003). "Fight racial profiling at local level, lawmaker says; U.S. guidelines get mixed review" (paid archive). Chicago Tribune. p. 8. Archived from the original on June 17, 2008. Retrieved June 1, 2008. +
  252. +
  253. ^ Pearson, Rick (July 17, 2003). "Taped confessions to be law; State will be 1st to pass legislation". Chicago Tribune. p. 1 (Metro). Archived from the original (paid archive) on December 18, 2008. Retrieved June 1, 2008. +
  254. +
  255. ^ Youngman, Sam; Blake, Aaron (March 14, 2007). "Obama's Crime Votes Are Fodder for Rivals". The Hill. Archived from the original on November 14, 2012. Retrieved May 18, 2012. See also: "US Presidential Candidate Obama Cites Work on State Death Penalty Reforms". International Herald Tribune. Associated Press. November 12, 2007. Archived from the original on June 7, 2008. Retrieved May 18, 2012. +
  256. +
  257. ^ Coffee, Melanie (November 6, 2004). "Attorney Chosen to Fill Obama's State Senate Seat". HPKCC. Associated Press. Archived from the original on May 16, 2008. Retrieved April 20, 2008. +
  258. +
  259. ^ Helman, Scott (October 12, 2007). "Early defeat launched a rapid political climb". The Boston Globe. p. 1A. Archived from the original on October 12, 2007. Retrieved April 13, 2008. +
  260. +
  261. ^ Strausberg, Chinta (September 26, 2002). "Opposition to war mounts". Chicago Defender. p. 1. Archived from the original (paid archive) on May 11, 2011. Retrieved February 3, 2008. +
  262. +
  263. ^ Office of the Press Secretary (October 2, 2002). "President, House leadership agree on Iraq resolution". whitehouse.gov. Retrieved February 18, 2008 – via National Archives. + +
  264. +
  265. ^ Glauber, Bill (October 3, 2003). "War protesters gentler, but passion still burns". Chicago Tribune. p. 1. Archived from the original on June 17, 2008. Retrieved February 3, 2008.(subscription required) +
    • Strausberg, Chinta (October 3, 2002). "War with Iraq undermines U.N". Chicago Defender. p. 1. Archived from the original on October 14, 2009. Retrieved October 28, 2008. Photo caption: Left Photo: Sen. Barack Obama along with Rev. Jesse Jackson spoke to nearly 3,000 anti-war protestors (below) during a rally at Federal Plaza Wednesday.
    • +
    • Katz, Marilyn (October 2, 2007). "Five years since our first action". Chicagoans Against War & Injustice. Archived from the original on July 21, 2011. Retrieved February 18, 2008.
    • +
    • Bryant, Greg; Vaughn, Jane B. (October 3, 2002). "300 attend rally against Iraq war". Daily Herald. p. 8. Retrieved October 28, 2008.(subscription required)
    • +
    • Mendell (2007), pp. 172–177.
    +
  266. +
  267. ^ Obama, Barack (October 2, 2002). "Remarks of Illinois State Sen. Barack Obama against going to war with Iraq". Barack Obama. Archived from the original on January 30, 2008. Retrieved February 3, 2008. + +
  268. +
  269. ^ Ritter, Jim (March 17, 2003). "Anti-war rally here draws thousands". Chicago Sun-Times. p. 3. Retrieved February 3, 2008. (subscription required) + +
  270. +
  271. ^ Davey, Monica (March 7, 2004). "Closely watched Illinois Senate race attracts 7 candidates in millionaire range". The New York Times. p. 19. Archived from the original on April 16, 2009. Retrieved April 13, 2008. +
  272. +
  273. ^ Mendell, David (March 17, 2004). "Obama routs Democratic foes; Ryan tops crowded GOP field; Hynes, Hull fall far short across state". Chicago Tribune. p. 1. Retrieved March 1, 2009. + +
  274. +
  275. ^ Bernstein, David (June 2007). "The Speech". Chicago Magazine. Archived from the original on June 14, 2008. Retrieved April 13, 2008. +
  276. +
  277. ^ "Star Power. Showtime: Some are on the rise; others have long been fixtures in the firmament. A galaxy of bright Democratic lights". Newsweek. August 2, 2004. pp. 48–51. Archived from the original on December 18, 2008. Retrieved November 15, 2008. + +
  278. +
  279. ^ "Ryan drops out of Senate race in Illinois". CNN. June 25, 2004. Archived from the original on January 8, 2018. Retrieved May 18, 2012. +
    • Mendell (2007), pp. 260–271.
    +
  280. +
  281. ^ Lannan, Maura Kelly (August 9, 2004). "Alan Keyes enters U.S. Senate race in Illinois against rising Democratic star". The San Diego Union-Tribune. Associated Press. Archived from the original on December 14, 2011. Retrieved April 13, 2008. +
  282. +
  283. ^ "America Votes 2004: U.S. Senate / Illinois". CNN. 2005. Archived from the original on April 16, 2008. Retrieved April 13, 2008. + +
  284. +
  285. ^ United States Congress. "Barack Obama (id: o000167)". Biographical Directory of the United States Congress. Retrieved October 12, 2011. +
  286. +
  287. ^ "Member Info". Congressional Black Caucus. Archived from the original on July 9, 2008. Retrieved June 25, 2008. +
  288. +
  289. ^ "Lugar–Obama Nonproliferation Legislation Signed into Law by the President". Richard Lugar U.S. Senate Office. January 11, 2007. Archived from the original on December 18, 2008. Retrieved April 27, 2008. See also: Lugar, Richard G.; Obama, Barack (December 3, 2005). "Junkyard Dogs of War". The Washington Post. Archived from the original on October 14, 2008. Retrieved April 27, 2008. +
  290. +
  291. ^ McCormack, John (December 21, 2007). "Google Government Gone Viral". Weekly Standard. Archived from the original on April 23, 2008. Retrieved April 27, 2008. See also: "President Bush Signs Coburn–Obama Transparency Act". Tom Coburn U.S. Senate Office. September 26, 2006. Archived from the original on May 1, 2008. Retrieved April 27, 2008. +
  292. +
  293. ^ "S. 3077: Strengthening Transparency and Accountability in Federal Spending Act of 2008: 2007–2008 (110th Congress)". Govtrack.us. June 3, 2008. Archived from the original on May 3, 2012. Retrieved May 18, 2012. +
  294. +
  295. ^ "S. 1033, Secure America and Orderly Immigration Act". Library of Congress. May 12, 2005. Archived from the original on February 26, 2017. Retrieved February 25, 2017. +
  296. +
  297. ^ "Democratic Republic of the Congo". United States Conference of Catholic Bishops. April 2006. Archived from the original on January 8, 2011. Retrieved January 26, 2012. +
  298. +
  299. ^ "The IRC Welcomes New U.S. Law on Congo". International Rescue Committee. January 5, 2007. Archived from the original on August 7, 2011. Retrieved April 27, 2008. +
  300. +
  301. ^ Weixel, Nathaniel (November 15, 2007). "Feingold, Obama Go After Corporate Jet Travel". The Hill. Archived from the original on May 15, 2008. Retrieved April 27, 2008. +
  302. +
  303. ^ Weixel, Nathaniel (December 5, 2007). "Lawmakers Press FEC on Bundling Regulation". The Hill. Archived from the original on April 16, 2008. Retrieved April 27, 2008. See also: "Federal Election Commission Announces Plans to Issue New Regulations to Implement the Honest Leadership and Open Government Act of 2007". Federal Election Commission. September 24, 2007. Archived from the original on April 11, 2008. Retrieved April 27, 2008. +
  304. +
  305. ^ "Obama, Bond Hail New Safeguards on Military Personality Disorder Discharges, Urge Further Action". Kit Bond U.S. Senate Office. October 1, 2007. Archived from the original on December 5, 2010. Retrieved April 27, 2008. +
  306. +
  307. ^ "Obama, Bond Applaud Senate Passage of Amendment to Expedite the Review of Personality Disorder Discharge Cases". March 14, 2008. Archived from the original on December 18, 2008. +
  308. +
  309. ^ "Iran Sanctions Enabling Act of 2009 (2009—S. 1065)". GovTrack.us. Archived from the original on August 28, 2018. Retrieved August 27, 2018. +
  310. +
  311. ^ "Obama, Schiff Provision to Create Nuclear Threat Reduction Plan Approved" (Press release). Barack Obama U.S. Senate Office. December 20, 2007. Archived from the original on December 18, 2008. +
  312. +
  313. ^ "Senate Passes Obama, McCaskill Legislation to Provide Safety Net for Families of Wounded Service Members". Barack Obama U.S. Senate Office. August 2, 2007. Archived from the original on December 18, 2008. Retrieved April 27, 2008. +
  314. +
  315. ^ "Committee Assignments". Barack Obama U.S. Senate Office. December 9, 2006. Archived from the original on December 9, 2006. Retrieved April 27, 2008. +
  316. +
  317. ^ "Obama Gets New Committee Assignments". Barack Obama U.S. Senate Office. Associated Press. November 15, 2006. Archived from the original on December 18, 2008. Retrieved April 27, 2008. +
  318. +
  319. ^ Baldwin, Tom (December 21, 2007). "'Stay at home' Barack Obama comes under fire for a lack of foreign experience". Sunday Times (UK). Archived from the original on April 15, 2020. Retrieved April 27, 2008. +
  320. +
  321. ^ Larson, Christina (September 2006). "Hoosier Daddy: What Rising Democratic Star Barack Obama Can Learn from an Old Lion of the GOP". Washington Monthly. Archived from the original on April 30, 2008. Retrieved April 27, 2008. + +
  322. +
  323. ^ Mason, Jeff (November 16, 2008). "Obama resigns Senate seat, thanks Illinois". Reuters. Retrieved March 10, 2009. +
  324. +
  325. ^ a b Pearson, Rick; Long, Ray (February 10, 2007). "Obama: I'm running for president". Chicago Tribune. Archived from the original on August 13, 2007. Retrieved September 20, 2008. +
  326. +
  327. ^ "Obama Launches Presidential Bid". BBC News. February 10, 2007. Archived from the original on February 2, 2008. Retrieved January 14, 2008. +
  328. +
  329. ^ Parsons, Christi (February 10, 2007). "Obama's launch site: Symbolic Springfield: Announcement venue evokes Lincoln legacy". Chicago Tribune. Archived from the original on May 11, 2011. Retrieved June 12, 2009. +
  330. +
  331. ^ "Barack Obama on the Issues: What Would Be Your Top Three Overall Priorities If Elected?". The Washington Post. Archived from the original on May 9, 2008. Retrieved April 14, 2008. See also: + +
  332. +
  333. ^ Tumulty, Karen (May 8, 2008). "The Five Mistakes Clinton Made". Time. Archived from the original on December 11, 2008. Retrieved November 11, 2008. + +
  334. +
  335. ^ Nagourney, Adam; Zeleny, Jeff (June 5, 2008). "Clinton to End Bid and Endorse Obama". The New York Times. Archived from the original on June 5, 2008. Retrieved November 20, 2010. +
  336. +
  337. ^ a b Nagourney, Adam; Zeleny, Jeff (August 23, 2008). "Obama Chooses Biden as Running Mate". The New York Times. Archived from the original on April 1, 2009. Retrieved September 20, 2008. +
  338. +
  339. ^ Baldwin, Tom (August 27, 2008). "Hillary Clinton: 'Barack is my candidate'". The Times. ISSN 0140-0460. Archived from the original on December 15, 2021. Retrieved December 15, 2021. +
  340. +
  341. ^ Nagourney, Adam (August 28, 2008). "Obama Wins Nomination; Biden and Bill Clinton Rally Party". The New York Times. ISSN 0362-4331. Archived from the original on August 27, 2008. Retrieved December 15, 2021. +
  342. +
  343. ^ Liasson, Mara; Norris, Michele (July 7, 2008). "Obama To Accept Nomination at Mile High Stadium". NPR. Archived from the original on March 16, 2015. Retrieved December 22, 2010. +
  344. +
  345. ^ "Obama accepts Democrat nomination". BBC News. August 29, 2008. Archived from the original on August 28, 2008. Retrieved August 29, 2008. + +
  346. +
  347. ^ Lloyd, Robert (August 29, 2008). "Barack Obama, Al Gore Raise the Roof at Invesco Field". Los Angeles Times. Archived from the original on September 6, 2008. Retrieved August 29, 2008. +
  348. +
  349. ^ Malone, Jim (July 2, 2007). "Obama Fundraising Suggests Close Race for Party Nomination". Voice of America. Archived from the original on September 14, 2007. + +
  350. +
  351. ^ Salant, Jonathan D. (June 19, 2008). "Obama Won't Accept Public Money in Election Campaign". Bloomberg. Archived from the original on February 7, 2017. Retrieved June 19, 2008. +
  352. +
  353. ^ "Commission on Presidential Debates Announces Sites, Dates, Formats and Candidate Selection Criteria for 2008 General Election" (Press release). Commission on Presidential Debates. November 19, 2007. Archived from the original on July 6, 2008. + +
  354. +
  355. ^ Johnson, Alex (November 4, 2008). "Barack Obama elected 44th president". NBC News. Retrieved February 20, 2009. + +
  356. +
  357. ^ "General Election: McCain vs. Obama". Real Clear Politics. Archived from the original on February 17, 2009. Retrieved February 20, 2009. +
  358. +
  359. ^ "Obama wins historic US election". BBC News. November 5, 2008. Archived from the original on December 18, 2008. Retrieved November 5, 2008. + +
  360. +
  361. ^ Obama, Barack (2008). "Transcript of Senator Barack Obama's speech to supporters after the Feb. 5 nominating contests, as provided by Federal News Service". The New York Times. Archived from the original on June 21, 2023. Retrieved June 21, 2023. Change will not come if we wait for some other person or if we wait for some other time. We are the ones we've been waiting for. We are the change that we seek. +
  362. +
  363. ^ Johnson, Wesley (November 5, 2008). "Change has come, says President-elect Obama". The Independent. London. Archived from the original on December 9, 2008. Retrieved November 5, 2008. +
  364. +
  365. ^ "U.S. Senate: Senators Who Became President". senate.gov. Archived from the original on July 24, 2021. Retrieved August 27, 2021. +
  366. +
  367. ^ Shear, Michael D. (April 4, 2011). "Obama Begins Re-Election Facing New Political Challenges". The New York Times (blog). Archived from the original on April 5, 2011. +
  368. +
  369. ^ "Obama announces re-election bid". United Press International. April 4, 2011. Archived from the original on May 10, 2011. +
  370. +
  371. ^ Zeleny, Jeff & Calmes, Jackie (April 4, 2011). "Obama Opens 2012 Campaign, With Eye on Money and Independent Voters". The New York Times. Archived from the original on November 15, 2012. Retrieved April 5, 2011. +
  372. +
  373. ^ Yoon, Robert (April 3, 2012). "Leading presidential candidate to clinch nomination Tuesday". CNN (blog). Archived from the original on April 26, 2012. Retrieved May 2, 2012. +
  374. +
  375. ^ "Obama clinches Democratic nomination". CNN (blog). April 3, 2012. Archived from the original on April 4, 2012. Retrieved April 3, 2012. +
  376. +
  377. ^ Cohen, Tom (September 6, 2012). "Clinton says Obama offers a better path forward for America". CNN. Archived from the original on July 6, 2015. Retrieved July 5, 2015. +
  378. +
  379. ^ Lauter, David (November 8, 2012). "Romney campaign gives up in Florida". Chicago Tribune. Archived from the original on November 9, 2012. Retrieved July 5, 2015. +
  380. +
  381. ^ Barnes, Robert (November 6, 2012). "Obama wins a second term as U.S. president". The Washington Post. Archived from the original on April 17, 2015. Retrieved July 5, 2015. +
  382. +
  383. ^ Welch, William M.; Strauss, Gary (November 7, 2012). "With win in critical battleground states, Obama wins second term". USA Today. Archived from the original on June 16, 2015. Retrieved July 5, 2015. +
  384. +
  385. ^ FEC (July 2013). "Election Results for the U.S. President, the U.S. Senate and the U.S. House of Representatives" (PDF). Federal Elections Commission. p. 5. Archived from the original (PDF) on October 2, 2013. Retrieved August 20, 2013. +
  386. +
  387. ^ Brownstein, Ronald (November 9, 2012). "The U.S. has reached a demographic milestone—and it's not turning back". National Journal. Archived from the original on November 11, 2012. Retrieved July 5, 2015. +
  388. +
  389. ^ Nichols, John (November 9, 2012). "Obama's 3 Million Vote, Electoral College Landslide, Majority of States Mandate". The Nation. Archived from the original on November 27, 2012. Retrieved November 18, 2012. +
  390. +
  391. ^ Lee, Kristen A. (November 7, 2012). "Election 2012: President Obama gives victory speech in front of thousands in Chicago, 'I have never been more hopeful about America'". Daily News. New York. Archived from the original on November 9, 2012. Retrieved November 8, 2012. +
  392. +
  393. ^ a b Shear, Michael (January 21, 2013). "Obama Offers Liberal Vision: 'We Must Act'". The New York Times. Archived from the original on January 21, 2013. Retrieved July 10, 2013. +
  394. +
  395. ^ Gearan, Anne; Baldor, Lolita C. (January 23, 2009). "Obama asks Pentagon for responsible Iraq drawdown". Pittsburgh Post-Gazette. Associated Press. Archived from the original on February 23, 2020. Retrieved February 23, 2020. +
  396. +
  397. ^ Glaberson, William (January 21, 2009). "Obama Orders Halt to Prosecutions at Guantánamo". The New York Times. Archived from the original on April 16, 2009. Retrieved February 3, 2009. +
  398. +
  399. ^ "Senate blocks transfer of Gitmo detainees", NBC News, Associated Press, May 20, 2009, archived from the original on November 4, 2014, retrieved March 22, 2011 +
  400. +
  401. ^ Serbu, Jared (January 7, 2011), "Obama signs Defense authorization bill", Federal News Radio, archived from the original on December 12, 2018, retrieved March 22, 2011 +
  402. +
  403. ^ Northam, Jackie (January 23, 2013). "Obama's Promise To Close Guantanamo Prison Falls Short". NPR. Archived from the original on March 26, 2013. Retrieved April 22, 2013. +
  404. +
  405. ^ Savage, Charlie (December 30, 2009). "Obama Curbs Secrecy of Classified Documents". The New York Times. ISSN 0362-4331. Archived from the original on December 20, 2022. Retrieved December 20, 2022. +
  406. +
  407. ^ Meckler, Laura (January 24, 2009). "Obama lifts 'gag rule' on family-planning groups". The Wall Street Journal. p. A3. Archived from the original on July 23, 2015. Retrieved September 21, 2012. +
    • Stein, Rob; Shear, Michael (January 24, 2009). "Funding restored to groups that perform abortions, other care". The Washington Post. p. A3. Archived from the original on November 11, 2012. Retrieved September 21, 2012. Lifting the Mexico City Policy would not permit U.S. tax dollars to be used for abortions, but it would allow funding to resume to groups that provide other services, including counseling about abortions.
    +
  408. +
  409. ^ Stolberg, Sheryl Gay (January 30, 2009). "Obama Signs Equal-Pay Legislation". The New York Times. Archived from the original on January 30, 2009. Retrieved June 15, 2009. +
  410. +
  411. ^ Levey, Noam N. (February 5, 2009). "Obama signs into law expansion of SCHIP health care program for children". Chicago Tribune. Archived from the original on April 30, 2009. Retrieved June 15, 2009. +
  412. +
  413. ^ "Obama overturns Bush policy on stem cells". CNN. March 9, 2009. Archived from the original on March 30, 2010. Retrieved April 18, 2010. +
  414. +
  415. ^ Desjardins, Lisa; Keck, Kristi; Mears, Bill (August 6, 2009). "Senate confirms Sotomayor for Supreme Court". CNN. Archived from the original on September 25, 2011. Retrieved August 6, 2009. +
  416. +
  417. ^ Hamby, Peter; Henry, Ed; Malveaux, Suzanne; Mears, Bill. "Obama nominates Sotomayor to Supreme Court". Archived from the original on September 15, 2017. Retrieved September 13, 2014. +
  418. +
  419. ^ Sherman, Mark (October 4, 2010). "New Era Begins on High Court: Kagan Takes Place as Third Woman". Associated Press. Archived from the original on October 10, 2017. Retrieved November 13, 2010. +
  420. +
  421. ^ "Obama Administration Implements Priority Enforcement Program, Limits Interior Enforcement". www.numbersusa.com. June 24, 2015. Archived from the original on May 25, 2023. Retrieved May 25, 2023. +
  422. +
  423. ^ Block, Robert; Matthews, Mark K. (January 27, 2010). "White House won't fund NASA moon program". Los Angeles Times. Archived from the original on October 26, 2019. Retrieved January 30, 2011. President Obama's budget proposal includes no money for the Ares I and Ares V rocket or Constellation program. Instead, NASA would be asked to monitor climate change and develop a new rocket +
  424. +
  425. ^ Mardell, Mark (January 16, 2013). "US gun debate: Obama unveils gun control proposals". BBC News. Archived from the original on January 16, 2013. Retrieved January 16, 2013. +
  426. +
  427. ^ "What's in Obama's Gun Control Proposal". The New York Times. January 16, 2013. Archived from the original on February 21, 2013. Retrieved February 12, 2013. +
  428. +
  429. ^ "Obama announces gun control executive action (full transcript)". CNN. January 5, 2016. Archived from the original on February 21, 2016. Retrieved January 7, 2016. +
  430. +
  431. ^ "Obama, in Europe, signs Patriot Act extension". NBC News. May 27, 2011. Archived from the original on August 10, 2019. Retrieved August 8, 2019. +
  432. +
  433. ^ Wolf, Z. Byron (August 13, 2013). "Fact-checking Obama's claims about Snowden". CNN. Archived from the original on August 8, 2019. Retrieved August 8, 2019. +
  434. +
  435. ^ Hosenball, Mark (April 3, 2014). "Obama's NSA overhaul may require phone carriers to store more data". Reuters. Archived from the original on June 2, 2022. Retrieved August 8, 2019. +
  436. +
  437. ^ Ackerman, Spencer (January 17, 2014). "Obama to overhaul NSA's bulk storage of Americans' telephone data". The Guardian. Archived from the original on August 12, 2019. Retrieved August 12, 2019. +
  438. +
  439. ^ a b c Roth, Kenneth (January 9, 2017). "Barack Obama's Shaky Legacy on Human Rights". Human Rights Watch. Archived from the original on February 2, 2021. Retrieved June 26, 2022. +
  440. +
  441. ^ Dyson, Michael Eric (2016). The Black Presidency: Barack Obama and the Politics of Race in America. Houghton Mifflin Harcourt. p. 275. ISBN 978-0-544-38766-9. +
  442. +
  443. ^ Gillion, Daniel Q. (2016). Governing with Words. doi:10.1017/CBO9781316412299. ISBN 978-1-316-41229-9. Archived from the original on August 10, 2019. Retrieved June 5, 2019. +
  444. +
  445. ^ Butler, Bennett; Mendelberg, Tali; Haines, Pavielle E. (2019). ""I'm Not the President of Black America": Rhetorical versus Policy Representation". Perspectives on Politics. 17 (4): 1038–1058. doi:10.1017/S1537592719000963. ISSN 1537-5927. +
  446. +
  447. ^ a b Rodgers, Walter (January 5, 2010). "A year into Obama's presidency, is America postracial?". The Christian Science Monitor. Archived from the original on November 17, 2015. Retrieved November 15, 2015. +
  448. +
  449. ^ Shear, Michael; Alcindor, Yamiche (January 14, 2017). "Jolted by Deaths, Obama Found His Voice on Race". The New York Times. Archived from the original on January 16, 2017. Retrieved January 17, 2017. +
  450. +
  451. ^ Cillizza, Chris (August 14, 2014). "President Obama's vision of post-racial America faces another stress test with Ferguson". The Washington Post. Archived from the original on November 17, 2015. Retrieved November 15, 2015. +
  452. +
  453. ^ Blake, John (July 1, 2016). "What black America won't miss about Obama". CNN. Archived from the original on October 3, 2022. +
  454. +
  455. ^ Cillizza, Chris (July 19, 2013). "President Obama's remarkably personal speech on Trayvon Martin and race in America". The Washington Post. Archived from the original on November 17, 2015. Retrieved November 15, 2015. +
  456. +
  457. ^ a b Capeheart, Jonathan (February 27, 2015). "From Trayvon Martin to 'black lives matter'". The Washington Post. Archived from the original on November 17, 2015. Retrieved November 15, 2015. +
  458. +
  459. ^ Bacon, Perry Jr. (January 3, 2015). "In Wake of Police Shootings, Obama Speaks More Bluntly About Race". NBC. Archived from the original on November 11, 2015. Retrieved November 15, 2015. +
  460. +
  461. ^ Hirschfield Davis, Julie (July 13, 2016). "Obama Urges Civil Rights Activists and Police to Bridge Divide". The New York Times. Archived from the original on July 18, 2016. Retrieved July 23, 2016. +
  462. +
  463. ^ "U.S. Worries About Race Relations Reach a New High". Gallup. April 11, 2016. Archived from the original on December 20, 2016. Retrieved December 5, 2016. +
  464. +
  465. ^ "Obama signs hate crimes bill into law". CNN. October 28, 2009. Archived from the original on November 12, 2020. Retrieved October 12, 2011. +
  466. +
  467. ^ Preston, Julia (October 30, 2009). "Obama Lifts a Ban on Entry Into U.S. by H.I.V.-Positive People". The New York Times. Archived from the original on April 7, 2010. Retrieved February 8, 2017. +
  468. +
  469. ^ "'Don't ask, don't tell' repealed as Obama signs landmark law". The Guardian. London. December 22, 2010. Archived from the original on December 23, 2010. Retrieved June 2, 2018. +
  470. +
  471. ^ Sheryl Gay Stolberg (December 23, 2010). "Obama Signs Away 'Don't Ask, Don't Tell'". The New York Times. Archived from the original on May 12, 2011. +
  472. +
  473. ^ a b Redden, Molly; Holpuch, Amanda (June 30, 2016). "US military ends ban on transgender service members". The Guardian. Archived from the original on February 19, 2017. Retrieved February 19, 2017. +
  474. +
  475. ^ Baim, Tracy (January 14, 2009). "Windy City Times exclusive: Obama's Marriage Views Changed. WCT Examines His Step Back". Windy City Times. Archived from the original on November 14, 2012. Retrieved May 10, 2012. +
  476. +
  477. ^ Baim, Tracy (February 4, 2004). "Obama Seeks U.S. Senate seat". Windy City Times. Archived from the original on May 14, 2012. Retrieved May 10, 2012. +
  478. +
  479. ^ "President Barack Obama's shifting stance on gay marriage". PolitiFact. Archived from the original on November 26, 2018. Retrieved November 28, 2018. +
  480. +
  481. ^ Daly, Corbett (May 9, 2012). "Obama backs same-sex marriage". CBS News. Archived from the original on December 19, 2013. Retrieved May 9, 2012. +
  482. +
  483. ^ Stein, Sam (May 9, 2012). "Obama Backs Gay Marriage". The Huffington Post. Archived from the original on June 29, 2015. Retrieved July 5, 2015. +
  484. +
  485. ^ Robillard, Kevin (January 21, 2013). "First inaugural use of the word 'gay'". Politico. Archived from the original on July 23, 2015. Retrieved January 21, 2013. +
  486. +
  487. ^ Michelson, Noah (January 21, 2013). "Obama Inauguration Speech Makes History With Mention of Gay Rights Struggle, Stonewall Uprising". The Huffington Post. Archived from the original on September 19, 2018. Retrieved January 21, 2013. +
  488. +
  489. ^ Reilly, Ryan J. (February 28, 2013). "Obama Administration: Gay Marriage Ban Unconstitutional In Prop. 8 Supreme Court Case". The Huffington Post. Archived from the original on April 11, 2013. Retrieved April 21, 2013. +
  490. +
  491. ^ Mears, Bill (February 27, 2013). "Obama administration weighs in on defense of marriage law". CNN. Archived from the original on September 1, 2013. Retrieved April 21, 2013. +
  492. +
  493. ^ "Stimulus package en route to Obama's desk". CNN. February 14, 2009. Archived from the original on March 30, 2009. Retrieved March 29, 2009. +
  494. +
  495. ^ "Obama's remarks on signing the stimulus plan". CNN. February 17, 2009. Archived from the original on February 20, 2009. Retrieved February 17, 2009. +
  496. +
  497. ^ Andrews, Edmund L.; Dash, Eric (March 23, 2009). "U.S. Expands Plan to Buy Banks' Troubled Assets". The New York Times. Archived from the original on March 25, 2009. Retrieved April 12, 2010. + +
  498. +
  499. ^ "White House questions viability of GM, Chrysler". The Huffington Post. March 30, 2009. Archived from the original on April 7, 2009. +
  500. +
  501. ^ Bunkley, Nick; Vlasic, Bill (April 27, 2009). "Chrysler and Union Agree to Deal Before Federal Deadline". The New York Times. Archived from the original on April 28, 2009. Retrieved April 12, 2010. +
  502. +
  503. ^ Hughes, John; Salas, Caroline; Green, Jeff; Van Voris, Bob (June 1, 2009). "GM Begins Bankruptcy Process With Filing for Affiliate". Bloomberg News. Archived from the original on June 13, 2010. Retrieved July 5, 2015. +
  504. +
  505. ^ Conkey, Christopher; Radnofsky, Louise (June 9, 2009). "Obama Presses Cabinet to Speed Stimulus Spending". The Wall Street Journal. News Corp. Archived from the original on July 26, 2013. Retrieved July 5, 2015. +
  506. +
  507. ^ Hedgpeth, Dana (August 21, 2009). "U.S. Says 'Cash for Clunkers' Program Will End on Monday". The Washington Post. Archived from the original on May 16, 2011. Retrieved March 26, 2010. +
  508. +
  509. ^ Szczesny, Joseph R. (August 26, 2009). "Was Cash for Clunkers a Success?". Time. Archived from the original on August 28, 2009. Retrieved March 26, 2010. +
  510. +
  511. ^ Mian, Atif R.; Sufi, Amir (September 1, 2010). "The Effects of Fiscal Stimulus: Evidence from the 2009 'Cash for Clunkers' Program". The Quarterly Journal of Economics. 127 (3): 1107–1142. doi:10.2139/ssrn.1670759. S2CID 219352572. SSRN 1670759. +
  512. +
  513. ^ Goldman, David (April 6, 2009). "CNNMoney.com's bailout tracker". CNNMoney. Vol. 06. p. 20. Archived from the original on April 7, 2019. Retrieved March 26, 2010. +
  514. +
  515. ^ Stein, Sylvie. "First Read—A breakdown of the debt-limit legislation". MSNBC. Archived from the original on January 14, 2012. Retrieved August 3, 2011. +
  516. +
  517. ^ "House passes debt ceiling bill". NBC News. March 8, 2011. Archived from the original on July 21, 2020. Retrieved August 3, 2011. +
  518. +
  519. ^ Theodossiou, Eleni; Hipple, Steven F. (2011). "Unemployment Remains High in 2010" (PDF). Monthly Labor Review. 134 (3): 3–22. Archived from the original (PDF) on May 8, 2011. Retrieved April 7, 2011. +
  520. +
  521. ^ Eddlemon, John P. (2011). "Payroll Employment Turns the Corner in 2010" (PDF). Monthly Labor Review. 134 (3): 23–32. Archived from the original (PDF) on May 6, 2011. Retrieved April 7, 2011. +
  522. +
  523. ^ "Unemployment Rate". Bureau of Labor Statistics. Archived from the original on November 21, 2011. Retrieved December 11, 2012. +
  524. +
  525. ^ "Unemployment Rate". Bureau of Labor Statistics. Archived from the original on April 28, 2019. Retrieved January 10, 2014. +
  526. +
  527. ^ "Unemployment Rate". Bureau of Labor Statistics. Archived from the original on April 28, 2019. Retrieved June 6, 2014. +
  528. +
  529. ^ a b "Percent Change in Real Gross Domestic Product (Quarterly)". National Income and Product Accounts Table. Bureau of Economic Analysis. Archived from the original on May 12, 2011. Retrieved April 7, 2011. +
  530. +
  531. ^ Harding, Robin (July 28, 2010). "Beige Book survey reports signs of slowdown". Financial Times. Archived from the original on July 29, 2010. Retrieved July 29, 2010. +
  532. +
  533. ^ "Percent Change in Real Gross Domestic Product (Annual)". National Income and Product Accounts Table. Bureau of Economic Analysis. Archived from the original on May 12, 2011. Retrieved April 7, 2011. +
  534. +
  535. ^ "Unemployment Rate". Bureau of Labor Statistics. Archived from the original on April 28, 2019. Retrieved September 12, 2018. +
  536. +
  537. ^ "1-month net change in employment". Bureau of Labor Statistics. Archived from the original on April 28, 2019. Retrieved September 12, 2018. +
  538. +
  539. ^ a b "Estimated Impact of the American Recovery and Reinvestment Act on Employment and Economic Output". Congressional Budget Office. November 22, 2011. Archived from the original on February 29, 2012. Retrieved February 21, 2012. +
  540. +
  541. ^ a b Calmes, Jackie; Cooper, Michael (November 20, 2009). "New Consensus Sees Stimulus Package as Worthy Step". The New York Times. Archived from the original on May 11, 2011. Retrieved December 21, 2010. +
  542. +
  543. ^ "CBO: Stimulus created as many as 2.1 million jobs". February 23, 2010. Archived from the original on March 3, 2010. Retrieved April 25, 2010. +
  544. +
  545. ^ Isidore, Chris (January 29, 2010). "Best economic growth in six years". CNN. Archived from the original on April 20, 2010. Retrieved April 18, 2010. +
  546. +
  547. ^ "New NABE Survey Shows Business Recovery Gaining Momentum, with More Jobs Ahead". Archived from the original on May 2, 2010. Retrieved April 26, 2010. +
  548. +
  549. ^ "U.S. GDP Growth Relative to Original NATO Members". Politics that Work. March 9, 2015. Archived from the original on April 23, 2015. Retrieved April 14, 2015. +
  550. +
  551. ^ Chapple, Irene (May 29, 2013). "OECD: U.S. will recover faster, Europe faces unemployment crisis". CNN. Archived from the original on December 20, 2019. Retrieved January 16, 2020. +
  552. +
  553. ^ Herszenhorn, David M.; Stolberg, Sheryl Gay (December 7, 2010). "Democrats Skeptical of Obama on New Tax Plan". The New York Times. Archived from the original on December 9, 2010. +
  554. +
  555. ^ "Obama signs tax deal into law". CNN. December 17, 2010. Archived from the original on December 18, 2010. Retrieved December 17, 2010. +
  556. +
  557. ^ Kuhnhenn, Jim (December 4, 2013). "Obama: Income Inequality a Defining Challenge". Associated Press. Archived from the original on December 7, 2013. Retrieved January 9, 2014. +
  558. +
  559. ^ "President Obama uses his final months to bring congressional approval of a 12-nation free trade pact called the Trans-Pacific Partnership". CBS News. September 5, 2016. Archived from the original on September 6, 2016. Retrieved September 5, 2016. +
  560. +
  561. ^ "Obama Halts Drilling Projects, Defends Actions". NPR. May 27, 2010. Archived from the original on September 19, 2018. Retrieved April 5, 2018. +
  562. +
  563. ^ Jonsson, Patrik (May 29, 2010). "Gulf oil spill: Obama's big political test". The Christian Science Monitor. Archived from the original on June 1, 2010. Retrieved June 6, 2010. +
  564. +
  565. ^ Neuman, Scott (March 31, 2010). "Obama Ends Ban On East Coast Offshore Drilling". NPR. Archived from the original on November 3, 2021. Retrieved October 30, 2021. +
  566. +
  567. ^ Goldenberg, Suzanne (July 28, 2013). "Barack Obama expresses reservations about Keystone XL pipeline project". The Guardian. London. Archived from the original on December 29, 2016. Retrieved November 7, 2023. +
  568. +
  569. ^ Stein, Sam (June 25, 2013). "Obama: Keystone XL Should Not Be Approved If It Will Increase Greenhouse Gas Emissions". The Huffington Post. Archived from the original on March 1, 2020. Retrieved January 16, 2020. +
  570. +
  571. ^ Calamur, Krishnadev (February 24, 2015). "Obama Vetoes Keystone XL Pipeline Bill". NPR. Archived from the original on June 9, 2015. Retrieved February 24, 2015. +
  572. +
  573. ^ Barron-Lopez, Laura (March 4, 2015). "Keystone veto override fails". The Hill. Capitol Hill Publishing. Archived from the original on July 15, 2015. Retrieved July 2, 2015. +
  574. +
  575. ^ "Obama bans oil drilling 'permanently' in millions of acres of ocean". BBC News. December 21, 2016. Archived from the original on October 30, 2021. Retrieved October 30, 2021. +
  576. +
  577. ^ Smith, David (December 20, 2016). "This article is more than 4 years old Barack Obama bans oil and gas drilling in most of Arctic and Atlantic oceans". The Guardian. Archived from the original on October 30, 2021. Retrieved October 30, 2021. +
  578. +
  579. ^ Volcovici, Valerie; Gardner, Timothy (December 20, 2016). "Obama bans new oil, gas drilling off Alaska, part of Atlantic coast". Reuters. Archived from the original on October 30, 2021. Retrieved October 30, 2021. +
  580. +
  581. ^ Eilperin, Juliet; Dennis, Brady (December 28, 2016). "With new monuments in Nevada, Utah, Obama adds to his environmental legacy". The Washington Post. Archived from the original on January 8, 2017. Retrieved November 7, 2023. +
  582. +
  583. ^ "Obama's Newly Designated National Monuments Upset Some Lawmakers". All Things Considered. NPR. December 29, 2016. Archived from the original on October 10, 2017. Retrieved April 5, 2018. +
  584. +
  585. ^ Connolly, Amy R. (February 13, 2016). "Obama expands public lands more than any U.S. president". United Press International. Archived from the original on May 19, 2020. Retrieved January 16, 2020. +
  586. +
  587. ^ a b Sweet, Lynn (July 22, 2009). "Obama July 22, 2009 press conference. Transcript". Chicago Sun-Times. Archived from the original on April 16, 2015. Retrieved July 5, 2015. +
  588. +
  589. ^ Stolberg, Sheryl Gay; Zeleny, Jeff (September 9, 2009). "Obama, Armed With Details, Says Health Plan Is Necessary". The New York Times. Archived from the original on September 12, 2009. Retrieved July 5, 2015. +
  590. +
  591. ^ Allen, Mike (September 9, 2009). "Barack Obama will hedge on public option". Politico. Archived from the original on July 26, 2013. Retrieved July 5, 2015. +
  592. +
  593. ^ "Health Insurance Premium Credits in the PPACA" (PDF). Congressional Research Service. Archived (PDF) from the original on October 14, 2012. Retrieved May 17, 2015. +
  594. +
  595. ^ "Obama calls for Congress to face health care challenge". CNN. September 9, 2009. Archived from the original on September 10, 2009. Retrieved September 9, 2009. +
  596. +
  597. ^ Nasaw, Daniel (March 10, 2009). "Stem cell". The Guardian. Archived from the original on July 26, 2013. Retrieved September 13, 2014. +
  598. +
  599. ^ Hulse, Carl; Pear, Robert (November 7, 2009). "Sweeping Health Care Plan Passes House". The New York Times. Archived from the original on March 31, 2011. Retrieved November 8, 2009. +
  600. +
  601. ^ Herszenhorn, David M.; Calmes, Jackie (December 7, 2009). "Abortion Was at Heart of Wrangling". The New York Times. Archived from the original on March 31, 2011. Retrieved December 6, 2009. +
  602. +
  603. ^ Hensley, Scott (December 24, 2009). "Senate Says Yes To Landmark Health Bill". NPR. Archived from the original on January 21, 2010. Retrieved December 24, 2009. +
  604. +
  605. ^ Stolberg, Sheryl Gay (March 23, 2010). "Obama Signs Landmark Health Care Bill". The New York Times. Archived from the original on March 25, 2010. Retrieved March 23, 2010. +
  606. +
  607. ^ Rice, Sabriya (March 25, 2010). "5 key things to remember about health care reform". CNN. Archived from the original on January 2, 2013. Retrieved January 6, 2013. +
  608. +
  609. ^ Grier, Peter (March 20, 2010). "Health Care Reform Bill 101". The Christian Science Monitor. Archived from the original on July 6, 2015. Retrieved July 5, 2015. +
  610. +
  611. ^ Elmendorf, Douglas W. (November 30, 2009). "An Analysis of Health Insurance Premiums Under the Patient Protection and Affordable Care Act" (PDF). Congressional Budget Office. Archived (PDF) from the original on February 27, 2012. Retrieved April 9, 2012. +
  612. +
  613. ^ Obama, Barack (August 2, 2016). "United States Health Care Reform". JAMA. 316 (5): 525–532. doi:10.1001/jama.2016.9797. ISSN 0098-7484. PMC 5069435. PMID 27400401. +
  614. +
  615. ^ Grier, Peter (March 21, 2010). "Health care reform bill 101: Who will pay for reform?". Christian Science Monitor. Archived from the original on July 6, 2015. Retrieved July 5, 2015. +
  616. +
  617. ^ Grier, Peter (March 19, 2010). "Health care reform bill 101: Who must buy insurance?". The Christian Science Monitor. Archived from the original on April 5, 2010. Retrieved April 7, 2010. +
  618. +
  619. ^ Elmendorf, Douglas W. (March 20, 2010). "H.R. 4872, Reconciliation Act of 2010 (Final Health Care Legislation)". Congressional Budget Office. Archived from the original on January 2, 2013. Retrieved January 6, 2013. +
  620. +
  621. ^ Barnes, Robert (June 28, 2012). "Supreme Court upholds Obama health care overhaul by 5–4 vote, approving insurance requirement". The Washington Post. Associated Press. Archived from the original on June 28, 2012. Retrieved June 29, 2012. +
  622. +
  623. ^ Leonard, Kimberly. "Supreme Court Upholds Obamacare Subsidies". U.S. News & World Report. Archived from the original on January 16, 2016. Retrieved November 25, 2015. +
  624. +
  625. ^ Colvin, Ross; Barkin, Noah (February 7, 2009). "Biden vows break with Bush era foreign policy". Toronto: Canada.com. Archived from the original on November 6, 2012. Retrieved January 31, 2013. + +
  626. +
  627. ^ "Obama reaches out to Muslim world on TV". NBC News. January 27, 2009. Archived from the original on September 27, 2013. Retrieved June 15, 2009. +
  628. +
  629. ^ "Barack Obama's address to Iran: Full text of Barack Obama's videotaped message to the people and leaders of Iran as they celebrate their New Year's holiday, Nowruz". The Guardian. London. March 20, 2013. Archived from the original on September 6, 2013. Retrieved July 14, 2013. +
  630. +
  631. ^ DeYoung, Karen (April 9, 2009). "Nation U.S. to Join Talks on Iran's Nuclear Program". The Washington Post. Archived from the original on October 4, 2018. Retrieved June 15, 2009. +
  632. +
  633. ^ "Obama in Egypt reaches out to Muslim world". CNN. June 4, 2009. Retrieved January 30, 2011. +
  634. +
  635. ^ Weber, Joseph; Dinan, Stephen (June 26, 2009). "Obama dismisses Ahmadinejad apology request". The Washington Times. Archived from the original on April 10, 2019. Retrieved July 2, 2015. +
  636. +
  637. ^ Lauter, David (June 23, 2014). "Memo justifying drone killing of American Al Qaeda leader is released". Los Angeles Times. Archived from the original on April 30, 2019. Retrieved December 7, 2021. +
  638. +
  639. ^ "Long-sought memo on lethal drone strike is released". Washington Post. June 23, 2014. Archived from the original on August 24, 2022. Retrieved August 15, 2022. +
  640. +
  641. ^ Shane, Scott (August 27, 2015). "The Lessons of Anwar al-Awlaki". The New York Times. ISSN 0362-4331. Archived from the original on August 27, 2015. Retrieved December 7, 2021. +
  642. +
  643. ^ Lauter, David (June 24, 2014). "Memo justifying drone killing of American Al Qaeda leader is released". Los Angeles Times. Archived from the original on April 30, 2019. Retrieved December 7, 2021. +
  644. +
  645. ^ "Saudi Arabia launces air attacks in Yemen". The Washington Post. March 25, 2015. Archived from the original on October 11, 2019. Retrieved August 21, 2017. +
  646. +
  647. ^ "Yemen conflict: US 'could be implicated in war crimes'". BBC News. October 10, 2016. Archived from the original on August 27, 2018. Retrieved August 27, 2018. +
  648. +
  649. ^ Bayoumy, Yara (September 7, 2016). "Obama administration arms sales offers to Saudi top $115 billion: ..." Reuters. Archived from the original on May 8, 2019. Retrieved August 27, 2018. +
  650. +
  651. ^ Stewart, Phil; Strobel, Warren (December 13, 2016). "America 'agrees to stop selling some arms' to Saudi Arabia". The Independent. Archived from the original on April 1, 2019. +
  652. +
  653. ^ Phillips, Tom (September 4, 2016). "Barack Obama 'deliberately snubbed' by Chinese in chaotic arrival at G20". The Guardian. +
  654. +
  655. ^ Feller, Ben (February 27, 2009). "Obama sets firm withdrawal timetable for Iraq". The Gazette. Associated Press. Archived from the original on February 7, 2017. Retrieved March 3, 2009. +
  656. +
  657. ^ Jones, Athena (February 27, 2009). "Obama announces Iraq plan". MSNBC. Archived from the original on November 16, 2014. Retrieved July 2, 2015. +
  658. +
  659. ^ Sykes, Hugh (August 19, 2010). "Last US combat brigade exits Iraq". BBC News. Retrieved December 25, 2012. +
  660. +
  661. ^ MacAskill, Ewen (September 1, 2010). "Barack Obama ends the war in Iraq. 'Now it's time to turn the page'". The Guardian. London. +
  662. +
  663. ^ "All U.S. troops out of Iraq by end of year". NBC News. October 21, 2011. Retrieved December 25, 2012. +
  664. +
  665. ^ "Obama Is Sending 275 US Troops To Iraq". Business Insider. Retrieved June 19, 2014. +
  666. +
  667. ^ Nebehay, Stephanie (September 8, 2014). "New U.N. rights boss warns of 'house of blood' in Iraq, Syria". Reuters. Retrieved July 11, 2015. +
  668. +
  669. ^ "DoD Authorizes War on Terror Award for Inherent Resolve Ops". Defense.gov. October 31, 2014. Retrieved November 22, 2014. +
  670. +
  671. ^ "Islamic State: Coalition 'pledges more troops' for Iraq". BBC News. December 8, 2014. Retrieved August 23, 2015. +
  672. +
  673. ^ Mehta, Aaron (January 19, 2015). "A-10 Performing 11 Percent of Anti-ISIS Sorties". Defense News. Retrieved August 23, 2015. +
  674. +
  675. ^ "1,000 soldiers from the 82nd Airborne headed to Iraq". Stars and Stripes. Retrieved August 23, 2015. +
  676. +
  677. ^ "Stealthy Jet Ensures Other War-Fighting Aircraft Survive". U.S. News & World Report. Archived from the original on August 13, 2015. Retrieved August 23, 2015. +
  678. +
  679. ^ "Obama calls Iraq war a 'dangerous distraction'". CNN. July 15, 2008. Retrieved August 15, 2022. +
  680. +
  681. ^ Broder, John M. (July 16, 2008). "Obama and McCain Duel over Iraq". The New York Times. +
  682. +
  683. ^ Hodge, Amanda (February 19, 2009). "Obama launches Afghanistan Surge". The Australian. Sydney. +
  684. +
  685. ^ "Top U.S. Commander in Afghanistan Is Fired". The Washington Post. May 12, 2009. + +
  686. +
  687. ^ "Obama details Afghan war plan, troop increases". NBC News. Associated Press. December 1, 2009. +
  688. +
  689. ^ "Gates says he agrees with Obama decision on McChrystal". CNN. June 24, 2010. Retrieved September 18, 2010. +
  690. +
  691. ^ Chandrasekaran, Rajiv (February 12, 2013). "Obama wants to cut troop level in Afghanistan in half over next year". The Washington Post. Retrieved February 14, 2013. +
  692. +
  693. ^ Marcus, Jonathan (October 15, 2015). "US troops in Afghanistan: Taliban resurgence sees rethink". BBC News. Retrieved October 15, 2015. +
  694. +
  695. ^ "Obama's Remarks on Iraq and Afghanistan". The New York Times. July 15, 2008. +
  696. +
  697. ^ a b c Mazzetti, Mark; Cooper, Helene; Baker, Peter (May 3, 2011). "Clues Gradually Led to the Location of Osama bin Laden". The New York Times. Archived from the original on May 3, 2011. Retrieved May 4, 2011. +
  698. +
  699. ^ a b Rucker, Philip; Wilson, Scott; Kornblut, Anne E. (May 2, 2011). "Osama bin Laden is killed by U.S. forces in Pakistan". The Washington Post. Retrieved September 13, 2014. +
  700. +
  701. ^ "Official offers details of bin Laden raid". Newsday. May 2, 2011. Retrieved September 13, 2014. +
  702. +
  703. ^ Schabner, Dean; Travers, Karen (May 1, 2011). "Osama bin Laden Killed by U.S. Forces in Pakistan". ABC News. Archived from the original on May 4, 2011. Retrieved May 3, 2011. +
  704. +
  705. ^ Baker, Peter; Cooper, Helene; Mazzetti, Mark (May 2, 2011). "Bin Laden Is Dead, Obama Says". The New York Times. Archived from the original on May 5, 2011. Retrieved May 3, 2011. +
  706. +
  707. ^ Walsh, Declan; Adams, Richard; MacAskill, Ewen (May 2, 2011). "Osama bin Laden is dead, Obama announces". The Guardian. London. Archived from the original on May 3, 2011. Retrieved May 3, 2011. +
  708. +
  709. ^ Dorning, Mike (May 2, 2011). "Death of Bin Laden May Strengthen Obama's Hand in Domestic, Foreign Policy". Bloomberg News. Archived from the original on May 3, 2011. Retrieved May 4, 2011. +
  710. +
  711. ^ Warren, Strobel. "Secret talks in Canada, Vatican City led to Cuba breakthrough". Reuters. Retrieved December 21, 2014. +
  712. +
  713. ^ Morello, Carol; DeYoung, Karen. "Secret U.S.-Cuba diplomacy ended in landmark deal on prisoners, future ties". The Washington Post. Retrieved December 21, 2014. +
  714. +
  715. ^ Roberts, Dan; Luscombe, Richard (December 10, 2013). "Obama shakes hands with Raúl Castro for first time at Mandela memorial". The Guardian. Retrieved February 15, 2017. +
  716. +
  717. ^ Nadeau, Barbie Latza (December 17, 2014). "The Pope's Diplomatic Miracle: Ending the U.S.–Cuba Cold War". The Daily Beast. Retrieved December 18, 2014. +
  718. +
  719. ^ Gillin, Joel (April 13, 2015). "The Cuban Thaw Is Obama's Finest Foreign Policy Achievement to Date". The New Republic. +
  720. +
  721. ^ "Obama announces re-establishment of U.S.-Cuba diplomatic ties". CNN. Retrieved July 1, 2015. +
  722. +
  723. ^ Whitefield, Mimi (July 20, 2015). "United States and Cuba reestablish diplomatic relations". The Miami Herald. Retrieved July 19, 2015. +
  724. +
  725. ^ Julie Hirschfeld Davis; Cave, Damien (March 21, 2016). "Obama Arrives in Cuba, Heralding New Era After Decades of Hostility". The New York Times. p. A1. Archived from the original on March 20, 2016. +
  726. +
  727. ^ Levinson, Charles (August 14, 2010). "U.S., Israel Build Military Cooperation". The Wall Street Journal. New York. Retrieved March 1, 2011. +
  728. +
  729. ^ Kampeas, Ron (October 26, 2012). "For Obama campaign, trying to put to rest persistent questions about 'kishkes'". Jewish Journal. +
  730. +
  731. ^ Berger, Robert (March 25, 2010). "Israel Refuses to Halt Construction in East Jerusalem". Voice of America. Retrieved July 2, 2015. +
  732. +
  733. ^ Kershner, Isabel (March 24, 2010). "Israel Confirms New Building in East Jerusalem". The New York Times. Archived from the original on March 29, 2010. Retrieved April 26, 2010. +
  734. +
  735. ^ "United States vetoes Security Council resolution on Israeli settlements". UN News Service Section. February 18, 2011. Retrieved September 13, 2014. +
  736. +
  737. ^ Levy, Elior (May 22, 2011). "PA challenges Netanyahu to accept 1967 lines". Ynetnews. Retrieved May 22, 2011. +
  738. +
  739. ^ Goldberg, Jeffrey (January 14, 2013). "Obama: 'Israel Doesn't Know What Its Best Interests Are'". Bloomberg. Retrieved January 23, 2013. +
  740. +
  741. ^ Goldberg, Jeffrey (September 13, 2015). "After the Iran Deal: Obama, Netanyahu, and the Future of the Jewish State". The Atlantic. Retrieved September 13, 2015. +
  742. +
  743. ^ Keinon, Herb (July 19, 2014). "Obama reaffirms Israel's right to defend itself". The Times of Israel. +
  744. +
  745. ^ "Netanyahu: Iran nuclear deal makes world much more dangerous, Israel not bound by it". Haaretz. July 14, 2015. Retrieved January 3, 2018. +
  746. +
  747. ^ Collinson, Stephen; Wright, David; Labott, Elise (December 24, 2016). "US Abstains as UN Demands End to Israeli Settlements". CNN. Retrieved January 7, 2017. +
  748. +
  749. ^ Barak, Ravid (December 26, 2016). "Netanyahu on UN Settlement Vote: Israel Will Not Turn the Other Cheek". Haaretz. Retrieved January 7, 2017. +
  750. +
  751. ^ "Israel-Palestinians: Netanyahu Condemns John Kerry Speech". BBC News. December 29, 2016. Retrieved January 7, 2017. +
  752. +
  753. ^ "Israel Halts $6 million to UN to Protest UN Settlements Vote". Fox News (from the Associated Press). January 6, 2017. Retrieved January 7, 2017. +
  754. +
  755. ^ "House Overwhelmingly Votes to Condemn UN Resolution on Israel Settlements". Fox News. January 5, 2017. Retrieved January 7, 2017. +
  756. +
  757. ^ Cortellessa, Eric (January 6, 2017). "US House Passes Motion Repudiating UN Resolution on Israel". The Times of Israel. Retrieved January 17, 2017. +
  758. +
  759. ^ "Floor Statement by Senator McCain Introducing the Senate Resolution Calling for a No-Fly Zone in Libya". Senate.gov. March 14, 2011. Archived from the original on September 27, 2011. Retrieved March 28, 2011. +
  760. +
  761. ^ "Senate Passes Resolution Calling for No-Fly Zone Over Libya". National Journal. March 1, 2011. Archived from the original on May 11, 2011. +
  762. +
  763. ^ "Libya declares ceasefire but fighting goes on". Al Jazeera. Retrieved May 9, 2024. +
  764. +
  765. ^ Kirkpatrick, David D.; Erlanger, Steven; Bumiller, Elisabeth (March 19, 2011). "Allies Open Air Assault on Qaddafi's Forces in Libya". The New York Times. ISSN 0362-4331. Retrieved May 9, 2024. +
  766. +
  767. ^ "Obama says US efforts in Libya have saved lives, control of operation can be turned over soon". Ventura County Star. Associated Press. Archived from the original on August 28, 2011. Retrieved March 22, 2011. +
  768. +
  769. ^ Pannell, Ian (March 21, 2011). "Gaddafi 'not targeted' by allied strikes". BBC News. Archived from the original on June 23, 2011. Retrieved July 3, 2011. +
  770. +
  771. ^ Jones, Sam (March 22, 2011). "F-15 fighter jet crashes in Libya". The Guardian. London. Archived from the original on March 22, 2011. Retrieved March 23, 2011. +
  772. +
  773. ^ "NATO No-Fly Zone over Libya Operation UNIFIED PROTECTOR" (PDF). NATO. March 25, 2011. Archived from the original (PDF) on May 15, 2011. +
  774. +
  775. ^ Montopoli, Brian (March 22, 2011). "Is Obama's Libya offensive constitutional?". CBS News. Retrieved March 22, 2011. +
  776. +
  777. ^ Stein, Sam (March 21, 2011). "Obama's Libya Policy Makes Strange Bedfellows of Congressional Critics". The Huffington Post. Archived from the original on March 23, 2011. Retrieved March 26, 2011. +
  778. +
  779. ^ "Obama juggles Libya promises, realities". CNN. March 25, 2011. Retrieved March 26, 2011. +
  780. +
  781. ^ Malloy, Allie; Treyz, Catherine (April 10, 2016). "Obama admits worst mistake of his presidency — CNN Politics". CNN. Retrieved April 24, 2021. +
  782. +
  783. ^ "President Obama: Libya aftermath 'worst mistake' of presidency". BBC News. April 11, 2016. Retrieved April 24, 2021. +
  784. +
  785. ^ "Assad must go, Obama says". The Washington Post. August 18, 2011. Retrieved November 23, 2015. +
  786. +
  787. ^ Nelson, Colleen. "Obama Says Syrian Leader Bashar al-Assad Must Go". +
  788. +
  789. ^ Hosenball, Mark (August 2, 2012). "Obama authorizes secret support for Syrian rebels". Reuters. Retrieved February 19, 2016. +
  790. +
  791. ^ Shear, Michael D.; Cooper, Helene; Schmitt, Eric (October 9, 2015). "Obama Administration Ends Effort to Train Syrians to Combat ISIS". The New York Times. Archived from the original on October 9, 2015. Retrieved February 20, 2016. +
  792. +
  793. ^ Stewart, Phil; Holton, Kate (October 9, 2015). "U.S. pulls plug on Syria rebel training effort; will focus on weapons supply". Reuters. Retrieved February 20, 2016. +
  794. +
  795. ^ "Obama 'red line' erased as Bashar Assad's chemical weapons use goes unchecked by U.S. military". The Washington Times. May 17, 2015. Retrieved November 23, 2015. +
  796. +
  797. ^ Gordon, Michael (September 14, 2013). "U.S. and Russia Reach Deal to Destroy Syria's Chemical Arms". The New York Times. Archived from the original on September 14, 2013. Retrieved February 19, 2016. +
  798. +
  799. ^ Boghani, Priyanka. "Syria Got Rid of Its Chemical Weapons—But Reports of Attacks Continue". Retrieved February 19, 2016. +
  800. +
  801. ^ "Obama outlines plan to target IS fighters". Al Jazeera. September 11, 2014. Retrieved September 24, 2014. +
  802. +
  803. ^ "Iran deal reached, Obama hails step toward 'more hopeful world'". Reuters. July 14, 2015. Retrieved July 14, 2015. +
  804. +
  805. ^ Solomon, Jay; Norman, Laurence; Lee, Carol E. (July 14, 2015). "Iran, World Powers Prepare to Sign Nuclear Accord". The Wall Street Journal. Retrieved July 14, 2015. +
  806. +
  807. ^ "Landmark deal reached on Iran nuclear program". CNN. July 14, 2015. Retrieved July 14, 2015. +
  808. +
  809. ^ "$1.7-billion payment to Iran was all in cash due to effectiveness of sanctions, White House says". Los Angeles Times. September 7, 2016. Retrieved October 30, 2019. +
  810. +
  811. ^ "Obama Administration Reportedly Shielded Hezbollah From DEA and CIA to Save Iran Nuclear Deal". Haaretz. December 18, 2017. +
  812. +
  813. ^ Meyer, Josh (December 18, 2017). "A Global Threat Emerges". Politico. +
  814. +
  815. ^ Thompson, Loren. "Obama Backs Biggest Nuclear Arms Buildup Since Cold War". Forbes. +
  816. +
  817. ^ Baker, Peter (March 26, 2010). "Obama Seals Arms Control Deal With Russia". The New York Times. Archived from the original on March 28, 2010. +
  818. +
  819. ^ Baker, Peter (December 22, 2010). "Senate Passes Arms Control Treaty With Russia, 71–26". The New York Times. Archived from the original on December 23, 2010. +
  820. +
  821. ^ McVeigh, Karen (December 6, 2011). "Gay rights must be criterion for US aid allocations, instructs Obama". The Guardian. London. Retrieved January 4, 2013. +
  822. +
  823. ^ Parsons, Christi (August 7, 2013). "Obama criticizes Russia's new anti-gay law in Leno interview". Los Angeles Times. Retrieved August 27, 2014. +
  824. +
  825. ^ Johnson, Luke (August 9, 2013). "Obama Opposes Olympic Boycott, Criticizes Russian Anti-Gay Law". The Huffington Post. Retrieved August 27, 2014. +
  826. +
  827. ^ "US election: The Russia factor: Officials say Moscow's interference is unprecedented. Has the Kremlin achieved its goal?". Financial Times. November 4, 2016. Archived from the original on February 7, 2017. +
  828. +
  829. ^ "Europeans View Obama's Exit With a Mix of Admiration and Regret". The New York Times. November 6, 2016. Archived from the original on November 7, 2016. +
  830. +
  831. ^ Wallace-Wells, Benjamin (November 2004). "The Great Black Hope: What's Riding on Barack Obama?". Washington Monthly. Archived from the original on May 13, 2008. Retrieved April 7, 2008. See also: Scott, Janny (December 28, 2007). "A Member of a New Generation, Obama Walks a Fine Line". International Herald Tribune. Archived from the original on January 17, 2008. Retrieved April 7, 2008. +
  832. +
  833. ^ Payne, Les (August 19, 2007). "In One Country, a Dual Audience". Newsday. New York. Archived from the original (paid archive) on September 15, 2008. Retrieved April 7, 2008. +
  834. +
  835. ^ Dorning, Mike (October 4, 2007). "Obama Reaches Across Decades to JFK". Chicago Tribune. Archived from the original (paid archive) on June 17, 2008. Retrieved April 7, 2008. See also: Harnden, Toby (October 15, 2007). "Barack Obama is JFK Heir, Says Kennedy Aide". The Daily Telegraph. London. Archived from the original on May 15, 2008. Retrieved April 7, 2008. +
  836. +
  837. ^ Holmes, Stephanie (November 30, 2008). "Obama: Oratory and originality". The Age. Melbourne. Archived from the original on December 18, 2008. Retrieved December 11, 2008. + +
  838. +
  839. ^ "ChangeDotGov's Channel". Archived from the original on February 20, 2010. Retrieved April 18, 2010 – via YouTube. +
  840. +
  841. ^ Saad, Lydia (January 24, 2009). "Obama Starts With 68% Job Approval". Gallup. Archived from the original on June 16, 2011. Retrieved June 19, 2011. +
  842. +
  843. ^ Jones, Jeffrey M. (January 22, 2009). What History Foretells for Obama’s First Job Approval Rating. Gallup, Inc.. +
  844. +
  845. ^ Jones, Jeffrey M. (November 20, 2009). Obama Job Approval Down to 49%. Gallup Inc.. +
  846. +
  847. ^ Jackson, David (April 15, 2011). "Obama hits low point in Gallup Poll—41%". USA Today. Retrieved June 19, 2011. +
  848. +
  849. ^ Terbush, Jon (December 9, 2010). "Approval By Numbers: How Obama Compares To Past Presidents". TPMDC. Archived from the original on July 4, 2011. Retrieved June 19, 2011. +
  850. +
  851. ^ a b c d e "Gallup Daily: Obama Job Approval". Gallup Polling. January 22, 2015. Retrieved March 23, 2015. +
  852. +
  853. ^ Oliphant, James (May 11, 2011). "Bin Laden bounce? New poll shows jump in Obama approval". Los Angeles Times. Retrieved June 7, 2011. +
  854. +
  855. ^ Balz, Dan; Cohen, John (June 6, 2011). "Obama loses bin Laden bounce; Romney on the move among GOP contenders". The Washington Post. Nash Holdings LLC. Retrieved June 7, 2011. +
  856. +
  857. ^ Jones, Jeffrey M. (October 21, 2011). "Obama Job Approval Average Slides to New Low in 11th Quarter". Gallup Inc. Archived from the original on January 15, 2024. +
  858. +
  859. '^ Saad, Lydia (September 27, 2012). Obama Approval, Vote Support Both Reach 50% or Better. Gallup. Inc. +
  860. +
  861. ^ "Presidential Job Approval Center". Gallup. Archived from the original on July 2, 2015. Retrieved June 23, 2015. +
  862. +
  863. ^ Topaz, Jonathan (October 15, 2014). Obama hits lowest approval. Politico. +
  864. +
  865. ^ Horsley, Scott (November 3, 2014). Obama's Low Approval Rating Casts Shadow Over Democratic Races. NPR. +
  866. +
  867. ^ Topaz, Jonathan (June 18, 2014). "NBC/WSJ poll: Obama low point". Politico. Retrieved July 25, 2023. +
  868. +
  869. ^ Preston, Mark (October 28, 2014). "Voters are angry". CNN. Retrieved July 25, 2023. +
  870. +
  871. ^ Jones, Jeffrey M. (February 6, 2015). "Obama Approval Ratings Still Historically Polarized". Gallup Inc. Retrieved July 31, 2023. +
  872. +
  873. ^ Dugan, Andrew; Newport, Frank (March 10, 2016). "Obama's Job Approval at Highest Level Since May 2013". Gallup Polling. Retrieved July 25, 2023. +
  874. +
  875. ^ "Barack Obama gets a midterm do-over to help boost Democrats". The Virginia Pilot. Associated Press. October 28, 2022. Retrieved July 24, 2023. +
  876. +
  877. ^ Saad, Lydia (June 19, 2017). "George W. Bush and Barack Obama Both Popular in Retirement". Gallup Inc. Retrieved July 31, 2023. +
  878. +
  879. ^ Jones, Jeffrey M. (February 15, 2018). "Obama's First Retrospective Job Approval Rating Is 63%". Gallup Inc. Retrieved July 31, 2023. +
  880. +
  881. ^ Jones, Jeffrey M. (July 17, 2023). "Retrospective Approval of JFK Rises to 90%; Trump at 46%". Gallup Inc. Retrieved July 31, 2023. +
  882. +
  883. ^ "World wants Obama as president: poll". ABC News. Reuters. September 9, 2008. +
  884. +
  885. ^ Wike, Richard; Poushter, Jacob; Zainulbhai, Hani (June 29, 2016). "As Obama Years Draw to Close, President and U.S. Seen Favorably in Europe and Asia". Global Attitudes & Trends. Pew Research Center. Retrieved February 23, 2017. +
  886. +
  887. ^ Wan, William; Clement, Scott (November 18, 2016). "Most of the world doesn't actually see America the way Trump said it did". The Washington Post. Retrieved February 8, 2021. +
  888. +
  889. ^ Freed, John C. (February 6, 2009). "Poll shows Obama atop list of most respected". The New York Times. Archived from the original on September 27, 2009. Retrieved January 22, 2012. +
  890. +
  891. ^ "Obama Most Popular Leader, Poll Finds". The New York Times. May 29, 2009. Archived from the original on June 1, 2009. Retrieved January 22, 2012. +
  892. +
  893. ^ "Obama remains a popular symbol of hope". France 24. June 2, 2009. Archived from the original on May 13, 2011. Retrieved January 22, 2012. +
  894. +
  895. ^ "The Nobel Peace Prize 2009". Nobel Foundation. Archived from the original on October 10, 2009. Retrieved October 9, 2009. +
  896. +
  897. ^ Philp, Catherine (October 10, 2009). "Barack Obama's peace prize starts a fight". The Times. ISSN 0140-0460. Retrieved December 15, 2021. +
  898. +
  899. ^ Otterman, Sharon (October 9, 2009). "World Reaction to a Nobel Surprise". The New York Times. Retrieved October 9, 2009. +
  900. +
  901. ^ "Obama Peace Prize win has Americans asking why?". Reuters. October 9, 2009. Retrieved October 9, 2009. +
  902. +
  903. ^ "Obama: Nobel Peace Prize 'a call to action'—Politics—White House". NBC News. October 9, 2009. Retrieved September 13, 2014. +
  904. +
  905. ^ "Obama's win unique among presidents". CNN. October 9, 2009. +
  906. +
  907. ^ Matt Spetalnick; Wojciech Moskwa (October 10, 2009). "Obama says Nobel Peace Prize is 'call to action'". Reuters. +
  908. +
  909. ^ "How Obama felt after Trump's inauguration". BBC News. Retrieved March 6, 2021. +
  910. +
  911. ^ Panetta, Grace. "Michelle Obama said attending Trump's inauguration as one of few people of color was 'a lot emotionally'". Business Insider. Retrieved March 6, 2021. +
  912. +
  913. ^ Kosinski, Michelle; Diaz, Daniella (May 27, 2016). "Peek inside Obama's post-presidential pad". CNN. Retrieved January 22, 2017. +
  914. +
  915. ^ "Former President Barack H. Obama Announced as Recipient of 2017 John F. Kennedy Profile in Courage Award". John F. Kennedy Presidential Library & Museum. March 2, 2017. Archived from the original on April 8, 2017. Retrieved April 8, 2017. +
  916. +
  917. ^ Shear, Michael D. (April 24, 2017). "Obama Steps Back into Public Life, Trying to Avoid One Word: Trump". The New York Times. Archived from the original on April 24, 2017. +
  918. +
  919. ^ Shelbourne, Mallory (September 10, 2017). "Former presidents fundraise for Irma disaster relief". The Hill. Retrieved September 11, 2017. +
  920. +
  921. ^ Hope, Leah (September 14, 2017). "Obama Foundation holds public meeting about presidential library project". WLS-TV. Retrieved November 17, 2020. +
  922. +
  923. ^ Dovere, Edward-Isaac (October 31, 2017). "Obama, opening his foundation's first summit, calls for fixing civic culture". Politico. +
  924. +
  925. ^ Neuman, Scott (May 22, 2018). "Obamas Sign Deal With Netflix, Form 'Higher Ground Productions'". NPR. Retrieved September 17, 2018. +
  926. +
  927. ^ Harris, Hunter (May 21, 2018). "The Obamas Will Produce Movies and Shows for Netflix". Vulture. Retrieved September 17, 2018. +
  928. +
  929. ^ Gonzalez, Sandra (January 13, 2020). "Barack and Michelle Obama's production company scores first Oscar nomination". CNN. Retrieved January 21, 2020. +
  930. +
  931. ^ Pitofsky, Marina (October 24, 2018). "Suspicious packages sent to Clintons, Obamas, CNN: What we know so far". USA Today. Archived from the original on October 24, 2018. +
  932. +
  933. ^ Lukpat, Alyssa (December 5, 2019). "Obamas reportedly buy Martha's Vineyard waterfront estate for $11.75 million". The Boston Globe. +
  934. +
  935. ^ "Barack Obama challenges 'woke' culture". BBC News. October 30, 2019. Retrieved October 4, 2021. +
  936. +
  937. ^ Rueb, Emily S.; Taylor, Derrick Bryson (October 31, 2019). "Obama on Call-Out Culture: 'That's Not Activism'". The New York Times. ISSN 0362-4331. Archived from the original on October 31, 2019. Retrieved October 4, 2021. +
  938. +
  939. ^ Jackson, John Fritze and David. "'Voters themselves must pick': Why Barack Obama isn't endorsing Joe Biden or anyone else for president". USA Today. Retrieved March 18, 2022. +
  940. +
  941. ^ Astor, Maggie; Glueck, Katie (April 14, 2020). "Barack Obama Endorses Joe Biden for President". The New York Times. Archived from the original on April 14, 2020. +
  942. +
  943. ^ "Obama endorses Joe Biden for president". BBC News. Retrieved March 6, 2021. +
  944. +
  945. ^ "DNC 2020: Obama blasts Trump's 'reality show' presidency". BBC News. August 20, 2020. Retrieved March 6, 2021. +
  946. +
  947. ^ Harris, Elizabeth A. (September 17, 2020). "Obama's Memoir 'A Promised Land' Coming in November". The New York Times. ISSN 0362-4331. Archived from the original on September 17, 2020. +
  948. +
  949. ^ Adichie, Chimamanda Ngozi (November 12, 2020). "Chimamanda Ngozi Adichie on Barack Obama's 'A Promised Land'". The New York Times. ISSN 0362-4331. Archived from the original on November 12, 2020. Retrieved November 17, 2020. +
  950. +
  951. ^ Carras, Christi (September 17, 2020). "Barack Obama's new memoir will arrive right after the presidential election". Los Angeles Times. Retrieved November 17, 2020. +
  952. +
  953. ^ Gabbatt, Adam (February 22, 2021). "Barack Obama and Bruce Springsteen team up for new podcast". The Guardian. Retrieved March 24, 2021. +
  954. +
  955. ^ Sisario, Ben (February 22, 2021). "Barack Obama and Bruce Springsteen: The Latest Podcast Duo". The New York Times. Archived from the original on December 28, 2021. Retrieved March 24, 2021. +
  956. +
  957. ^ Otterson, Joe (December 8, 2021). "'Upshaws' Co-Creator Regina Hicks Sets Netflix Overall Deal, to Develop Comedy Series With Obamas' Higher Ground". Variety. Retrieved December 9, 2021. +
  958. +
  959. ^ Perez, Lexy (March 5, 2022). "Barack Obama, Lin-Manuel Miranda Among 2022 Audie Awards Winners". Billboard. Retrieved March 6, 2022. +
  960. +
  961. ^ "Remarks by President Biden, Vice President Harris, and Former President Obama on the Affordable Care Act". The White House. April 5, 2022. Retrieved April 6, 2022. +
  962. +
  963. ^ Benson, Samuel (April 5, 2022). "Obama returns to White House for first time since leaving office". POLITICO. Retrieved April 6, 2022. +
  964. +
  965. ^ "Obama's back—for a day—in White House health bill push". AP NEWS. April 5, 2022. Retrieved April 6, 2022. +
  966. +
  967. ^ "Barack and Michelle Obama sign with Amazon after Spotify declines to renew audio deal". Fortune. Retrieved June 22, 2022. +
  968. +
  969. ^ Chan, J. Clara (June 21, 2022). "The Obamas' Higher Ground Leaves Spotify for Audible Multiyear Deal". The Hollywood Reporter. Retrieved June 22, 2022. +
  970. +
  971. ^ "Meet the artists who painted the Obama White House portraits". Washington Post. ISSN 0190-8286. Retrieved November 6, 2022. +
  972. +
  973. ^ Montgomery, Daniel (September 3, 2022). "2022 Creative Arts Emmy winners list in all categories [UPDATING LIVE]". GoldDerby. Retrieved September 4, 2022. +
  974. +
  975. ^ "5 lessons from Obama's national parks show on Netflix". Washington Post. ISSN 0190-8286. Retrieved May 26, 2022. +
  976. +
  977. ^ Jones, Mondaire (October 21, 2022). "Barack Obama is Wrong to Oppose Expanding the Supreme Court". The Nation. +
  978. +
  979. ^ "Here's why former US president Barack Obama is in Australia". March 27, 2023. Retrieved March 29, 2023. +
  980. +
  981. ^ Staszewska, Ewa (March 28, 2023). "Barack Obama set to reel in $1 million during Aussie speaking tour as he visits Sydney Opera House with wife Michelle". Retrieved March 29, 2023. +
  982. +
  983. ^ Vidler, Adam; Theocharous, Mikala (March 28, 2023). "Former US President Barack Obama could net $1 million for Australian speaking gigs". Retrieved March 29, 2023. +
  984. +
  985. ^ Mueller, Julia (October 9, 2023). "Obama condemns 'brazen' attacks against Israel". The Hill. Retrieved December 18, 2023. +
  986. +
  987. ^ Singh, Kanishka (October 23, 2023). "Obama warns some of Israel's actions in Gaza may backfire". Reuters. +
  988. +
  989. ^ Baker, Sam (February 14, 2024). "Read: Historians rank Trump as worst president". Axios. Retrieved May 21, 2024. +
  990. +
  991. ^ Stirland, Sarah Lai. "The Obama Campaign: A Great Campaign, or the Greatest?". Wired. Archived from the original on December 11, 2023. +
  992. +
  993. ^ "Barack Obama: A Master Class in Public Speaking [Video]". Forbes. +
  994. +
  995. ^ "3 Moments Where President Obama Earned the Title of Great Communicator". September 13, 2016. +
  996. +
  997. ^ Zelizer, Julian E. (2018). "Policy Revolution without a Political Transformation". In Zelizer, Julian (ed.). The Presidency of Barack Obama: a First Historical Assessment. Princeton University Press. pp. 1–10. ISBN 978-0-691-16028-3. +
  998. +
  999. ^ Kamarck, Elaine (April 6, 2018). "The fragile legacy of Barack Obama". Brookings. Archived from the original on April 6, 2018. Retrieved October 30, 2021. +
  1000. +
  1001. ^ W. Wise, David (April 30, 2019). "Obama's legacy is as a disappointingly conventional president". Retrieved November 4, 2022. +
  1002. +
  1003. ^ a b "Obama Legacy Will Be Recovery from Recession, Affordable Care Act". ABC News. January 20, 2017. Retrieved March 15, 2017. +
  1004. +
  1005. ^ Eibner, Christine; Nowak, Sarah (2018). The Effect of Eliminating the Individual Mandate Penalty and the Role of Behavioral Factors. Commonwealth Fund (Report). doi:10.26099/SWQZ-5G92. +
  1006. +
  1007. ^ Oberlander, Jonathan (June 1, 2010). "Long Time Coming: Why Health Reform Finally Passed". Health Affairs. 29 (6): 1112–1116. doi:10.1377/hlthaff.2010.0447. ISSN 0278-2715. PMID 20530339. +
  1008. +
  1009. ^ Blumenthal, David; Abrams, Melinda; Nuzum, Rachel (June 18, 2015). "The Affordable Care Act at 5 Years". New England Journal of Medicine. 372 (25): 2451–2458. doi:10.1056/NEJMhpr1503614. ISSN 0028-4793. PMID 25946142. S2CID 28486139. +
  1010. +
  1011. ^ Cohen, Alan B.; Colby, David C.; Wailoo, Keith A.; Zelizer, Julian E. (June 1, 2015). Medicare and Medicaid at 50: America's Entitlement Programs in the Age of Affordable Care. Oxford University Press. ISBN 978-0-19-023156-9. +
  1012. +
  1013. ^ Stolberg, Sheryl Gay; Pear, Robert (March 23, 2010). "Obama Signs Health Care Overhaul into Law". The New York Times. +
  1014. +
  1015. ^ Long, Heather (January 6, 2017). "Final tally: Obama created 11.3 million jobs". CNN. +
  1016. +
  1017. ^ "Barack Obama's Legacy: Dodd-Frank Wall Street reform". CBS News. Retrieved March 15, 2017. +
  1018. +
  1019. ^ Bowman, Quinn (October 28, 2009). "Obama Signs Measure to Widen Hate Crimes Law". PBS NewsHour. Retrieved November 8, 2022. +
  1020. +
  1021. ^ Crary, David (January 4, 2017). "LGBT activists view Obama as staunch champion of their cause". Associated Press. +
  1022. +
  1023. ^ Bumiller, Elisabeth (July 22, 2011). "Obama Ends 'Don't Ask, Don't Tell' Policy". The New York Times. Archived from the original on July 23, 2011. +
  1024. +
  1025. ^ Kennedy, Kennedy (June 30, 2016). "Pentagon Says Transgender Troops Can Now Serve Openly". The Two-Way. NPR. +
  1026. +
  1027. ^ Smith, Michael; Newport, Frank (January 9, 2017). "Americans Assess Progress Under Obama". The Gallup Organization. +
  1028. +
  1029. ^ Zenko, Micah (January 12, 2016). "Obama's Embrace of Drone Strikes Will Be a Lasting Legacy". The New York Times. Retrieved March 2, 2019. +
  1030. +
  1031. ^ Grandin, Greg (January 15, 2017). "Why Did the US Drop 26,171 Bombs on the World Last Year?". The Nation. Retrieved January 11, 2018. +
  1032. +
  1033. ^ Agerholm, Harriet (January 19, 2017). "Map shows where President Barack Obama dropped his 20,000 bombs". The Independent. Retrieved January 11, 2018. +
  1034. +
  1035. ^ Parsons, Christi; Hennigan, W. J. (January 13, 2017). "President Obama, who hoped to sow peace, instead led the nation in war". Los Angeles Times. +
  1036. +
  1037. ^ Gramlich, John (January 5, 2017). "Federal prison population fell during Obama's term, reversing recent trend". Pew Research Center. +
  1038. +
  1039. ^ Cone, Allen (January 18, 2017). "Obama leaving office at 60 percent approval rating". United Press International. Retrieved February 26, 2017. +
  1040. +
  1041. ^ Agiesta, Jennifer (January 18, 2017). "Obama approval hits 60 percent as end of term approaches". CNN. Retrieved February 26, 2017. +
  1042. +
  1043. ^ Rottinghaus, Brandon; Vaughn, Justin S. (February 13, 2015). "Measuring Obama against the great presidents". Brookings Institution. +
  1044. +
  1045. ^ Jones, Jeffrey M. (February 15, 2018). "Obama's First Retrospective Job Approval Rating Is 63%". Gallup. Retrieved March 26, 2022. +
  1046. +
  1047. ^ "The Obama Presidential Center". Barack Obama Foundation. Archived from the original on August 24, 2019. Retrieved October 10, 2023. +
  1048. +
  1049. ^ Williams, Sydney (November 17, 2020). "Former President Barack Obama's third book starts shipping today". NBC News. Retrieved September 22, 2021. +
  1050. +
  1051. ^ Ressner, Jeffrey; Smith, Ben (August 22, 2008). "Exclusive: Obama's Lost Law Review Article". Politico. Archived from the original on February 8, 2021. Retrieved February 20, 2021. +
  1052. +
  1053. ^ "Barack Hussein Obama Takes The Oath Of Office" on YouTube. January 20, 2009. +
  1054. +
+

Bibliography

+ +

Further reading

+
+ +
+

External links

+ +

Official

+ +

Other

+ + + + + + + + + + +
+
+ +
+
+ +
+ +
+
+
+
    +
  • +
  • +
+
+ + + + diff --git a/tests/paper.pdf b/tests/stub_data/paper.pdf similarity index 100% rename from tests/paper.pdf rename to tests/stub_data/paper.pdf diff --git a/tests/stub_data/stub_manifest.csv b/tests/stub_data/stub_manifest.csv new file mode 100644 index 00000000..9da53895 --- /dev/null +++ b/tests/stub_data/stub_manifest.csv @@ -0,0 +1,4 @@ +file_location,doi,title +"paper.pdf","10.1021/acs.jctc.2c01235","A Perspective on Explanations of Molecular Prediction Models" +"bates.txt",,"Frederick Bates (Wikipedia article)" +"flag_day.html",,"National Flag Day of Canada (Wikipedia article)" diff --git a/tests/test_agents.py b/tests/test_agents.py new file mode 100644 index 00000000..f1ffe2e1 --- /dev/null +++ b/tests/test_agents.py @@ -0,0 +1,401 @@ +from __future__ import annotations + +import itertools +import json +import re +from pathlib import Path +from typing import Any, cast +from unittest.mock import patch + +import pytest +from aviary.tools import ToolsAdapter, ToolSelector +from ldp.agent import SimpleAgent +from pydantic import ValidationError +from pytest_subtests import SubTests + +from paperqa.agents import agent_query +from paperqa.agents.env import settings_to_tools +from paperqa.agents.models import AgentStatus, AnswerResponse, QueryRequest +from paperqa.agents.search import get_directory_index +from paperqa.agents.tools import ( + EnvironmentState, + GatherEvidence, + GenerateAnswer, + PaperSearch, +) +from paperqa.docs import Docs +from paperqa.settings import AgentSettings, Settings +from paperqa.types import Answer, Context, Doc, Text +from paperqa.utils import get_year, md5sum + + +@pytest.mark.asyncio +async def test_get_directory_index(agent_test_settings: Settings) -> None: + index = await get_directory_index(settings=agent_test_settings) + assert index.fields == [ + "file_location", + "body", + "title", + "year", + ], "Incorrect fields in index" + # paper.pdf + flag_day.html + bates.txt + obama.txt + assert len(await index.index_files) == 4, "Incorrect number of index files" + results = await index.query(query="who is Frederick Bates?") + paper_dir = cast(Path, agent_test_settings.paper_directory) + assert results[0].docs.keys() == {md5sum((paper_dir / "bates.txt").absolute())} + + +@pytest.mark.asyncio +async def test_get_directory_index_w_manifest( + agent_test_settings: Settings, reset_log_levels, caplog # noqa: ARG001 +) -> None: + agent_test_settings.manifest_file = "stub_manifest.csv" + index = await get_directory_index(settings=agent_test_settings) + assert index.fields == [ + "file_location", + "body", + "title", + "year", + ], "Incorrect fields in index" + # paper.pdf + flag_day.html + bates.txt + obama.txt + assert len(await index.index_files) == 4, "Incorrect number of index files" + results = await index.query(query="who is Frederick Bates?") + top_result = next(iter(results[0].docs.values())) + paper_dir = cast(Path, agent_test_settings.paper_directory) + assert top_result.dockey == md5sum((paper_dir / "bates.txt").absolute()) + # note: this title comes from the manifest, so we know it worked + assert top_result.title == "Frederick Bates (Wikipedia article)" + + +@pytest.mark.flaky(reruns=2, only_rerun=["AssertionError", "httpx.RemoteProtocolError"]) +@pytest.mark.parametrize("agent_type", ["fake", ToolSelector, SimpleAgent]) +@pytest.mark.asyncio +async def test_agent_types( + agent_test_settings: Settings, agent_type: str | type +) -> None: + question = "How can you use XAI for chemical property prediction?" + + # make sure agent_llm is different from default, so we can correctly track tokens + # for agent + agent_test_settings.agent.agent_llm = "gpt-4o-2024-08-06" + agent_test_settings.llm = "gpt-4o-mini" + agent_test_settings.summary_llm = "gpt-4o-mini" + agent_test_settings.agent.agent_prompt += ( + "\n\n Call each tool once in appropriate order and " + " accept the answer for now, as we're in debug mode." + ) + request = QueryRequest(query=question, settings=agent_test_settings) + response = await agent_query(request, agent_type=agent_type) + assert response.answer.answer, "Answer not generated" + assert response.answer.answer != "I cannot answer", "Answer not generated" + assert response.answer.context, "No contexts were found" + assert response.answer.question == question + agent_llm = request.settings.agent.agent_llm + # TODO: once LDP can track tokens, we can remove this check + if agent_type not in {"fake", SimpleAgent}: + print(response.answer.token_counts) + assert ( + response.answer.token_counts[agent_llm][0] > 1000 + ), "Expected many prompt tokens" + assert ( + response.answer.token_counts[agent_llm][1] > 50 + ), "Expected many completion tokens" + assert response.answer.cost > 0, "Expected nonzero cost" + + +@pytest.mark.asyncio +async def test_timeout(agent_test_settings: Settings) -> None: + agent_test_settings.prompts.pre = None + agent_test_settings.agent.timeout = 0.001 + agent_test_settings.llm = "gpt-4o-mini" + agent_test_settings.agent.tool_names = {"gen_answer"} + response = await agent_query( + QueryRequest( + query="Are COVID-19 vaccines effective?", settings=agent_test_settings + ) + ) + # ensure that GenerateAnswerTool was called + assert response.status == AgentStatus.TIMEOUT, "Agent did not timeout" + assert "I cannot answer" in response.answer.answer + + +@pytest.mark.asyncio +async def test_propagate_options(agent_test_settings: Settings) -> None: + llm_name = "gpt-4o-mini" + default_llm_names = { + cls.model_fields[name].default + for name, cls in itertools.product(("llm", "summary_llm"), (Settings,)) + } + assert ( + llm_name not in default_llm_names + ), f"Assertions require not matching a default LLM name in {default_llm_names}." + + agent_test_settings.llm = llm_name + agent_test_settings.answer.answer_max_sources = 5 + agent_test_settings.answer.evidence_k = 6 + agent_test_settings.answer.answer_length = "400 words" + agent_test_settings.prompts.pre = None + agent_test_settings.prompts.system = "End all responses with ###" + agent_test_settings.answer.evidence_skip_summary = True + agent_test_settings.answer.evidence_detailed_citations = False + + query = QueryRequest( + query="What is is a self-explanatory model?", settings=agent_test_settings + ) + response = await agent_query(query, agent_type="fake") + assert response.status == AgentStatus.SUCCESS, "Agent did not succeed" + result = response.answer + assert len(result.answer) > 200, "Answer did not return any results" + assert "###" in result.answer, "Answer did not propagate system prompt" + assert ( + len(result.contexts[0].context) == agent_test_settings.parsing.chunk_size + ), "Summary was not skipped" + + +@pytest.mark.asyncio +async def test_gather_evidence_rejects_empty_docs() -> None: + # Patch GenerateAnswerTool._arun so that if this tool is chosen first, we + # don't give a 'cannot answer' response. A 'cannot answer' response can + # lead to an unsure status, which will break this test's assertions. Since + # this test is about a GatherEvidenceTool edge case, defeating + # GenerateAnswerTool is fine + with patch.object( + GenerateAnswer, "gen_answer", return_value="Failed to answer question." + ): + settings = Settings() + settings.agent.tool_names = {"gather_evidence", "gen_answer"} + response = await agent_query( + query=QueryRequest( + query="Are COVID-19 vaccines effective?", settings=settings + ), + docs=Docs(), + ) + assert response.status == AgentStatus.FAIL, "Agent should have registered a failure" + + +@pytest.mark.flaky(reruns=3, only_rerun=["AssertionError"]) +@pytest.mark.asyncio +async def test_agent_sharing_state( + agent_test_settings: Settings, subtests: SubTests +) -> None: + agent_test_settings.agent.search_count = 3 # Keep low for speed + agent_test_settings.answer.evidence_k = 2 + agent_test_settings.answer.answer_max_sources = 1 + llm_model = agent_test_settings.get_llm() + summary_llm_model = agent_test_settings.get_summary_llm() + embedding_model = agent_test_settings.get_embedding_model() + + answer = Answer(question="What is is a self-explanatory model?") + docs = Docs() + query = QueryRequest(query=answer.question, settings=agent_test_settings) + env_state = EnvironmentState(docs=docs, answer=answer) + + with subtests.test(msg=PaperSearch.__name__): + search_tool = PaperSearch( + settings=agent_test_settings, embedding_model=embedding_model + ) + await search_tool.paper_search( + "XAI self explanatory model", min_year=None, max_year=None, state=env_state + ) + assert env_state.docs.docs, "Search did not save any papers" + assert all( + (isinstance(d, Doc) or issubclass(d, Doc)) # type: ignore[unreachable] + for d in env_state.docs.docs.values() + ), "Document type or DOI propagation failure" + + with subtests.test(msg=GatherEvidence.__name__): + assert not answer.contexts, "No contexts is required for a later assertion" + + gather_evidence_tool = GatherEvidence( + settings=agent_test_settings, + summary_llm_model=summary_llm_model, + embedding_model=embedding_model, + ) + await gather_evidence_tool.gather_evidence(answer.question, state=env_state) + assert answer.contexts, "Evidence did not return any results" + + with subtests.test(msg=f"{GenerateAnswer.__name__} working"): + generate_answer_tool = GenerateAnswer( + settings=agent_test_settings, + llm_model=llm_model, + summary_llm_model=summary_llm_model, + embedding_model=embedding_model, + ) + result = await generate_answer_tool.gen_answer(answer.question, state=env_state) + assert re.search( + pattern=EnvironmentState.STATUS_SEARCH_REGEX_PATTERN, string=result + ) + assert len(answer.answer) > 200, "Answer did not return any results" + assert ( + GenerateAnswer.extract_answer_from_message(result) == answer.answer + ), "Failed to regex extract answer from result" + assert ( + len(answer.used_contexts) <= query.settings.answer.answer_max_sources + ), "Answer has more sources than expected" + + +def test_tool_schema(agent_test_settings: Settings) -> None: + """Check the tool schema passed to LLM providers.""" + tools = settings_to_tools(agent_test_settings) + assert ToolsAdapter.dump_python(tools, exclude_none=True) == [ + { + "type": "function", + "info": { + "name": "gather_evidence", + "description": ( + "Gather evidence from previous papers given a specific question" + " to increase evidence and relevant paper counts.\n\nA valuable" + " time to invoke this tool is right after another tool" + " increases paper count.\nFeel free to invoke this tool in" + " parallel with other tools, but do not call this tool in" + " parallel with itself.\nOnly invoke this tool when the paper" + " count is above zero, or this tool will be useless." + ), + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Specific question to gather evidence for.", + "title": "Question", + } + }, + "required": ["question"], + }, + }, + }, + { + "type": "function", + "info": { + "name": "paper_search", + "description": ( + "Search for papers to increase the paper count.\n\nRepeat" + " previous calls with the same query and years to continue a" + " search.\nThis tool can be called concurrently.\nThis tool" + " introduces novel papers, so invoke this tool when just" + " beginning or when unsatisfied with the current evidence." + ), + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": ( + "A search query, which can be a specific phrase," + " complete sentence, or general keywords, e.g." + " 'machine learning for immunology'. Also can be" + " given search operators." + ), + "title": "Query", + }, + "min_year": { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "description": ( + "Filter for minimum publication year, or None for" + " no minimum year. The current year is" + f" {get_year()}." + ), + "title": "Min Year", + }, + "max_year": { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "description": ( + "Filter for maximum publication year, or None for" + " no maximum year. The current year is" + f" {get_year()}." + ), + "title": "Max Year", + }, + }, + "required": ["query", "min_year", "max_year"], + }, + }, + }, + { + "type": "function", + "info": { + "name": "gen_answer", + "description": ( + "Ask a model to propose an answer using current" + " evidence.\n\nThe tool may fail, indicating that better or" + " different evidence should be found.\nAim for at least five" + " pieces of evidence from multiple sources before invoking this" + " tool.\nFeel free to invoke this tool in parallel with other" + " tools, but do not call this tool in parallel with itself." + ), + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Question to be answered.", + "title": "Question", + } + }, + "required": ["question"], + }, + }, + }, + ] + + +def test_query_request_docs_name_serialized() -> None: + """Test that the query request has a docs_name property.""" + request = QueryRequest(query="Are COVID-19 vaccines effective?") + request_data = json.loads(request.model_dump_json()) + assert "docs_name" in request_data + assert request_data["docs_name"] is None + request.set_docs_name("my_doc") + request_data = json.loads(request.model_dump_json()) + assert request_data["docs_name"] == "my_doc" + + +def test_answers_are_striped() -> None: + """Test that answers are striped.""" + answer = Answer( + question="What is the meaning of life?", + contexts=[ + Context( + context="bla", + text=Text( + name="text", + text="The meaning of life is 42.", + embedding=[43.3, 34.2], + doc=Doc( + docname="foo", + citation="bar", + dockey="baz", + embedding=[43.1, 65.2], + ), + ), + score=3, + ) + ], + ) + response = AnswerResponse(answer=answer, bibtex={}, status="success") + + assert response.answer.contexts[0].text.embedding is None + assert response.answer.contexts[0].text.text == "" # type: ignore[unreachable,unused-ignore] + assert response.answer.contexts[0].text.doc is not None + assert response.answer.contexts[0].text.doc.embedding is None + # make sure it serializes + response.model_dump_json() + + +@pytest.mark.parametrize( + ("kwargs", "result"), + [ + ({}, None), + ({"tool_names": {GenerateAnswer.TOOL_FN_NAME}}, None), + ({"tool_names": set()}, ValidationError), + ({"tool_names": {PaperSearch.TOOL_FN_NAME}}, ValidationError), + ], +) +def test_agent_prompt_collection_validations( + kwargs: dict[str, Any], result: type[Exception] | None +) -> None: + if result is None: + AgentSettings(**kwargs) + else: + with pytest.raises(result): + AgentSettings(**kwargs) diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..c089a725 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,67 @@ +import io +import os +import sys +from pathlib import Path + +import pytest + +from paperqa.settings import Settings +from paperqa.utils import pqa_directory + +try: + from paperqa.agents import ask, build_index, main, search_query + from paperqa.agents.models import AnswerResponse +except ImportError: + pytest.skip("agents module is not installed", allow_module_level=True) + + +def test_can_modify_settings() -> None: + old_argv = sys.argv + old_stdout = sys.stdout + captured_output = io.StringIO() + try: + sys.argv = "paperqa -s debug --llm=my-model-foo save unit_test".split() + main() + + sys.stdout = captured_output + assert Settings.from_name("unit_test").llm == "my-model-foo" + + sys.argv = "paperqa -s unit_test view".split() + main() + + output = captured_output.getvalue().strip() + assert "my-model-foo" in output + finally: + sys.argv = old_argv + sys.stdout = old_stdout + os.unlink(pqa_directory("settings") / "unit_test.json") + + +def test_cli_ask(agent_index_dir: Path, stub_data_dir: Path) -> None: + settings = Settings.from_name("debug") + settings.index_directory = agent_index_dir + settings.paper_directory = stub_data_dir + response = ask( + "How can you use XAI for chemical property prediction?", settings=settings + ) + assert response.answer.formatted_answer + + search_result = search_query( + " ".join(response.answer.formatted_answer.split()[:5]), + "answers", + settings, + ) + found_answer = search_result[0][0] + assert isinstance(found_answer, AnswerResponse) + assert found_answer.model_dump_json() == response.model_dump_json() + + +def test_cli_can_build_and_search_index( + agent_index_dir: Path, stub_data_dir: Path +) -> None: + settings = Settings.from_name("debug") + settings.index_directory = agent_index_dir + index_name = "test" + build_index(index_name, stub_data_dir, settings) + search_result = search_query("XAI", index_name, settings) + assert search_result diff --git a/tests/test_clients.py b/tests/test_clients.py index b75bb36f..6d814c72 100644 --- a/tests/test_clients.py +++ b/tests/test_clients.py @@ -1,24 +1,27 @@ from __future__ import annotations import logging -from typing import Any, Collection, Sequence, cast +from collections.abc import Collection, Sequence +from typing import Any, cast import aiohttp import pytest import paperqa from paperqa.clients import ( + ALL_CLIENTS, CrossrefProvider, DocMetadataClient, SemanticScholarProvider, ) from paperqa.clients.client_models import MetadataPostProcessor, MetadataProvider from paperqa.clients.journal_quality import JournalQualityPostProcessor +from paperqa.clients.retractions import RetrationDataPostProcessor -@pytest.mark.vcr() +@pytest.mark.vcr @pytest.mark.parametrize( - ("paper_attributes"), + "paper_attributes", [ { "title": ( @@ -32,15 +35,18 @@ "journal": "Journal of Applied Physics", "authors": ["Michael D. Skarlinski", "David J. Quesnel"], "formatted_citation": ( - "Michael D. Skarlinski and David J. Quesnel. Effect of native " - "oxide layers on copper thin-film tensile properties: a reactive" - " molecular dynamics study. Journal of Applied Physics, 118:235306, " - "Dec 2015. URL: https://doi.org/10.1063/1.4938384, doi:10.1063/1.4938384. " - "This article has 8 citations and is from a peer-reviewed journal." + "Michael D. Skarlinski and David J. Quesnel. Effect of native oxide" + " layers on copper thin-film tensile properties: a reactive molecular" + " dynamics study. Journal of Applied Physics, 118:235306, Dec 2015." + " URL: https://doi.org/10.1063/1.4938384, doi:10.1063/1.4938384. This" + " article has 8 citations and is from a peer-reviewed journal." ), + "is_oa": False, }, { - "title": "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", + "title": ( + "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research" + ), "source": ["semantic_scholar"], "key": "lala2023paperqaretrievalaugmentedgenerative", "doi": "10.48550/arxiv.2312.07559", @@ -55,11 +61,13 @@ "Andrew D. White", ], "formatted_citation": ( - "Jakub L'ala, Odhran O'Donoghue, Aleksandar Shtedritski, Sam Cox, Samuel G. Rodriques," - " and Andrew D. White. Paperqa: retrieval-augmented generative agent for scientific " - "research. ArXiv, Dec 2023. URL: https://doi.org/10.48550/arxiv.2312.07559, " - "doi:10.48550/arxiv.2312.07559. This article has 21 citations." + "Jakub L'ala, Odhran O'Donoghue, Aleksandar Shtedritski, Sam Cox," + " Samuel G. Rodriques, and Andrew D. White. Paperqa:" + " retrieval-augmented generative agent for scientific research. ArXiv," + " Dec 2023. URL: https://doi.org/10.48550/arxiv.2312.07559," + " doi:10.48550/arxiv.2312.07559. This article has 23 citations." ), + "is_oa": None, }, { "title": "Augmenting large language models with chemistry tools", @@ -77,34 +85,53 @@ "Philippe Schwaller", ], "formatted_citation": ( - "Andres M. Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D. White, and " - "Philippe Schwaller. Augmenting large language models with chemistry tools. Nature " - "Machine Intelligence, 6:525-535, May 2024. URL: https://doi.org/10.1038/s42256-024-00832-8, " - "doi:10.1038/s42256-024-00832-8. This article has 187 citations and is from a " - "domain leading peer-reviewed journal." + "Andres M. Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D." + " White, and Philippe Schwaller. Augmenting large language models with" + " chemistry tools. Nature Machine Intelligence, 6:525-535, May 2024." + " URL: https://doi.org/10.1038/s42256-024-00832-8," + " doi:10.1038/s42256-024-00832-8. This article has 191 citations and is" + " from a domain leading peer-reviewed journal." ), + "is_oa": True, }, ], ) -@pytest.mark.asyncio() -async def test_title_search(paper_attributes: dict[str, str]): +@pytest.mark.asyncio +async def test_title_search(paper_attributes: dict[str, str]) -> None: async with aiohttp.ClientSession() as session: - client = DocMetadataClient(session) + client_list = list(ALL_CLIENTS) + client_list.remove(RetrationDataPostProcessor) + client = DocMetadataClient( + session, + clients=cast( + Collection[ + type[MetadataPostProcessor[Any]] | type[MetadataProvider[Any]] + ], + client_list, + ), + ) details = await client.query(title=paper_attributes["title"]) assert set(details.other["client_source"]) == set( # type: ignore[union-attr] paper_attributes["source"] ), "Should have the correct source" for key, value in paper_attributes.items(): - if key != "source": + if key not in {"is_oa", "source"}: assert getattr(details, key) == value, f"Should have the correct {key}" + elif key == "is_oa": + assert ( + details.other.get("is_oa") == value # type: ignore[union-attr] + ), "Open access data should match" -@pytest.mark.vcr() +@pytest.mark.vcr @pytest.mark.parametrize( - ("paper_attributes"), + "paper_attributes", [ { - "title": "High-throughput screening of human genetic variants by pooled prime editing", + "title": ( + "High-throughput screening of human genetic variants by pooled prime" + " editing" + ), "source": ["semantic_scholar", "crossref"], "key": "herger2024highthroughputscreeningof", "doi": "10.1101/2024.04.01.587366", @@ -119,16 +146,19 @@ async def test_title_search(paper_attributes: dict[str, str]): "Gregory M. Findlay", ], "formatted_citation": ( - "Michael Herger, Christina M. Kajba, Megan Buckley, Ana Cunha, Molly Strom, " - "and Gregory M. Findlay. High-throughput screening of human genetic variants " - "by pooled prime editing. bioRxiv, Apr 2024. URL: https://doi.org/10.1101/2024.04.01.587366, " - "doi:10.1101/2024.04.01.587366. This article has 1 citations." + "Michael Herger, Christina M. Kajba, Megan Buckley, Ana Cunha, Molly" + " Strom, and Gregory M. Findlay. High-throughput screening of human" + " genetic variants by pooled prime editing. bioRxiv, Apr 2024. URL:" + " https://doi.org/10.1101/2024.04.01.587366," + " doi:10.1101/2024.04.01.587366. This article has 1 citations." ), + "is_oa": True, }, { "title": ( - "An essential role of active site arginine residue in iodide binding and histidine residue " - "in electron transfer for iodide oxidation by horseradish peroxidase" + "An essential role of active site arginine residue in iodide binding" + " and histidine residue in electron transfer for iodide oxidation by" + " horseradish peroxidase" ), "source": ["semantic_scholar", "crossref"], "key": "adak2001anessentialrole", @@ -142,12 +172,15 @@ async def test_title_search(paper_attributes: dict[str, str]): "Ranajit K. Banerjee", ], "formatted_citation": ( - "Subrata Adak, Debashis Bandyopadhyay, Uday Bandyopadhyay, and Ranajit K. Banerjee. " - "An essential role of active site arginine residue in iodide binding and histidine residue " - "in electron transfer for iodide oxidation by horseradish peroxidase. Molecular and Cellular " - "Biochemistry, 218:1-11, Feb 2001. URL: https://doi.org/10.1023/a:1007154515475, " - "doi:10.1023/a:1007154515475. This article has 7 citations and is from a peer-reviewed journal." + "Subrata Adak, Debashis Bandyopadhyay, Uday Bandyopadhyay, and Ranajit" + " K. Banerjee. An essential role of active site arginine residue in" + " iodide binding and histidine residue in electron transfer for iodide" + " oxidation by horseradish peroxidase. Molecular and Cellular" + " Biochemistry, 218:1-11, Feb 2001. URL:" + " https://doi.org/10.1023/a:1007154515475, doi:10.1023/a:1007154515475." + " This article has 7 citations and is from a peer-reviewed journal." ), + "is_oa": False, }, { "title": "Convalescent-anti-sars-cov-2-plasma/immune-globulin", @@ -158,29 +191,47 @@ async def test_title_search(paper_attributes: dict[str, str]): "journal": "Reactions Weekly", "authors": [], "formatted_citation": ( - "Unknown author(s). Convalescent-anti-sars-cov-2-plasma/immune-globulin. Reactions Weekly, " - "1962:145-145, Jun 2023. URL: https://doi.org/10.1007/s40278-023-41815-2, " - "doi:10.1007/s40278-023-41815-2. This article has 0 citations and is from a peer-reviewed journal." + "Unknown author(s)." + " Convalescent-anti-sars-cov-2-plasma/immune-globulin. Reactions" + " Weekly, 1962:145-145, Jun 2023. URL:" + " https://doi.org/10.1007/s40278-023-41815-2," + " doi:10.1007/s40278-023-41815-2. This article has 0 citations and is" + " from a peer-reviewed journal." ), + "is_oa": False, }, ], ) -@pytest.mark.asyncio() -async def test_doi_search(paper_attributes: dict[str, str]): +@pytest.mark.asyncio +async def test_doi_search(paper_attributes: dict[str, str]) -> None: async with aiohttp.ClientSession() as session: - client = DocMetadataClient(session) + client_list = list(ALL_CLIENTS) + client_list.remove(RetrationDataPostProcessor) + client = DocMetadataClient( + session, + clients=cast( + Collection[ + type[MetadataPostProcessor[Any]] | type[MetadataProvider[Any]] + ], + client_list, + ), + ) details = await client.query(doi=paper_attributes["doi"]) assert set(details.other["client_source"]) == set( # type: ignore[union-attr] paper_attributes["source"] ), "Should have the correct source" for key, value in paper_attributes.items(): - if key != "source": + if key not in {"is_oa", "source"}: assert getattr(details, key) == value, f"Should have the correct {key}" + elif key == "is_oa": + assert ( + details.other.get("is_oa") == value # type: ignore[union-attr] + ), "Open access data should match" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_bulk_doi_search(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_bulk_doi_search() -> None: dois = [ "10.1063/1.4938384", "10.48550/arxiv.2312.07559", @@ -196,17 +247,21 @@ async def test_bulk_doi_search(): assert all(d for d in details), "All results should be non-None" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_bulk_title_search(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_bulk_title_search() -> None: titles = [ - "Effect of native oxide layers on copper thin-film tensile properties: A reactive molecular dynamics study", + ( + "Effect of native oxide layers on copper thin-film tensile properties: A" + " reactive molecular dynamics study" + ), "PaperQA: Retrieval-Augmented Generative Agent for Scientific Research", "Augmenting large language models with chemistry tools", "High-throughput screening of human genetic variants by pooled prime editing", ( - "An essential role of active site arginine residue in iodide binding and histidine residue " - "in electron transfer for iodide oxidation by horseradish peroxidase" + "An essential role of active site arginine residue in iodide binding and" + " histidine residue in electron transfer for iodide oxidation by" + " horseradish peroxidase" ), "Convalescent-anti-sars-cov-2-plasma/immune-globulin", ] @@ -217,31 +272,34 @@ async def test_bulk_title_search(): assert all(d for d in details), "All results should be non-None" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_bad_titles(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_bad_titles() -> None: async with aiohttp.ClientSession() as session: client = DocMetadataClient(session) details = await client.query(title="askldjrq3rjaw938h") assert not details, "Should return None for bad title" details = await client.query( - title="Effect of native oxide layers on copper thin-film tensile properties: A study" + title=( + "Effect of native oxide layers on copper thin-film tensile properties:" + " A study" + ) ) assert details, "Should find a similar title" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_bad_dois(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_bad_dois() -> None: async with aiohttp.ClientSession() as session: client = DocMetadataClient(session) details = await client.query(title="abs12032jsdafn") assert not details, "Should return None for bad doi" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_minimal_fields_filtering(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_minimal_fields_filtering() -> None: async with aiohttp.ClientSession() as session: client = DocMetadataClient(session) details = await client.query( @@ -253,8 +311,9 @@ async def test_minimal_fields_filtering(): assert not details.authors, "Authors should not be populated" # type: ignore[union-attr] assert details.citation == ( # type: ignore[union-attr] "Unknown author(s). Augmenting large language models with chemistry tools." - " Unknown journal, Unknown year. URL: https://doi.org/10.1038/s42256-024-00832-8, " - "doi:10.1038/s42256-024-00832-8." + " Unknown journal, Unknown year. URL:" + " https://doi.org/10.1038/s42256-024-00832-8," + " doi:10.1038/s42256-024-00832-8." ), "Citation should be populated" assert set(details.other["client_source"]) == { # type: ignore[union-attr] "semantic_scholar", @@ -263,9 +322,9 @@ async def test_minimal_fields_filtering(): assert not details.source_quality, "No source quality data should exist" # type: ignore[union-attr] -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_s2_only_fields_filtering(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_s2_only_fields_filtering() -> None: async with aiohttp.ClientSession() as session: # now get with authors just from one source s2_client = DocMetadataClient(session, clients=[SemanticScholarProvider]) @@ -276,17 +335,18 @@ async def test_s2_only_fields_filtering(): assert s2_details.authors, "Authors should be populated" # type: ignore[union-attr] assert set(s2_details.other["client_source"]) == {"semantic_scholar"} # type: ignore[union-attr] assert s2_details.citation == ( # type: ignore[union-attr] - "Andrés M Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D. White, " - "and P. Schwaller. Augmenting large language models with chemistry tools. " - "Unknown journal, Unknown year. URL: https://doi.org/10.1038/s42256-024-00832-8, " - "doi:10.1038/s42256-024-00832-8." + "Andrés M Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D." + " White, and P. Schwaller. Augmenting large language models with chemistry" + " tools. Unknown journal, Unknown year. URL:" + " https://doi.org/10.1038/s42256-024-00832-8," + " doi:10.1038/s42256-024-00832-8." ), "Citation should be populated" assert not s2_details.source_quality, "No source quality data should exist" # type: ignore[union-attr] -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_crossref_journalquality_fields_filtering(): +@pytest.mark.vcr(record_mode="new_episodes") +@pytest.mark.asyncio +async def test_crossref_journalquality_fields_filtering() -> None: async with aiohttp.ClientSession() as session: crossref_client = DocMetadataClient( session, @@ -301,21 +361,43 @@ async def test_crossref_journalquality_fields_filtering(): title="Augmenting large language models with chemistry tools", fields=["title", "doi", "authors", "journal"], ) - assert set(crossref_details.other["client_source"]) == { # type: ignore[union-attr] + assert crossref_details, "Failed to query crossref" + assert set(crossref_details.other["client_source"]) == { "crossref" }, "Should be from only crossref" - assert crossref_details.source_quality == 2, "Should have source quality data" # type: ignore[union-attr] - assert crossref_details.citation == ( # type: ignore[union-attr] - "Andres M. Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D. White, " - "and Philippe Schwaller. Augmenting large language models with chemistry tools. " - "Nature Machine Intelligence, Unknown year. URL: https://doi.org/10.1038/s42256-024-00832-8, " - "doi:10.1038/s42256-024-00832-8." + assert crossref_details.source_quality == 2, "Should have source quality data" + assert ( + crossref_details.citation + == "Andres M. Bran, Sam Cox, Oliver Schilter, Carlo Baldassari, Andrew D." + " White, and Philippe Schwaller. Augmenting large language models with" + " chemistry tools. Nature Machine Intelligence, Unknown year. URL:" + " https://doi.org/10.1038/s42256-024-00832-8," + " doi:10.1038/s42256-024-00832-8." ), "Citation should be populated" + async with aiohttp.ClientSession() as session: + crossref_client = DocMetadataClient( + session, + clients=cast( + Collection[ + type[MetadataPostProcessor[Any]] | type[MetadataProvider[Any]] + ], + [CrossrefProvider, JournalQualityPostProcessor], + ), + ) + nejm_crossref_details = await crossref_client.query( + title=( + "Beta-Blocker Interruption or Continuation after Myocardial Infarction" # codespell:ignore + ), + fields=["title", "doi", "authors", "journal"], + ) + + assert nejm_crossref_details.source_quality == 3, "Should have source quality data" # type: ignore[union-attr] -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_author_matching(): + +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_author_matching() -> None: async with aiohttp.ClientSession() as session: crossref_client = DocMetadataClient(session, clients=[CrossrefProvider]) s2_client = DocMetadataClient(session, clients=[SemanticScholarProvider]) @@ -342,9 +424,9 @@ async def test_author_matching(): assert s2_details_w_author, "Should return results for good author" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_odd_client_requests(): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_odd_client_requests() -> None: # try querying using an authors match, but not requesting authors back async with aiohttp.ClientSession() as session: client = DocMetadataClient(session) @@ -390,8 +472,8 @@ async def test_odd_client_requests(): ), "Should return title even though we asked for some bad fields" -@pytest.mark.asyncio() -async def test_ensure_robust_to_timeouts(monkeypatch): +@pytest.mark.asyncio +async def test_ensure_robust_to_timeouts(monkeypatch) -> None: # 0.15 should be short enough to not get a response in time. monkeypatch.setattr(paperqa.clients.crossref, "CROSSREF_API_REQUEST_TIMEOUT", 0.05) monkeypatch.setattr( @@ -407,17 +489,17 @@ async def test_ensure_robust_to_timeouts(monkeypatch): assert details is None, "Should return None for timeout" -@pytest.mark.asyncio() -async def test_bad_init(): +@pytest.mark.asyncio +async def test_bad_init() -> None: with pytest.raises( ValueError, match="At least one MetadataProvider must be provided." ): client = DocMetadataClient(clients=[]) # noqa: F841 -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_ensure_sequential_run(caplog): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_ensure_sequential_run(caplog, reset_log_levels) -> None: # noqa: ARG001 caplog.set_level(logging.DEBUG) # were using a DOI that is NOT in crossref, but running the crossref client first # we will ensure that both are run sequentially @@ -450,9 +532,11 @@ async def test_ensure_sequential_run(caplog): ), "Crossref should run first" -@pytest.mark.vcr() -@pytest.mark.asyncio() -async def test_ensure_sequential_run_early_stop(caplog): +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_ensure_sequential_run_early_stop( + caplog, reset_log_levels # noqa: ARG001 +) -> None: caplog.set_level(logging.DEBUG) # now we should stop after hitting s2 async with aiohttp.ClientSession() as session: @@ -487,3 +571,25 @@ async def test_ensure_sequential_run_early_stop(caplog): record_indices["semantic_scholar"] != -1 ), "Semantic Scholar should be found" assert record_indices["early_stop"] != -1, "We should stop early." + + +@pytest.mark.asyncio +async def test_crossref_retraction_status(): + async with aiohttp.ClientSession() as session: + crossref_client = DocMetadataClient( + session, + clients=cast( + Collection[ + type[MetadataPostProcessor[Any]] | type[MetadataProvider[Any]] + ], + [CrossrefProvider, RetrationDataPostProcessor], + ), + ) + crossref_details = await crossref_client.query( + title="The Dilemma and Countermeasures of Music Education under the Background of Big Data", + fields=["title", "doi", "authors", "journal"], + ) + + assert "**RETRACTED ARTICLE** Citation: Jiaye Han." in crossref_details.formatted_citation # type: ignore[union-attr] + + assert crossref_details.is_retracted is True, "Should be retracted" # type: ignore[union-attr] diff --git a/tests/test_configs.py b/tests/test_configs.py new file mode 100644 index 00000000..8ae3c2d9 --- /dev/null +++ b/tests/test_configs.py @@ -0,0 +1,52 @@ +from unittest.mock import patch + +import pytest +from pydantic import ValidationError + +from paperqa.settings import ( + PromptSettings, + Settings, + get_formatted_variables, + get_settings, +) + + +def test_prompt_settings_validation() -> None: + with pytest.raises(ValidationError): + PromptSettings(summary="Invalid {variable}") + + valid_settings = PromptSettings( + summary="{citation} {question} {summary_length} {text}" + ) + assert valid_settings.summary == "{citation} {question} {summary_length} {text}" + + with pytest.raises(ValidationError): + PromptSettings(pre="Invalid {var}") + + valid_pre_settings = PromptSettings(pre="{question}") + assert valid_pre_settings.pre == "{question}" + + +def test_get_formatted_variables() -> None: + template = "This is a test {variable} with {another_variable}" + variables = get_formatted_variables(template) + assert variables == {"variable", "another_variable"} + + +def test_get_settings_with_valid_config() -> None: + settings = get_settings("fast") + assert not settings.parsing.use_doc_details + + +def test_get_settings_missing_file() -> None: + with ( + patch("importlib.resources.files", side_effect=FileNotFoundError), + pytest.raises(FileNotFoundError), + ): + get_settings("missing_config") + + +def test_settings_default_instantiation() -> None: + settings = Settings() + assert "gpt-" in settings.llm + assert settings.answer.evidence_k == 10 diff --git a/tests/test_paperqa.py b/tests/test_paperqa.py index 3bc8efb7..1a9d339d 100644 --- a/tests/test_paperqa.py +++ b/tests/test_paperqa.py @@ -1,111 +1,66 @@ -from __future__ import annotations - import contextlib import os import pickle -import tempfile import textwrap +from collections.abc import AsyncIterable from io import BytesIO from pathlib import Path -from typing import cast, no_type_check +import httpx import numpy as np import pytest -import requests -from openai import AsyncOpenAI - -from paperqa import ( - Answer, - Doc, - Docs, - NumpyVectorStore, - PromptCollection, - Text, - print_callback, -) + +from paperqa import Answer, Doc, Docs, NumpyVectorStore, Settings, print_callback from paperqa.clients import CrossrefProvider +from paperqa.core import llm_parse_json from paperqa.llms import ( - AnthropicLLMModel, EmbeddingModel, HybridEmbeddingModel, - LangchainEmbeddingModel, - LangchainLLMModel, - LangchainVectorStore, + LiteLLMEmbeddingModel, + LiteLLMModel, LLMModel, - OpenAIEmbeddingModel, - OpenAILLMModel, SparseEmbeddingModel, - VoyageAIEmbeddingModel, - get_score, - guess_model_type, - is_openai_model, ) from paperqa.readers import read_doc from paperqa.utils import ( + extract_score, get_citenames, - llm_read_json, maybe_is_html, maybe_is_text, name_in_text, - strings_similarity, strip_citations, ) -def test_is_openai_model(): - assert is_openai_model("gpt-4o-mini") - assert is_openai_model("babbage-002") - assert is_openai_model("gpt-4-1106-preview") - assert is_openai_model("davinci-002") - assert is_openai_model("ft:gpt-3.5-turbo-0125:my-org::ABC123") - assert not is_openai_model("llama") - assert not is_openai_model("labgpt") - assert not is_openai_model("mixtral-7B") - os.environ["ANYSCALE_API_KEY"] = "abc123" - os.environ["ANYSCALE_BASE_URL"] = "https://example.com" - assert is_openai_model("meta-llama/Meta-Llama-3-70B-Instruct") - assert is_openai_model("mistralai/Mixtral-8x22B-Instruct-v0.1") - os.environ.pop("ANYSCALE_API_KEY") - os.environ.pop("ANYSCALE_BASE_URL") - assert not is_openai_model("meta-llama/Meta-Llama-3-70B-Instruct") - assert not is_openai_model("mistralai/Mixtral-8x22B-Instruct-v0.1") - - -def test_guess_model_type(): - assert guess_model_type("gpt-3.5-turbo") == "chat" - assert guess_model_type("babbage-002") == "completion" - assert guess_model_type("gpt-4-1106-preview") == "chat" - assert guess_model_type("gpt-3.5-turbo-instruct") == "completion" - assert guess_model_type("davinci-002") == "completion" - os.environ["ANYSCALE_API_KEY"] = "abc123" - os.environ["ANYSCALE_BASE_URL"] = "https://example.com" - assert guess_model_type("meta-llama/Meta-Llama-3-70B-Instruct") == "chat" - assert guess_model_type("mistralai/Mixtral-8x22B-Instruct-v0.1") == "chat" - os.environ.pop("ANYSCALE_API_KEY") - os.environ.pop("ANYSCALE_BASE_URL") - - -def test_get_citations(): +@pytest.fixture +def docs_fixture(stub_data_dir: Path) -> Docs: + docs = Docs() + with (stub_data_dir / "paper.pdf").open("rb") as f: + docs.add_file(f, "Wellawatte et al, XAI Review, 2023") + return docs + + +def test_get_citations() -> None: text = ( - "Yes, COVID-19 vaccines are effective. Various studies have documented the " - "effectiveness of COVID-19 vaccines in preventing severe disease, " - "hospitalization, and death. The BNT162b2 vaccine has shown effectiveness " - "ranging from 65% to -41% for the 5-11 years age group and 76% to 46% for the " - "12-17 years age group, after the emergence of the Omicron variant in New York " - "(Dorabawila2022EffectivenessOT). Against the Delta variant, the effectiveness " - "of the BNT162b2 vaccine was approximately 88% after two doses " - "(Bernal2021EffectivenessOC pg. 1-3).\n\n" - "Vaccine effectiveness was also found to be 89% against hospitalization and " - "91% against emergency department or urgent care clinic visits " - "(Thompson2021EffectivenessOC pg. 3-5, Goo2031Foo pg. 3-4). In the UK " - "vaccination program, vaccine effectiveness was approximately 56% in " - "individuals aged ≥70 years between 28-34 days post-vaccination, increasing to " - "approximately 58% from day 35 onwards (Marfé2021EffectivenessOC).\n\n" - "However, it is important to note that vaccine effectiveness can decrease over " - "time. For instance, the effectiveness of COVID-19 vaccines against severe " - "COVID-19 declined to 64% after 121 days, compared to around 90% initially " - "(Chemaitelly2022WaningEO, Foo2019Bar). Despite this, vaccines still provide " - "significant protection against severe outcomes (Bar2000Foo pg 1-3; Far2000 pg 2-5)." + "Yes, COVID-19 vaccines are effective. Various studies have documented the" + " effectiveness of COVID-19 vaccines in preventing severe disease," + " hospitalization, and death. The BNT162b2 vaccine has shown effectiveness" + " ranging from 65% to -41% for the 5-11 years age group and 76% to 46% for the" + " 12-17 years age group, after the emergence of the Omicron variant in New York" + " (Dorabawila2022EffectivenessOT). Against the Delta variant, the effectiveness" + " of the BNT162b2 vaccine was approximately 88% after two doses" + " (Bernal2021EffectivenessOC pg. 1-3).\n\nVaccine effectiveness was also found" + " to be 89% against hospitalization and 91% against emergency department or" + " urgent care clinic visits (Thompson2021EffectivenessOC pg. 3-5, Goo2031Foo" + " pg. 3-4). In the UK vaccination program, vaccine effectiveness was" + " approximately 56% in individuals aged ≥70 years between 28-34 days" + " post-vaccination, increasing to approximately 58% from day 35 onwards" + " (Marfé2021EffectivenessOC).\n\nHowever, it is important to note that vaccine" + " effectiveness can decrease over time. For instance, the effectiveness of" + " COVID-19 vaccines against severe COVID-19 declined to 64% after 121 days," + " compared to around 90% initially (Chemaitelly2022WaningEO, Foo2019Bar)." + " Despite this, vaccines still provide significant protection against severe" + " outcomes (Bar2000Foo pg 1-3; Far2000 pg 2-5)." ) ref = { "Dorabawila2022EffectivenessOT", @@ -121,62 +76,65 @@ def test_get_citations(): assert get_citenames(text) == ref -def test_single_author(): +def test_single_author() -> None: text = "This was first proposed by (Smith 1999)." assert strip_citations(text) == "This was first proposed by ." -def test_multiple_authors(): +def test_multiple_authors() -> None: text = "Recent studies (Smith et al. 1999) show that this is true." assert strip_citations(text) == "Recent studies show that this is true." -def test_multiple_citations(): - text = "As discussed by several authors (Smith et al. 1999; Johnson 2001; Lee et al. 2003)." +def test_multiple_citations() -> None: + text = ( + "As discussed by several authors (Smith et al. 1999; Johnson 2001; Lee et al." + " 2003)." + ) assert strip_citations(text) == "As discussed by several authors ." -def test_citations_with_pages(): +def test_citations_with_pages() -> None: text = "This is shown in (Smith et al. 1999, p. 150)." assert strip_citations(text) == "This is shown in ." -def test_citations_without_space(): +def test_citations_without_space() -> None: text = "Findings by(Smith et al. 1999)were significant." assert strip_citations(text) == "Findings bywere significant." -def test_citations_with_commas(): +def test_citations_with_commas() -> None: text = "The method was adopted by (Smith, 1999, 2001; Johnson, 2002)." assert strip_citations(text) == "The method was adopted by ." -def test_citations_with_text(): +def test_citations_with_text() -> None: text = "This was noted (see Smith, 1999, for a review)." assert strip_citations(text) == "This was noted ." -def test_no_citations(): +def test_no_citations() -> None: text = "There are no references in this text." assert strip_citations(text) == "There are no references in this text." -def test_malformed_citations(): +def test_malformed_citations() -> None: text = "This is a malformed citation (Smith 199)." assert strip_citations(text) == "This is a malformed citation (Smith 199)." -def test_edge_case_citations(): +def test_edge_case_citations() -> None: text = "Edge cases like (Smith et al.1999) should be handled." assert strip_citations(text) == "Edge cases like should be handled." -def test_citations_with_special_characters(): +def test_citations_with_special_characters() -> None: text = "Some names have dashes (O'Neil et al. 2000; Smith-Jones 1998)." assert strip_citations(text) == "Some names have dashes ." -def test_citations_with_nonstandard_chars(): +def test_citations_with_nonstandard_chars() -> None: text = ( "In non-English languages, citations might look different (Müller et al. 1999)." ) @@ -186,53 +144,11 @@ def test_citations_with_nonstandard_chars(): ) -def test_ablations(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - with open(doc_path, "rb") as f: - docs = Docs(prompts=PromptCollection(skip_summary=True)) - docs.add_file(f, "Wellawatte et al, XAI Review, 2023") - answer = docs.get_evidence( - Answer( - question="Which page is the statement 'Deep learning (DL) is advancing the boundaries of computational" # noqa: ISC003 - + "chemistry because it can accurately model non-linear structure-function relationships.' on?" - ) - ) - assert ( - answer.contexts[0].text.text == answer.contexts[0].context - ), "summarization not ablated" - answer = docs.get_evidence( - Answer( - question="Which page is the statement 'Deep learning (DL) is advancing the boundaries of computational" # noqa: ISC003 - + "chemistry because it can accurately model non-linear structure-function relationships.' on?" - ), - disable_vector_search=True, - ) - - -def test_location_awareness(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - with open(doc_path, "rb") as f: - docs = Docs() - docs.add_file(f, "Wellawatte et al, XAI Review, 2023") - answer = docs.get_evidence( - Answer( - question="Which page is the statement 'Deep learning (DL) is advancing the boundaries of computational" # noqa: ISC003 - + "chemistry because it can accurately model non-linear structure-function relationships.' on?" - ), - detailed_citations=True, - ) - assert "2" in answer.context or "1" in answer.context - - -def test_maybe_is_text(): +def test_maybe_is_text() -> None: assert maybe_is_text("This is a test. The sample conc. was 1.0 mM (at 245 ^F)") assert not maybe_is_text("\\C0\\C0\\B1\x00") # get front page of wikipedia - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day" - ) + r = httpx.get("https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day") assert maybe_is_text(r.text) assert maybe_is_html(BytesIO(r.text.encode())) @@ -242,7 +158,7 @@ def test_maybe_is_text(): assert not maybe_is_text(bad_text) -def test_name_in_text(): +def test_name_in_text() -> None: name1 = "FooBar2022" name2 = "FooBar2022a" name3 = "FooBar20" @@ -283,7 +199,7 @@ def test_name_in_text(): assert not name_in_text(name3, text7) -def test_extract_score(): +def test_extract_score() -> None: sample = """ The text describes an experiment where different cell subtypes, including colorectal cancer-associated fibroblasts, were treated with @@ -294,7 +210,7 @@ def test_extract_score(): cancer-associated fibroblasts that typically survive at 2 weeks when cultured with oxaliplatin. (0/10) """ - assert get_score(sample) == 0 + assert extract_score(sample) == 0 sample = """ COVID-19 vaccinations have been shown to be effective against hospitalization @@ -306,7 +222,7 @@ def test_extract_score(): showed higher and more sustained vaccine effectiveness. 8 """ - assert get_score(sample) == 8 + assert extract_score(sample) == 8 sample = """ Here is a 100-word summary of the text: @@ -322,7 +238,7 @@ def test_extract_score(): provide protection against COVID-19. Relevance score: 8 """ - assert get_score(sample) == 8 + assert extract_score(sample) == 8 sample = """ Here is a 100-word summary of the provided text: The text details @@ -337,7 +253,7 @@ def test_extract_score(): effectiveness of COVID-19 vaccinations. Score: 3/10 """ - assert get_score(sample) == 3 + assert extract_score(sample) == 3 sample = """ Here is a 100-word summary of the text: The text discusses a @@ -355,7 +271,7 @@ def test_extract_score(): of COVID-19 vaccinations. """ - assert get_score(sample) == 8 + assert extract_score(sample) == 8 sample = """ Here is a 100-word summary of the text: The text discusses the safety and @@ -373,7 +289,7 @@ def test_extract_score(): of Covid-19 vaccinations. """ - assert get_score(sample) == 5 + assert extract_score(sample) == 5 sample = """ Introduce dynamic elements such as moving nodes or edges to create a sense of activity within @@ -394,7 +310,7 @@ def test_extract_score(): organize the network in a visually appealing manner, such as force-directed placement or hierarchical layouts. 3/10 """ - assert get_score(sample) == 3 + assert extract_score(sample) == 3 sample = ( "The text mentions a work by Shozo Yokoyama titled " @@ -406,7 +322,7 @@ def test_extract_score(): "Relevance Score: 7" ) - assert get_score(sample) == 7 + assert extract_score(sample) == 7 sample = ( "The evolution of human color vision is " @@ -426,7 +342,7 @@ def test_extract_score(): "Relevance Score: 9.5" ) - assert get_score(sample) == 9 + assert extract_score(sample) == 9 @pytest.mark.parametrize( @@ -457,11 +373,11 @@ def test_extract_score(): """, ], ) -def test_llm_read_json(example: str): - assert llm_read_json(example) == {"example": "json"} +def test_llm_parse_json(example: str) -> None: + assert llm_parse_json(example) == {"example": "json"} -def test_llm_read_json_newlines(): +def test_llm_parse_json_newlines() -> None: """Make sure that newlines in json are preserved and escaped.""" example = textwrap.dedent( """ @@ -472,340 +388,251 @@ def test_llm_read_json_newlines(): "relevance_score": 7 }""" ) - assert llm_read_json(example) == { + assert llm_parse_json(example) == { "summary": "A line\n\nAnother line", "relevance_score": 7, } -@pytest.mark.asyncio() -async def test_chain_completion(): - client = AsyncOpenAI() - llm = OpenAILLMModel(config={"model": "babbage-002", "temperature": 0.2}) - call = llm.make_chain( - client, - "The {animal} says", - skip_system=True, - ) +@pytest.mark.asyncio +async def test_chain_completion() -> None: + s = Settings(llm="babbage-002", temperature=0.2) outputs = [] - def accum(x): + def accum(x) -> None: outputs.append(x) - completion = await call({"animal": "duck"}, callbacks=[accum]) # type: ignore[call-arg] + llm = s.get_llm() + completion = await llm.run_prompt( + prompt="The {animal} says", + data={"animal": "duck"}, + skip_system=True, + callbacks=[accum], + ) assert completion.seconds_to_first_token > 0 assert completion.prompt_count > 0 assert completion.completion_count > 0 assert str(completion) == "".join(outputs) - completion = await call({"animal": "duck"}) # type: ignore[call-arg] + completion = await llm.run_prompt( + prompt="The {animal} says", data={"animal": "duck"}, skip_system=True + ) assert completion.seconds_to_first_token == 0 assert completion.seconds_to_last_token > 0 + assert completion.cost > 0 + + +@pytest.mark.asyncio +async def test_chain_chat() -> None: + model_config = { + "model_list": [ + { + "model_name": "gpt-4o-mini", + "litellm_params": { + "model": "gpt-4o-mini", + "temperature": 0, + "max_tokens": 56, + }, + } + ] + } + llm = LiteLLMModel(name="gpt-4o-mini", config=model_config) -@pytest.mark.asyncio() -async def test_chain_chat(): - client = AsyncOpenAI() - llm = OpenAILLMModel( - config={"temperature": 0, "model": "gpt-4o-mini", "max_tokens": 56} - ) - call = llm.make_chain( - client, - "The {animal} says", - skip_system=True, - ) outputs = [] - def accum(x): + def accum(x) -> None: outputs.append(x) - completion = await call({"animal": "duck"}, callbacks=[accum]) # type: ignore[call-arg] + completion = await llm.run_prompt( + prompt="The {animal} says", + data={"animal": "duck"}, + skip_system=True, + callbacks=[accum], + ) assert completion.seconds_to_first_token > 0 assert completion.prompt_count > 0 assert completion.completion_count > 0 assert str(completion) == "".join(outputs) + assert completion.cost > 0 - completion = await call({"animal": "duck"}) # type: ignore[call-arg] + completion = await llm.run_prompt( + prompt="The {animal} says", + data={"animal": "duck"}, + skip_system=True, + ) assert completion.seconds_to_first_token == 0 assert completion.seconds_to_last_token > 0 + assert completion.cost > 0 # check with mixed callbacks - async def ac(x): # noqa: ARG001 + async def ac(x) -> None: pass - completion = await call({"animal": "duck"}, callbacks=[accum, ac]) # type: ignore[call-arg] - - -@pytest.mark.asyncio() -async def test_anthropic_chain() -> None: - - try: - from anthropic import AsyncAnthropic - except ImportError: - pytest.skip("Test requires anthropic to be installed.") - - client = AsyncAnthropic() - llm = AnthropicLLMModel() - call = llm.make_chain( - client, - "The {animal} says", + completion = await llm.run_prompt( + prompt="The {animal} says", + data={"animal": "duck"}, skip_system=True, + callbacks=[accum, ac], ) + assert completion.cost > 0 - def accum(x): - outputs.append(x) +@pytest.mark.skipif(os.environ.get("ANTHROPIC_API_KEY") is None, reason="No API key") +@pytest.mark.asyncio +async def test_anthropic_chain(stub_data_dir: Path) -> None: + anthropic_settings = Settings(llm="claude-3-haiku-20240307") outputs: list[str] = [] - completion = await call({"animal": "duck"}, callbacks=[accum]) # type: ignore[call-arg] - assert completion.seconds_to_first_token > 0 - assert completion.prompt_count > 0 - assert completion.completion_count > 0 - assert str(completion) == "".join(outputs) - assert isinstance(completion.text, str) - - completion = await call({"animal": "duck"}) # type: ignore[call-arg] - assert completion.seconds_to_first_token == 0 - assert completion.seconds_to_last_token > 0 - assert isinstance(completion.text, str) - - docs = Docs(llm="claude-3-sonnet-20240229", client=client) - await docs.aadd_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - answer = await docs.aget_evidence( - Answer(question="What is the national flag of Canada?") - ) - await docs.aquery("What is the national flag of Canada?", answer=answer) + def accum(x) -> None: + outputs.append(x) -@pytest.mark.skipif( - not (os.environ.get("ANYSCALE_BASE_URL") and os.environ.get("ANYSCALE_API_KEY")), - reason="Anyscale URL and keys are not set", -) -@pytest.mark.asyncio() -async def test_anyscale_chain(): - client = AsyncOpenAI( - base_url=os.environ["ANYSCALE_BASE_URL"], api_key=os.environ["ANYSCALE_API_KEY"] - ) - llm = OpenAILLMModel( - config={ - "temperature": 0, - "model": "meta-llama/Meta-Llama-3-8B-Instruct", - "max_tokens": 56, # matches openAI chat test - } - ) - call = llm.make_chain( - client, - "The {animal} says", + llm = anthropic_settings.get_llm() + completion = await llm.run_prompt( + prompt="The {animal} says", + data={"animal": "duck"}, skip_system=True, + callbacks=[accum], ) - outputs = [] # type: ignore[var-annotated] - completion = await call({"animal": "duck"}, callbacks=[outputs.append]) # type: ignore[call-arg] assert completion.seconds_to_first_token > 0 assert completion.prompt_count > 0 assert completion.completion_count > 0 assert str(completion) == "".join(outputs) + assert isinstance(completion.text, str) + assert completion.cost > 0 - completion = await call({"animal": "duck"}) # type: ignore[call-arg] + completion = await llm.run_prompt( + prompt="The {animal} says", data={"animal": "duck"}, skip_system=True + ) assert completion.seconds_to_first_token == 0 assert completion.seconds_to_last_token > 0 + assert isinstance(completion.text, str) + assert completion.cost > 0 - completion = await call({"animal": "duck"}, callbacks=[outputs.append]) # type: ignore[call-arg] - - -def test_docs(): - docs = Docs(llm="babbage-002") - docs.add_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert docs.docs["test"].docname == "Wiki2023" - assert docs.llm == "babbage-002" - assert docs.summary_llm == "babbage-002" - - -def test_evidence(): - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) docs = Docs() - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - original_doc = next(iter(docs.docs.values())) - assert ( - original_doc.embedding is not None - ), "For downstream assertions of in-tact embedding, we need an embedding" - evidence = docs.get_evidence( - Answer(question="For which state was Bates a governor?"), k=1, max_sources=1 - ) - assert "Missouri" in evidence.context - assert original_doc.embedding is not None, "Embedding should have remained in-tact" - assert ( - evidence.contexts[0].text.doc.embedding is None - ), "Embedding should have been removed" - - evidence = docs.get_evidence( - Answer(question="For which state was Bates a governor?"), - detailed_citations=True, - ) - assert "From WikiMedia Foundation, 2023, Accessed now" in evidence.context - os.remove(doc_path) - - -def test_json_evidence() -> None: - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", timeout=30.0 - ) - r.raise_for_status() - f.write(r.text) - summary_llm = OpenAILLMModel( - config={ - "model": "gpt-4o-mini", - "response_format": {"type": "json_object"}, - "temperature": 0.0, - } - ) - docs = Docs( - prompts=PromptCollection(json_summary=True), - summary_llm_model=summary_llm, - llm_result_callback=print_callback, - ) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - evidence = docs.get_evidence( - Answer(question="For which state was Bates a governor?"), k=1, max_sources=1 - ) - assert "Missouri" in evidence.context - - evidence = docs.get_evidence( - Answer(question="For which state was Bates a governor?"), - detailed_citations=True, + await docs.aadd( + stub_data_dir / "flag_day.html", + "National Flag of Canada Day", + settings=anthropic_settings, ) - assert "From WikiMedia Foundation, 2023, Accessed now" in evidence.context - os.remove(doc_path) - - -def test_custom_json_props(): - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - summary_llm = OpenAILLMModel( - config={ - "model": "gpt-4o-mini", - "response_format": {"type": "json_object"}, - "temperature": 0.0, - } - ) - my_prompts = PromptCollection( - json_summary=True, - summary_json_system="Provide a summary of the excerpt that could help answer the question based on the excerpt. " # noqa: E501 - "The excerpt may be irrelevant. Do not directly answer the question - only summarize relevant information. " - "Respond with the following JSON format:\n\n" - '{{\n"summary": "...",\n"person_name": "...",\n"relevance_score": "..."}}\n\n' - "where `summary` is relevant information from text - " - "about 100 words words, `person_name` specifies the person discussed in " - "the excerpt (may be different than query), and `relevance_score` is " - "the relevance of `summary` to answer the question (integer out of 10).", - ) - docs = Docs( - prompts=my_prompts, - summary_llm_model=summary_llm, - llm_result_callback=print_callback, - ) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - evidence = docs.get_evidence( - Answer(question="For which state was Bates a governor?"), k=1, max_sources=1 - ) - assert "person_name" in cast(dict, evidence.contexts[0].model_extra) - assert "person_name: " in evidence.context - answer = docs.query("What is Frederick Bates's greatest accomplishment?") - assert "person_name" in answer.context - os.remove(doc_path) - - -def test_query(): - docs = Docs() - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + result = await docs.aget_evidence( + "What is the national flag of Canada?", settings=anthropic_settings ) - docs.query("What is Frederick Bates's greatest accomplishment?") + assert result.cost > 0 -def test_answer_attributes(): +def test_make_docs(stub_data_dir: Path) -> None: docs = Docs() - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", + docs.add( + stub_data_dir / "flag_day.html", + "WikiMedia Foundation, 2023, Accessed now", dockey="test", ) - answer = docs.query("What is Frederick Bates's greatest accomplishment?") - used_citations = answer.used_contexts - assert len(used_citations) > 0 - assert len(used_citations) < len(answer.contexts) - assert ( - answer.get_citation(next(iter(used_citations))) - == "WikiMedia Foundation, 2023, Accessed now" - ) - - # make sure it is serialized correctly - js = answer.model_dump_json() - assert "used_contexts" in js - - # make sure it can be deserialized - answer2 = Answer.model_validate_json(js) - assert answer2.used_contexts == used_citations + assert docs.docs["test"].docname == "Wiki2023" -def test_llmresult_callbacks(): +def test_evidence(docs_fixture) -> None: + fast_settings = Settings.from_name("debug") + evidence = docs_fixture.get_evidence( + Answer(question="What does XAI stand for?"), + settings=fast_settings, + ).contexts + assert len(evidence) >= fast_settings.answer.evidence_k + + +def test_json_evidence(docs_fixture) -> None: + settings = Settings.from_name("fast") + settings.prompts.use_json = True + settings.prompts.summary_json_system = ( + "Provide a summary of the excerpt that could help answer the question based on" + " the excerpt. The excerpt may be irrelevant. Do not directly answer the" + " question - only summarize relevant information. Respond with the following" + ' JSON format:\n\n {{\n"summary": "...",\n"author_name":' + ' "...",\n"relevance_score": "..."}}\n\n where `summary` is relevant' + " information from text - about 100 words words, `author_name` specifies the" + " author , and `relevance_score` is the relevance of `summary` to answer the" + " question (integer out of 10)." + ) + evidence = docs_fixture.get_evidence( + Answer(question="Who wrote this article?"), + settings=settings, + ).contexts + assert evidence[0].author_name + + +def test_ablations(docs_fixture) -> None: + settings = Settings() + settings.answer.evidence_skip_summary = True + settings.answer.evidence_retrieval = False + contexts = docs_fixture.get_evidence( + "Which page is the statement 'Deep learning (DL) is advancing the boundaries of" + " computational chemistry because it can accurately model non-linear" + " structure-function relationships.' on?", + settings=settings, + ).contexts + assert contexts[0].text.text == contexts[0].context, "summarization not ablated" + + assert len(contexts) == len(docs_fixture.texts), "evidence retrieval not ablated" + + +def test_location_awareness(docs_fixture) -> None: + settings = Settings() + settings.answer.evidence_k = 3 + settings.prompts.use_json = False + settings.prompts.system = "Answer either N/A or a page number." + settings.prompts.summary = "{citation}\n\n{text}\n\n{question}{summary_length}" + settings.answer.evidence_summary_length = "" + + contexts = docs_fixture.get_evidence( + "Which page is the statement 'Deep learning (DL) is advancing the boundaries of" + " computational chemistry because it can accurately model non-linear" + " structure-function relationships.' on?", + settings=settings, + ).contexts + assert "1" in "\n".join( + [c.context for c in contexts] + ), "location not found in evidence" + + +def test_query(docs_fixture) -> None: + docs_fixture.query("Is XAI usable in chemistry?") + + +def test_llmresult_callback(docs_fixture) -> None: my_results = [] - async def my_callback(result): + async def my_callback(result) -> None: my_results.append(result) - docs = Docs(llm_result_callback=my_callback) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + settings = Settings.from_name("fast") + summary_llm = settings.get_summary_llm() + summary_llm.llm_result_callback = my_callback + docs_fixture.get_evidence( + "What is XAI?", settings=settings, summary_llm_model=summary_llm ) - docs.query("What is Frederick Bates's greatest accomplishment?") - assert any(x.name == "answer" for x in my_results) - assert len(my_results) > 1 + assert my_results + assert my_results[0].name -def test_duplicate() -> None: +def test_duplicate(stub_data_dir: Path) -> None: """Check Docs doesn't store duplicates, while checking nonduplicate docs are stored.""" docs = Docs() - assert docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + assert docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", dockey="test1", ) assert ( - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", dockey="test1", ) is None ) assert len(docs.docs) == 1, "Should have added only one document" - assert docs.add_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", + assert docs.add( + stub_data_dir / "flag_day.html", citation="WikiMedia Foundation, 2023, Accessed now", dockey="test2", ) @@ -814,45 +641,34 @@ def test_duplicate() -> None: ), "Unique documents should be hashed as unique" -def test_custom_embedding(): +def test_custom_embedding(stub_data_dir: Path) -> None: class MyEmbeds(EmbeddingModel): name: str = "my_embed" - async def embed_documents(self, client, texts): # noqa: ARG002 + async def embed_documents(self, texts): return [[1, 2, 3] for _ in texts] docs = Docs( - docs_index=NumpyVectorStore(embedding_model=MyEmbeds()), - texts_index=NumpyVectorStore(embedding_model=MyEmbeds()), - embedding_client=None, + texts_index=NumpyVectorStore(), ) - assert docs._embedding_client is None - assert docs.embedding == "my_embed" - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + embedding_model=MyEmbeds(), ) - assert docs.docs["test"].embedding == [1, 2, 3] + assert docs.texts[0].embedding == [1, 2, 3] -def test_sparse_embedding() -> None: +def test_sparse_embedding(stub_data_dir: Path) -> None: docs = Docs( - docs_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - embedding_client=None, + texts_index=NumpyVectorStore(), ) - assert docs._embedding_client is None - assert docs.embedding.startswith("sparse") # type: ignore[union-attr] - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + embedding_model=SparseEmbeddingModel(), ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] - assert all( - len(np.array(x.embedding).shape) == 1 for x in docs.docs.values() - ), "Embeddings should be 1D" + assert any(docs.texts[0].embedding) # type: ignore[arg-type] assert all( len(np.array(x.embedding).shape) == 1 for x in docs.texts ), "Embeddings should be 1D" @@ -862,34 +678,20 @@ def test_sparse_embedding() -> None: assert docs.texts[1].embedding is not None assert np.shape(docs.texts[0].embedding) == np.shape(docs.texts[1].embedding) - # test alias - docs = Docs(embedding="sparse") - assert docs._embedding_client is None - assert docs.embedding.startswith("sparse") # type: ignore[union-attr] - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] - -def test_hybrid_embedding() -> None: - model = HybridEmbeddingModel( - models=[OpenAIEmbeddingModel(), SparseEmbeddingModel()] +def test_hybrid_embedding(stub_data_dir: Path) -> None: + emb_model = HybridEmbeddingModel( + models=[LiteLLMEmbeddingModel(), SparseEmbeddingModel()] ) docs = Docs( - docs_index=NumpyVectorStore(embedding_model=model), - texts_index=NumpyVectorStore(embedding_model=model), + texts_index=NumpyVectorStore(), ) - assert isinstance(docs._embedding_client, AsyncOpenAI) - assert docs.embedding.startswith("hybrid") # type: ignore[union-attr] - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + embedding_model=emb_model, ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] + assert any(docs.texts[0].embedding) # type: ignore[arg-type] # check the embeddings are the same size assert docs.texts[0].embedding is not None @@ -897,414 +699,88 @@ def test_hybrid_embedding() -> None: assert np.shape(docs.texts[0].embedding) == np.shape(docs.texts[1].embedding) # now try via alias - docs = Docs( + emb_settings = Settings( embedding="hybrid-text-embedding-3-small", ) - assert isinstance(docs._embedding_client, AsyncOpenAI) - assert docs.embedding.startswith("hybrid") # type: ignore[union-attr] - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] - - -def test_sentence_transformer_embedding(): - from paperqa import SentenceTransformerEmbeddingModel - - docs = Docs(embedding="sentence-transformers") - assert docs._embedding_client is None - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + embedding_model=emb_settings.get_embedding_model(), ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] + assert any(docs.texts[0].embedding) - docs = Docs( - texts_index=NumpyVectorStore( - embedding_model=SentenceTransformerEmbeddingModel() - ), - doc_index=NumpyVectorStore(embedding_model=SentenceTransformerEmbeddingModel()), - embedding_client=None, - ) - assert docs._embedding_client is None - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert any(docs.docs["test"].embedding) # type: ignore[arg-type] +def test_custom_llm(stub_data_dir: Path) -> None: + from paperqa.llms import Chunk -def test_custom_llm(): class MyLLM(LLMModel): name: str = "myllm" - async def acomplete(self, client, prompt): # noqa: ARG002 - assert client is None - return "Echo" - - docs = Docs(llm_model=MyLLM(), client=None) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - evidence = docs.get_evidence(Answer(question="Echo")) - assert "Echo" in evidence.context - - -def test_custom_llm_stream(): - class MyLLM(LLMModel): - name: str = "myllm" - - async def acomplete_iter(self, client, prompt): # noqa: ARG002 - assert client is None - yield "Echo" - - docs = Docs(llm_model=MyLLM(), client=None) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - evidence = docs.get_evidence( - Answer(question="Echo"), - get_callbacks=lambda x: [lambda y: print(y, end="")], # noqa: ARG005 - ) - assert "Echo" in evidence.context - - -def test_langchain_llm(): - from langchain_openai import ChatOpenAI, OpenAI + async def acomplete(self, prompt: str) -> Chunk: # noqa: ARG002 + return Chunk(text="Echo", prompt_tokens=1, completion_tokens=1) - docs = Docs(llm="langchain", client=ChatOpenAI(model="gpt-4o-mini")) - assert isinstance(docs.llm_model, LangchainLLMModel) - assert isinstance(docs.summary_llm_model, LangchainLLMModel) - assert docs.llm == "gpt-4o-mini" - assert docs.summary_llm == "gpt-4o-mini" - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert docs._client is not None - assert isinstance(docs.llm_model, LangchainLLMModel) - assert docs.summary_llm_model == docs.llm_model - - docs.get_evidence( - Answer(question="What is Frederick Bates's greatest accomplishment?"), - get_callbacks=lambda x: [lambda y: print(y, end="")], # noqa: ARG005 - ) - - assert docs.llm_model.llm_type == "chat" - - # trying without callbacks (different codepath) - docs.get_evidence( - Answer(question="What is Frederick Bates's greatest accomplishment?") - ) + async def acomplete_iter( + self, prompt: str # noqa: ARG002 + ) -> AsyncIterable[Chunk]: + yield Chunk(text="Echo", prompt_tokens=1, completion_tokens=1) - # now completion - - docs = Docs(llm_model=LangchainLLMModel(), client=OpenAI(model="babbage-002")) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - docs.get_evidence( - Answer(question="What is Frederick Bates's greatest accomplishment?"), - get_callbacks=lambda x: [lambda y: print(y, end="")], # noqa: ARG005 - ) - - assert docs.summary_llm_model.llm_type == "completion" # type: ignore[union-attr] - - # trying without callbacks (different codepath) - docs.get_evidence( - Answer(question="What is Frederick Bates's greatest accomplishment?") - ) - - # now make sure we can pickle it - docs_pickle = pickle.dumps(docs) - docs2 = pickle.loads(docs_pickle) # noqa: S301 - assert docs2._client is None - assert docs2.llm == "babbage-002" - docs2.set_client(OpenAI(model="babbage-002")) - assert docs2.summary_llm == "babbage-002" - docs2.get_evidence( - Answer(question="What is Frederick Bates's greatest accomplishment?"), - get_callbacks=lambda x: [print], # noqa: ARG005 - ) - - -def test_langchain_embeddings(): - from langchain_openai import OpenAIEmbeddings - - docs = Docs( - texts_index=NumpyVectorStore(embedding_model=LangchainEmbeddingModel()), - docs_index=NumpyVectorStore(embedding_model=LangchainEmbeddingModel()), - embedding_client=OpenAIEmbeddings(), - ) - assert docs._embedding_client is not None - - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - docs = Docs(embedding="langchain", embedding_client=OpenAIEmbeddings()) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", + docs = Docs() + docs.add( + stub_data_dir / "bates.txt", citation="WikiMedia Foundation, 2023, Accessed now", dockey="test", + llm_model=MyLLM(), ) + evidence = docs.get_evidence("Echo", summary_llm_model=MyLLM()).contexts + assert "Echo" in evidence[0].context + evidence = docs.get_evidence( + "Echo", callbacks=[print_callback], summary_llm_model=MyLLM() + ).contexts + assert "Echo" in evidence[0].context -@pytest.mark.skipif( - not os.environ.get("VOYAGE_API_KEY"), - reason="Voyage embeddings require a VOYAGE_API_KEY", -) -def test_voyage_embeddings(): - from voyageai import AsyncClient - - docs = Docs( - texts_index=NumpyVectorStore( - embedding_model=VoyageAIEmbeddingModel(name="voyage-2") - ), - docs_index=NumpyVectorStore( - embedding_model=VoyageAIEmbeddingModel(name="voyage-2") - ), - embedding_client=AsyncClient(), - ) - assert docs._embedding_client is not None - - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - assert docs.docs["test"].embedding, "Embedding should not be None" - assert any(docs.docs["test"].embedding), "Embeddings are not present" - assert len(docs.docs["test"].embedding) == 1024, "Wrong voyage-2 embedding shape" - - -@pytest.mark.asyncio() -async def test_langchain_vector_store(): - from langchain_community.vectorstores.faiss import FAISS - from langchain_openai import OpenAIEmbeddings - - some_texts = [ - Text( - embedding=OpenAIEmbeddings().embed_query("test"), - text="this is a test", - name="test", - doc=Doc(docname="test", citation="test", dockey="test"), - ) - ] - - index = LangchainVectorStore() - with pytest.raises(ValueError, match="You must set store_builder"): - index.add_texts_and_embeddings(some_texts) - - with pytest.raises(ValueError, match="store_builder must take two arguments"): - index = LangchainVectorStore(store_builder=lambda x: None) # noqa: ARG005 - - with pytest.raises(ValueError, match="store_builder must be callable"): - index = LangchainVectorStore(store_builder="foo") - - # now with real builder - index = LangchainVectorStore( - store_builder=lambda x, y: FAISS.from_embeddings(x, OpenAIEmbeddings(), y) - ) - assert index._store is None - index.add_texts_and_embeddings(some_texts) - assert index._store is not None - # check search returns Text obj - data, _ = await index.similarity_search(None, "test", k=1) # type: ignore[unreachable] - assert isinstance(data[0], Text) - - # now try with convenience - index = LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()) - assert index._store is None - index.add_texts_and_embeddings(some_texts) - assert index._store is not None - docs = Docs( - texts_index=LangchainVectorStore(cls=FAISS, embedding_model=OpenAIEmbeddings()) - ) - assert docs._embedding_client is not None # from docs_index default - - await docs.aadd_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - # should be embedded - - # now try with JIT - docs = Docs(texts_index=index, jit_texts_index=True) - await docs.aadd_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", +def test_docs_pickle(stub_data_dir) -> None: + """Ensure that Docs object can be pickled and unpickled correctly.""" + docs = Docs() + docs.add( + stub_data_dir / "flag_day.html", + "WikiMedia Foundation, 2023, Accessed now", dockey="test", ) - # should get cleared and rebuilt here - ev = await docs.aget_evidence( - answer=Answer(question="What is Frederick Bates's greatest accomplishment?") - ) - assert len(ev.context) > 0 - # now with dockkey filter - await docs.aget_evidence( - answer=Answer( - question="What is Frederick Bates's greatest accomplishment?", - dockey_filter=["test"], - ) - ) - # make sure we can pickle it + # Pickle the Docs object docs_pickle = pickle.dumps(docs) - pickle.loads(docs_pickle) # noqa: S301 + unpickled_docs = pickle.loads(docs_pickle) - # will not work at this point - have to reset index + assert unpickled_docs.docs["test"].docname == "Wiki2023" + assert len(unpickled_docs.docs) == 1 -@pytest.mark.asyncio() -async def test_aquery(): +def test_bad_context(stub_data_dir) -> None: docs = Docs() - await docs.aadd_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", + docs.add(stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now") + answer = docs.query( + "What do scientist estimate as the planetary composition of Jupyter?" ) - await docs.aquery("What is Frederick Bates's greatest accomplishment?") + assert "cannot answer" in answer.answer -@pytest.mark.asyncio() -async def test_adoc_match() -> None: +def test_repeat_keys(stub_data_dir) -> None: docs = Docs() - await docs.aadd_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation=( - 'Wikipedia contributors. "Frederick Bates (politician)."' - " Wikipedia, The Free Encyclopedia. Wikipedia, The Free Encyclopedia," - " Accessed now" - ), - dockey="test", + result = docs.add( + stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now" ) - for _ in range(2): # Check calling 2+ times doesn't wipe the Docs - assert await docs.adoc_match( - "What is Frederick Bates's greatest accomplishment?" - ) - - -def test_docs_pickle() -> None: - # 1. Fill out docs - with tempfile.NamedTemporaryFile(mode="r+", encoding="utf-8", suffix=".html") as f: - # get front page of wikipedia - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Take_Your_Dog_to_Work_Day" - ) - r.raise_for_status() - f.write(r.text) - docs = Docs( - llm_model=OpenAILLMModel( - config={"temperature": 0.0, "model": "gpt-4o-mini"} - ), - summary_llm_model=OpenAILLMModel( - config={"temperature": 0.0, "model": "gpt-4o-mini"} - ), - ) - assert docs._client is not None - old_config = cast(OpenAILLMModel, docs.llm_model).config - old_sconfig = docs.summary_llm_model.config # type: ignore[union-attr] - docs.add(f.name, "WikiMedia Foundation, 2023, Accessed now", chunk_chars=1000) # type: ignore[arg-type] - - # 2. Pickle and unpickle, checking unpickled is in-tact - docs_pickle = pickle.dumps(docs) - docs2 = pickle.loads(docs_pickle) # noqa: S301 - with pytest.raises(ValueError, match="forget to set it after pickling"): - docs2.query("What date is bring your dog to work in the US?") - docs2.set_client() - assert docs2._client is not None - assert docs2.llm_model.config == old_config - assert docs2.summary_llm_model.config == old_sconfig - print(old_config, old_sconfig) - assert len(docs.docs) == len(docs2.docs) - for _ in range(4): # Retry a few times, because this is flaky - docs_context = docs.get_evidence( - Answer( - question="What date is bring your dog to work in the US?", - summary_length="about 20 words", - ), - k=3, - max_sources=1, - ).context - docs2_context = docs2.get_evidence( - Answer( - question="What date is bring your dog to work in the US?", - summary_length="about 20 words", - ), - k=3, - max_sources=1, - ).context - # It is shocking how unrepeatable this is - if strings_similarity(s1=docs_context, s2=docs2_context) > 0.50: - break - else: - raise AssertionError("Failed to attain similar contexts, even with retrying.") - - # make sure we can query - docs.query("What date is bring your dog to work in the US?") - - # make sure we can still embed documents - docs2.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - ) - - -def test_bad_context(): - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs() - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - answer = docs.query("What is the radius of Jupyter?") - assert "cannot answer" in answer.answer - os.remove(doc_path) - - -def test_repeat_keys(): - doc_path = "example.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs( - llm_model=OpenAILLMModel(config={"temperature": 0.0, "model": "babbage-002"}) + assert result + result = docs.add( + stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now" ) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - try: # noqa: SIM105 - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - except ValueError: - pass + assert not result assert len(docs.docs) == 1 - # now with different paths - doc_path2 = "example2.txt" - with open(doc_path2, "w", encoding="utf-8") as f: - # get wiki page about politician - f.write(r.text) - f.write("\n") # so we don't have same hash - docs.add(doc_path2, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] + docs.add( + stub_data_dir / "flag_day.html", "WikiMedia Foundation, 2023, Accessed now" + ) assert len(docs.docs) == 2 # check keys @@ -1312,47 +788,39 @@ def test_repeat_keys(): assert ds[0].docname == "Wiki2023" assert ds[1].docname == "Wiki2023a" - os.remove(doc_path) - os.remove(doc_path2) - -def test_pdf_reader(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - docs = Docs(llm_model=OpenAILLMModel(config={"temperature": 0.0, "model": "gpt-4"})) - docs.add(doc_path, "Wellawatte et al, XAI Review, 2023") # type: ignore[arg-type] - answer = docs.query("Are counterfactuals actionable? [yes/no]") +def test_can_read_normal_pdf_reader(docs_fixture) -> None: + answer = docs_fixture.query("Are counterfactuals actionable? [yes/no]") assert "yes" in answer.answer or "Yes" in answer.answer -def test_pdf_reader_w_no_match_doc_details(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - docs = Docs(llm_model=OpenAILLMModel(config={"temperature": 0.0, "model": "gpt-4"})) - docs.add(doc_path, "Wellawatte et al, XAI Review, 2023", use_doc_details=True) # type: ignore[arg-type] +def test_pdf_reader_w_no_match_doc_details(stub_data_dir: Path) -> None: + docs = Docs() + docs.add(stub_data_dir / "paper.pdf", "Wellawatte et al, XAI Review, 2023") # doc will be a DocDetails object, but nothing can be found # thus, we retain the prior citation data assert ( next(iter(docs.docs.values())).citation == "Wellawatte et al, XAI Review, 2023" ) - answer = docs.query("Are counterfactuals actionable? [yes/no]") - assert "yes" in answer.answer or "Yes" in answer.answer -def test_pdf_reader_match_doc_details(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - docs = Docs(llm_model=OpenAILLMModel(config={"temperature": 0.0, "model": "gpt-4"})) +def test_pdf_reader_match_doc_details(stub_data_dir: Path) -> None: + doc_path = stub_data_dir / "paper.pdf" + docs = Docs() # we limit to only crossref since s2 is too flaky docs.add( - doc_path, # type: ignore[arg-type] - "Wellawatte et al, A Perspective on Explanations of Molecular Prediction Models, XAI Review, 2023", + doc_path, + "Wellawatte et al, A Perspective on Explanations of Molecular Prediction" + " Models, XAI Review, 2023", use_doc_details=True, clients={CrossrefProvider}, fields=["author", "journal"], ) doc_details = next(iter(docs.docs.values())) - assert doc_details.dockey == "5300ef1d5fb960d7" + assert doc_details.dockey in { + "41f786fcc56d27ff0c1507153fae3774", # From file contents + "5300ef1d5fb960d7", # Or from crossref data + } # note year is unknown because citation string is only parsed for authors/title/doi # AND we do not request it back from the metadata sources assert doc_details.docname == "wellawatteUnknownyearaperspectiveon" @@ -1367,85 +835,49 @@ def test_pdf_reader_match_doc_details(): assert "yes" in answer.answer or "Yes" in answer.answer -def test_fileio_reader_pdf(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - with open(doc_path, "rb") as f: +@pytest.mark.flaky(reruns=3, only_rerun=["AssertionError"]) +def test_fileio_reader_pdf(stub_data_dir: Path) -> None: + with (stub_data_dir / "paper.pdf").open("rb") as f: docs = Docs() docs.add_file(f, "Wellawatte et al, XAI Review, 2023") answer = docs.query("Are counterfactuals actionable?[yes/no]") assert "yes" in answer.answer or "Yes" in answer.answer -def test_fileio_reader_txt(): +def test_fileio_reader_txt(stub_data_dir: Path) -> None: # can't use curie, because it has trouble with parsed HTML docs = Docs() - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - if r.status_code != 200: - raise ValueError("Could not download wikipedia page") + with (stub_data_dir / "bates.txt").open("rb") as file: + file_content = file.read() + docs.add_file( - BytesIO(r.text.encode()), + BytesIO(file_content), "WikiMedia Foundation, 2023, Accessed now", - chunk_chars=1000, ) answer = docs.query("What country was Frederick Bates born in?") assert "United States" in answer.answer -def test_pdf_pypdf_reader(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") - splits1 = read_doc( - Path(doc_path), - Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=True, - overlap=100, - chunk_chars=3000, - ) - splits2 = read_doc( - Path(doc_path), - Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=False, - overlap=100, - chunk_chars=3000, - ) - assert ( - strings_similarity(splits1[0].text.casefold(), splits2[0].text.casefold()) - > 0.85 - ) - - -def test_parser_only_reader(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") +def test_parser_only_reader(stub_data_dir: Path): + doc_path = stub_data_dir / "paper.pdf" parsed_text = read_doc( Path(doc_path), Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=True, - overlap=100, - chunk_chars=3000, parsed_text_only=True, ) assert parsed_text.metadata.parse_type == "pdf" - assert any("pypdf" in t for t in parsed_text.metadata.parsing_libraries) assert parsed_text.metadata.chunk_metadata is None assert parsed_text.metadata.total_parsed_text_length == sum( len(t) for t in parsed_text.content.values() # type: ignore[misc,union-attr] ) -def test_chunk_metadata_reader(): - tests_dir = os.path.dirname(os.path.abspath(__file__)) - doc_path = os.path.join(tests_dir, "paper.pdf") +def test_chunk_metadata_reader(stub_data_dir: Path) -> None: + doc_path = stub_data_dir / "paper.pdf" chunk_text, metadata = read_doc( Path(doc_path), Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=True, - overlap=100, - chunk_chars=3000, - parsed_text_only=False, + parsed_text_only=False, # noqa: FURB120 include_metadata=True, ) assert metadata.parse_type == "pdf" @@ -1459,21 +891,12 @@ def test_chunk_metadata_reader(): for i in range(len(chunk_text) - 1) ) - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) + doc_path = stub_data_dir / "flag_day.html" chunk_text, metadata = read_doc( Path(doc_path), Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=False, - overlap=100, - chunk_chars=3000, - parsed_text_only=False, + parsed_text_only=False, # noqa: FURB120 include_metadata=True, ) # NOTE the use of tiktoken changes the actual char and overlap counts @@ -1484,15 +907,12 @@ def test_chunk_metadata_reader(): assert all(len(chunk.text) <= 3000 * 1.25 for chunk in chunk_text) assert metadata.total_parsed_text_length // 3000 <= len(chunk_text) - doc_path = os.path.abspath(__file__) + doc_path = Path(os.path.abspath(__file__)) chunk_text, metadata = read_doc( - Path(doc_path), + doc_path, Doc(docname="foo", citation="Foo et al, 2002", dockey="1"), - force_pypdf=False, - overlap=100, - chunk_chars=3000, - parsed_text_only=False, + parsed_text_only=False, # noqa: FURB120 include_metadata=True, ) assert metadata.parse_type == "txt" @@ -1503,125 +923,17 @@ def test_chunk_metadata_reader(): assert metadata.total_parsed_text_length // 3000 <= len(chunk_text) -def test_prompt_length(): - doc_path = "example.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs() - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - docs.query("What is the name of the politician?") - - -def test_code(): +def test_code() -> None: # load this script - doc_path = os.path.abspath(__file__) - docs = Docs( - llm_model=OpenAILLMModel(config={"temperature": 0.0, "model": "babbage-002"}) - ) - docs.add(doc_path, "test_paperqa.py", docname="test_paperqa.py", disable_check=True) # type: ignore[arg-type] - assert len(docs.docs) == 1 - docs.query("What function tests the preview?") - - -def test_citation(): - doc_path = "example.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs() - docs.add(doc_path) # type: ignore[arg-type] - assert next(iter(docs.docs.values())).docname in { - "Wikipedia2024", - "Frederick2024", - "Wikipedia", - "Frederick", - } - - -def test_dockey_filter(): - """Test that we can filter evidence with dockeys.""" - doc_path = "example2.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs() - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - # add with new dockey - with open("example.txt", "w", encoding="utf-8") as f: - f.write(r.text) - f.write("\n") # so we don't have same hash - docs.add("example.txt", "WikiMedia Foundation, 2023, Accessed now", dockey="test") # type: ignore[arg-type] - answer = Answer(question="What country is Bates from?", dockey_filter=["test"]) - docs.get_evidence(answer) - - -def test_dockey_delete(): - """Test that we can filter evidence with dockeys.""" - doc_path = "example2.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) + doc_path = Path(os.path.abspath(__file__)) + settings = Settings.from_name("fast") docs = Docs() - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - # add with new dockey - with open("example.txt", "w", encoding="utf-8") as f: - f.write(r.text) - f.write("\n\nBates could be from Angola") # so we don't have same hash - docs.add("example.txt", "WikiMedia Foundation, 2023, Accessed now", docname="test") # type: ignore[arg-type] - answer = Answer(question="What country was Bates born in?") - answer = docs.get_evidence( - answer, max_sources=25, k=30 - ) # we just have a lot so we get both docs - keys = {c.text.doc.dockey for c in answer.contexts} - assert len(keys) == 2 - assert len(docs.docs) == 2 - - docs.delete(docname="test") + docs.add(doc_path, "test_paperqa.py", docname="test_paperqa.py", disable_check=True) assert len(docs.docs) == 1 - assert len(list(filter(lambda x: x.doc.dockey == "test", docs.texts))) == 0 - - answer = Answer(question="What country was Bates born in?") - assert len(docs.docs) == 1 - assert len(docs.deleted_dockeys) == 1 - answer = docs.get_evidence(answer, max_sources=25, k=30) - keys = {c.text.doc.dockey for c in answer.contexts} - assert len(keys) == 1 - - -def test_query_filter(): - """Test that we can filter evidence with in query.""" - doc_path = "example2.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs = Docs() - docs.add( - doc_path, # type: ignore[arg-type] - "Information about Fredrick Bates, WikiMedia Foundation, 2023, Accessed now", + assert ( + "test_paperqa.py" + in docs.query("What file is read in by test_code?", settings=settings).answer ) - # add with new dockey - with open("example.txt", "w", encoding="utf-8") as f: - f.write(r.text) - f.write("\n") # so we don't have same hash - docs.add("example.txt", "WikiMedia Foundation, 2023, Accessed now", dockey="test") # type: ignore[arg-type] - docs.query("What country is Bates from?", key_filter=True) - # the filter shouldn't trigger, so just checking that it doesn't crash def test_zotero() -> None: @@ -1629,235 +941,76 @@ def test_zotero() -> None: Docs() with contextlib.suppress(ValueError): # Close enough - ZoteroDB(library_type="user") # "group" if group library + ZoteroDB() # "group" if group library -def test_too_much_evidence(): - doc_path = "example2.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get("https://en.wikipedia.org/wiki/Barack_Obama") # noqa: S113 - f.write(r.text) - docs = Docs(llm="gpt-4o-mini", summary_llm="gpt-4o-mini") - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] +def test_too_much_evidence(stub_data_dir: Path, stub_data_dir_w_near_dupes) -> None: + doc_path = stub_data_dir / "obama.txt" + mini_settings = Settings(llm="gpt-4o-mini", summary_llm="gpt-4o-mini") + docs = Docs() + docs.add( + doc_path, "WikiMedia Foundation, 2023, Accessed now", settings=mini_settings + ) # add with new dockey - with open("example.txt", "w", encoding="utf-8") as f: - f.write(r.text) - f.write("\n") # so we don't have same hash docs.add( - "example.txt", # type: ignore[arg-type] + stub_data_dir_w_near_dupes / "obama_modified.txt", "WikiMedia Foundation, 2023, Accessed now", - dockey="test", - chunk_chars=4000, + settings=mini_settings, ) - docs.query("What is Barrack's greatest accomplishment?", max_sources=10, k=10) + settings = Settings.from_name("fast") + settings.answer.evidence_k = 10 + settings.answer.answer_max_sources = 10 + docs.query("What is Barrack's greatest accomplishment?", settings=settings) -def test_custom_prompts(): +def test_custom_prompts(stub_data_dir: Path) -> None: my_qaprompt = ( - "Answer the question '{question}' " - "using the country name alone. For example: " - "A: United States\nA: Canada\nA: Mexico\n\n Using the context:\n\n{context}\n\nA: " + "Answer the question '{question}' using the country name alone. For example: A:" + " United States\nA: Canada\nA: Mexico\n\n Using the" + " context:\n\n{context}\n\nA: " ) - - docs = Docs(prompts=PromptCollection(qa=my_qaprompt)) - - doc_path = "example.html" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - answer = docs.query("What country is Frederick Bates from?") + settings = Settings.from_name("fast") + settings.prompts.qa = my_qaprompt + docs = Docs() + docs.add(stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now") + answer = docs.query("What country is Frederick Bates from?", settings=settings) assert "United States" in answer.answer -def test_pre_prompt(): - pre = "Provide context you have memorized that could help answer '{question}'. " - - docs = Docs(prompts=PromptCollection(pre=pre)) - - doc_path = "example.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - docs.query("What country is Bates from?") - - -def test_post_prompt(): - post = ( - "We are trying to answer the question below " - "and have an answer provided. " - "Please edit the answer be extremely terse, with no extra words or formatting" - "with no extra information.\n\n" - "Q: {question}\nA: {answer}\n\n" - ) - - docs = Docs(prompts=PromptCollection(post=post)) - - doc_path = "example.txt" - with open(doc_path, "w", encoding="utf-8") as f: - # get wiki page about politician - r = requests.get( # noqa: S113 - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)" - ) - f.write(r.text) - docs.add(doc_path, "WikiMedia Foundation, 2023, Accessed now") # type: ignore[arg-type] - docs.query("What country is Bates from?") - - docs = Docs( - prompts=PromptCollection( - system="Answer all questions with as few words as possible" - ) +def test_pre_prompt(stub_data_dir: Path) -> None: + pre = ( + "What is water's boiling point in Fahrenheit? Please respond with a complete" + " sentence." ) - docs.query("What country is Bates from?") - -@pytest.mark.skip("TODO: bring this back") -@no_type_check # TODO: remove this when restored -def test_memory() -> None: - # Not sure why, but gpt-3.5 cannot do this anymore. - docs = Docs(memory=True, k=3, max_sources=1, llm="gpt-4", key_filter=False) - docs.add_url( - "https://en.wikipedia.org/wiki/Red_Army", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - answer1 = docs.query("When did the Soviet Union and Japan agree to a cease-fire?") - assert answer1.memory is not None - assert "1939" in answer1.answer - assert "Answer" in docs.memory_model.load_memory_variables({})["memory"] - answer2 = docs.query("When was the conflict resolved?") - assert "1941" in answer2.answer or "1945" in answer2.answer - assert answer2.memory is not None - assert "Answer" in docs.memory_model.load_memory_variables({})["memory"] - print(answer2.answer) - - docs.clear_memory() - - answer3 = docs.query("When was the conflict resolved?") - assert answer3.memory is not None + settings = Settings.from_name("fast") + settings.prompts.pre = pre + docs = Docs() + docs.add(stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now") + assert "212" not in docs.query("What is the boiling point of water?").answer assert ( - "I cannot answer" in answer3.answer - or "insufficient" in answer3.answer - or "does not provide" in answer3.answer - or "ambiguous" in answer3.answer - ) - - -def test_add_texts(): - llm_config = {"temperature": 0.1, "model": "babbage-02"} - # want to use sparse embedding because - # we get some randomness in the OpenAI embeddings - docs = Docs( - llm_model=OpenAILLMModel(config=llm_config), - docs_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - embedding_client=None, - ) - - docs.add_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - - docs2 = Docs( - docs_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - embedding_client=None, - ) - texts = [Text(**dict(t)) for t in docs.texts] - for t in texts: - t.embedding = None - docs2.add_texts(texts, next(iter(docs.docs.values()))) - - for t1, t2 in zip(docs2.texts, docs.texts): - assert t1.text == t2.text - assert t1.embedding is not None - assert t2.embedding is not None - assert np.allclose(t1.embedding, t2.embedding, atol=1e-3) - docs2._build_texts_index() - # now do it again to test after text index is already built - llm_config = {"temperature": 0.1, "model": "babbage-02"} - docs = Docs( - llm_model=OpenAILLMModel(config=llm_config), - docs_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - texts_index=NumpyVectorStore(embedding_model=SparseEmbeddingModel()), - embedding_client=None, + "212" + in docs.query("What is the boiling point of water?", settings=settings).answer ) - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test3", - ) - - texts = [Text(**dict(t)) for t in docs.texts] - for t in texts: - t.embedding = None - docs2.add_texts(texts, next(iter(docs.docs.values()))) - assert len(docs2.docs) == 2 - docs2.query("What country was Bates Born in?") - -def test_external_doc_index(): +def test_post_prompt(stub_data_dir: Path) -> None: + post = "The opposite of down is" + settings = Settings.from_name("fast") + settings.prompts.post = post docs = Docs() - docs.add_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", - citation="WikiMedia Foundation, 2023, Accessed now", - dockey="test", - ) - evidence = docs.query(query="What is the date of flag day?", key_filter=True) - docs2 = Docs(docs_index=docs.docs_index, texts_index=docs.texts_index) - assert len(docs2.docs) == 0 - evidence = docs2.query("What is the date of flag day?", key_filter=True) - assert "February 15" in evidence.context + docs.add(stub_data_dir / "bates.txt", "WikiMedia Foundation, 2023, Accessed now") + response = docs.query("What country is Bates from?", settings=settings) + assert "up" in response.answer.lower() -def test_embedding_name_consistency(): +def test_external_doc_index(stub_data_dir: Path) -> None: docs = Docs() - assert docs.embedding == "text-embedding-3-small" - assert docs.texts_index.embedding_model.name == "text-embedding-3-small" - docs = Docs(embedding="langchain") - assert docs.embedding == "langchain" - assert docs.texts_index.embedding_model.name == "langchain" - assert isinstance(docs.texts_index.embedding_model, LangchainEmbeddingModel) - docs = Docs(embedding="foo") - assert docs.embedding == "foo" - assert isinstance(docs.texts_index.embedding_model, OpenAIEmbeddingModel) - docs = Docs( - texts_index=NumpyVectorStore(embedding_model=OpenAIEmbeddingModel(name="test")) - ) - assert docs.embedding == "test" - - docs = Docs(embedding="hybrid-text-embedding-3-small") - assert isinstance(docs.docs_index.embedding_model, HybridEmbeddingModel) - assert docs.docs_index.embedding_model.models[0].name == "text-embedding-3-small" - assert docs.docs_index.embedding_model.models[1].name == "sparse" - - -def test_external_texts_index(): - docs = Docs(jit_texts_index=True) - docs.add_url( - "https://en.wikipedia.org/wiki/National_Flag_of_Canada_Day", - citation="Flag Day of Canada, WikiMedia Foundation, 2023, Accessed now", - ) - answer = docs.query(query="On which date is flag day annually observed?") - assert "February 15" in answer.answer - - docs.add_url( - "https://en.wikipedia.org/wiki/Frederick_Bates_(politician)", - citation="Fredrick Bates, WikiMedia Foundation, 2023, Accessed now", - ) - - answer = docs.query( - query="On which date is flag day annually observed?", key_filter=True + docs.add( + stub_data_dir / "flag_day.html", "WikiMedia Foundation, 2023, Accessed now" ) - assert "February 15" in answer.answer + # force embedding + _ = docs.get_evidence(query="What is the date of flag day?") + docs2 = Docs(texts_index=docs.texts_index) + assert not docs2.docs + assert docs2.get_evidence("What is the date of flag day?").contexts diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..f3de9dac --- /dev/null +++ b/uv.lock @@ -0,0 +1,2358 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f7/22bba300a16fd1cad99da1a23793fe43963ee326d012fdf852d0b4035955/aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", size = 16786 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/b6/58ea188899950d759a837f9a58b2aee1d1a380ea4d6211ce9b1823748851/aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd", size = 12155 }, +] + +[[package]] +name = "aiohttp" +version = "3.10.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/28/ca549838018140b92a19001a8628578b0f2a3b38c16826212cc6f706e6d4/aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", size = 7524360 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/90/54ccb1e4eadfb6c95deff695582453f6208584431d69bf572782e9ae542b/aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", size = 586455 }, + { url = "https://files.pythonhosted.org/packages/c3/7a/95e88c02756e7e718f054e1bb3ec6ad5d0ee4a2ca2bb1768c5844b3de30a/aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", size = 397255 }, + { url = "https://files.pythonhosted.org/packages/07/4f/767387b39990e1ee9aba8ce642abcc286d84d06e068dc167dab983898f18/aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", size = 388973 }, + { url = "https://files.pythonhosted.org/packages/61/46/0df41170a4d228c07b661b1ba9d87101d99a79339dc93b8b1183d8b20545/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77", size = 1326126 }, + { url = "https://files.pythonhosted.org/packages/af/20/da0d65e07ce49d79173fed41598f487a0a722e87cfbaa8bb7e078a7c1d39/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061", size = 1364538 }, + { url = "https://files.pythonhosted.org/packages/aa/20/b59728405114e57541ba9d5b96033e69d004e811ded299537f74237629ca/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697", size = 1399896 }, + { url = "https://files.pythonhosted.org/packages/2a/92/006690c31b830acbae09d2618e41308fe4c81c0679b3b33a3af859e0b7bf/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7", size = 1312914 }, + { url = "https://files.pythonhosted.org/packages/d4/71/1a253ca215b6c867adbd503f1e142117527ea8775e65962bc09b2fad1d2c/aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0", size = 1271301 }, + { url = "https://files.pythonhosted.org/packages/0a/ab/5d1d9ff9ce6cce8fa54774d0364e64a0f3cd50e512ff09082ced8e5217a1/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5", size = 1291652 }, + { url = "https://files.pythonhosted.org/packages/75/5f/f90510ea954b9ae6e7a53d2995b97a3e5c181110fdcf469bc9238445871d/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e", size = 1286289 }, + { url = "https://files.pythonhosted.org/packages/be/9e/1f523414237798660921817c82b9225a363af436458caf584d2fa6a2eb4a/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1", size = 1341848 }, + { url = "https://files.pythonhosted.org/packages/f6/36/443472ddaa85d7d80321fda541d9535b23ecefe0bf5792cc3955ea635190/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277", size = 1361619 }, + { url = "https://files.pythonhosted.org/packages/19/f6/3ecbac0bc4359c7d7ba9e85c6b10f57e20edaf1f97751ad2f892db231ad0/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058", size = 1320869 }, + { url = "https://files.pythonhosted.org/packages/34/7e/ed74ffb36e3a0cdec1b05d8fbaa29cb532371d5a20058b3a8052fc90fe7c/aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072", size = 359271 }, + { url = "https://files.pythonhosted.org/packages/98/1b/718901f04bc8c886a742be9e83babb7b93facabf7c475cc95e2b3ab80b4d/aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff", size = 379143 }, + { url = "https://files.pythonhosted.org/packages/d9/1c/74f9dad4a2fc4107e73456896283d915937f48177b99867b63381fadac6e/aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", size = 583468 }, + { url = "https://files.pythonhosted.org/packages/12/29/68d090551f2b58ce76c2b436ced8dd2dfd32115d41299bf0b0c308a5483c/aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", size = 394066 }, + { url = "https://files.pythonhosted.org/packages/8f/f7/971f88b4cdcaaa4622925ba7d86de47b48ec02a9040a143514b382f78da4/aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", size = 389098 }, + { url = "https://files.pythonhosted.org/packages/f1/5a/fe3742efdce551667b2ddf1158b27c5b8eb1edc13d5e14e996e52e301025/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", size = 1332742 }, + { url = "https://files.pythonhosted.org/packages/1a/52/a25c0334a1845eb4967dff279151b67ca32a948145a5812ed660ed900868/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", size = 1372134 }, + { url = "https://files.pythonhosted.org/packages/96/3d/33c1d8efc2d8ec36bff9a8eca2df9fdf8a45269c6e24a88e74f2aa4f16bd/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", size = 1414413 }, + { url = "https://files.pythonhosted.org/packages/64/74/0f1ddaa5f0caba1d946f0dd0c31f5744116e4a029beec454ec3726d3311f/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", size = 1328107 }, + { url = "https://files.pythonhosted.org/packages/0a/32/c10118f0ad50e4093227234f71fd0abec6982c29367f65f32ee74ed652c4/aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", size = 1280126 }, + { url = "https://files.pythonhosted.org/packages/c6/c9/77e3d648d97c03a42acfe843d03e97be3c5ef1b4d9de52e5bd2d28eed8e7/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", size = 1292660 }, + { url = "https://files.pythonhosted.org/packages/7e/5d/99c71f8e5c8b64295be421b4c42d472766b263a1fe32e91b64bf77005bf2/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", size = 1300988 }, + { url = "https://files.pythonhosted.org/packages/8f/2c/76d2377dd947f52fbe8afb19b18a3b816d66c7966755c04030f93b1f7b2d/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", size = 1339268 }, + { url = "https://files.pythonhosted.org/packages/fd/e6/3d9d935cc705d57ed524d82ec5d6b678a53ac1552720ae41282caa273584/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", size = 1366993 }, + { url = "https://files.pythonhosted.org/packages/fe/c2/f7eed4d602f3f224600d03ab2e1a7734999b0901b1c49b94dc5891340433/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", size = 1329459 }, + { url = "https://files.pythonhosted.org/packages/ce/8f/27f205b76531fc592abe29e1ad265a16bf934a9f609509c02d765e6a8055/aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", size = 356968 }, + { url = "https://files.pythonhosted.org/packages/39/8c/4f6c0b2b3629f6be6c81ab84d9d577590f74f01d4412bfc4067958eaa1e1/aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", size = 377650 }, + { url = "https://files.pythonhosted.org/packages/7b/b9/03b4327897a5b5d29338fa9b514f1c2f66a3e4fc88a4e40fad478739314d/aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", size = 576994 }, + { url = "https://files.pythonhosted.org/packages/67/1b/20c2e159cd07b8ed6dde71c2258233902fdf415b2fe6174bd2364ba63107/aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", size = 390684 }, + { url = "https://files.pythonhosted.org/packages/4d/6b/ff83b34f157e370431d8081c5d1741963f4fb12f9aaddb2cacbf50305225/aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", size = 386176 }, + { url = "https://files.pythonhosted.org/packages/4d/a1/6e92817eb657de287560962df4959b7ddd22859c4b23a0309e2d3de12538/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", size = 1303310 }, + { url = "https://files.pythonhosted.org/packages/04/29/200518dc7a39c30ae6d5bc232d7207446536e93d3d9299b8e95db6e79c54/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", size = 1340445 }, + { url = "https://files.pythonhosted.org/packages/8e/20/53f7bba841ba7b5bb5dea580fea01c65524879ba39cb917d08c845524717/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", size = 1385121 }, + { url = "https://files.pythonhosted.org/packages/f1/b4/d99354ad614c48dd38fb1ee880a1a54bd9ab2c3bcad3013048d4a1797d3a/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", size = 1299669 }, + { url = "https://files.pythonhosted.org/packages/51/39/ca1de675f2a5729c71c327e52ac6344e63f036bd37281686ae5c3fb13bfb/aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", size = 1252638 }, + { url = "https://files.pythonhosted.org/packages/54/cf/a3ae7ff43138422d477348e309ef8275779701bf305ff6054831ef98b782/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", size = 1266889 }, + { url = "https://files.pythonhosted.org/packages/6e/7a/c6027ad70d9fb23cf254a26144de2723821dade1a624446aa22cd0b6d012/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", size = 1266249 }, + { url = "https://files.pythonhosted.org/packages/64/fd/ed136d46bc2c7e3342fed24662b4827771d55ceb5a7687847aae977bfc17/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", size = 1311036 }, + { url = "https://files.pythonhosted.org/packages/76/9a/43eeb0166f1119256d6f43468f900db1aed7fbe32069d2a71c82f987db4d/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", size = 1338756 }, + { url = "https://files.pythonhosted.org/packages/d5/bc/d01ff0810b3f5e26896f76d44225ed78b088ddd33079b85cd1a23514318b/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", size = 1299976 }, + { url = "https://files.pythonhosted.org/packages/3e/c9/50a297c4f7ab57a949f4add2d3eafe5f3e68bb42f739e933f8b32a092bda/aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", size = 355609 }, + { url = "https://files.pythonhosted.org/packages/65/28/aee9d04fb0b3b1f90622c338a08e54af5198e704a910e20947c473298fd0/aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", size = 375697 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/e3/c4c8d473d6780ef1853d630d581f70d655b4f8d7553c6997958c283039a2/anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94", size = 163930 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/a2/10639a79341f6c019dedc95bd48a4928eed9f1d1197f4c04f546fc7ae0ff/anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7", size = 86780 }, +] + +[[package]] +name = "astroid" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348 }, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, +] + +[[package]] +name = "attrs" +version = "24.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, +] + +[[package]] +name = "bibtexparser" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/06/f710a276e4e018b02fe17b39313629213a43c365401179bc437e0ab24425/bibtexparser-1.4.1.tar.gz", hash = "sha256:e00e29e24676c4808e0b4333b37bb55cca9cbb7871a56f63058509281588d789", size = 55102 } + +[[package]] +name = "build" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "os_name == 'nt'" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/bb/4a1b7e3a7520e310cf7bfece43788071604e1ccf693a7f0c4638c59068d6/build-1.2.2.tar.gz", hash = "sha256:119b2fb462adef986483438377a13b2f42064a2a3a4161f24a0cca698a07ac8c", size = 46516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/fd/e4bda6228637ecae5732162b5ac2a5a822e2ba8e546eb4997cde51b231a3/build-1.2.2-py3-none-any.whl", hash = "sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613", size = 22823 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "contourpy" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/f6/31a8f28b4a2a4fa0e01085e542f3081ab0588eff8e589d39d775172c9792/contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", size = 13464370 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/1f/9375917786cb39270b0ee6634536c0e22abf225825602688990d8f5c6c19/contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad", size = 266356 }, + { url = "https://files.pythonhosted.org/packages/05/46/9256dd162ea52790c127cb58cfc3b9e3413a6e3478917d1f811d420772ec/contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49", size = 250915 }, + { url = "https://files.pythonhosted.org/packages/e1/5d/3056c167fa4486900dfbd7e26a2fdc2338dc58eee36d490a0ed3ddda5ded/contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66", size = 310443 }, + { url = "https://files.pythonhosted.org/packages/ca/c2/1a612e475492e07f11c8e267ea5ec1ce0d89971be496c195e27afa97e14a/contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081", size = 348548 }, + { url = "https://files.pythonhosted.org/packages/45/cf/2c2fc6bb5874158277b4faf136847f0689e1b1a1f640a36d76d52e78907c/contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1", size = 319118 }, + { url = "https://files.pythonhosted.org/packages/03/33/003065374f38894cdf1040cef474ad0546368eea7e3a51d48b8a423961f8/contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d", size = 323162 }, + { url = "https://files.pythonhosted.org/packages/42/80/e637326e85e4105a802e42959f56cff2cd39a6b5ef68d5d9aee3ea5f0e4c/contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c", size = 1265396 }, + { url = "https://files.pythonhosted.org/packages/7c/3b/8cbd6416ca1bbc0202b50f9c13b2e0b922b64be888f9d9ee88e6cfabfb51/contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb", size = 1324297 }, + { url = "https://files.pythonhosted.org/packages/4d/2c/021a7afaa52fe891f25535506cc861c30c3c4e5a1c1ce94215e04b293e72/contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c", size = 171808 }, + { url = "https://files.pythonhosted.org/packages/8d/2f/804f02ff30a7fae21f98198828d0857439ec4c91a96e20cf2d6c49372966/contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67", size = 217181 }, + { url = "https://files.pythonhosted.org/packages/c9/92/8e0bbfe6b70c0e2d3d81272b58c98ac69ff1a4329f18c73bd64824d8b12e/contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f", size = 267838 }, + { url = "https://files.pythonhosted.org/packages/e3/04/33351c5d5108460a8ce6d512307690b023f0cfcad5899499f5c83b9d63b1/contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6", size = 251549 }, + { url = "https://files.pythonhosted.org/packages/51/3d/aa0fe6ae67e3ef9f178389e4caaaa68daf2f9024092aa3c6032e3d174670/contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639", size = 303177 }, + { url = "https://files.pythonhosted.org/packages/56/c3/c85a7e3e0cab635575d3b657f9535443a6f5d20fac1a1911eaa4bbe1aceb/contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c", size = 341735 }, + { url = "https://files.pythonhosted.org/packages/dd/8d/20f7a211a7be966a53f474bc90b1a8202e9844b3f1ef85f3ae45a77151ee/contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06", size = 314679 }, + { url = "https://files.pythonhosted.org/packages/6e/be/524e377567defac0e21a46e2a529652d165fed130a0d8a863219303cee18/contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09", size = 320549 }, + { url = "https://files.pythonhosted.org/packages/0f/96/fdb2552a172942d888915f3a6663812e9bc3d359d53dafd4289a0fb462f0/contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd", size = 1263068 }, + { url = "https://files.pythonhosted.org/packages/2a/25/632eab595e3140adfa92f1322bf8915f68c932bac468e89eae9974cf1c00/contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35", size = 1322833 }, + { url = "https://files.pythonhosted.org/packages/73/e3/69738782e315a1d26d29d71a550dbbe3eb6c653b028b150f70c1a5f4f229/contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb", size = 172681 }, + { url = "https://files.pythonhosted.org/packages/0c/89/9830ba00d88e43d15e53d64931e66b8792b46eb25e2050a88fec4a0df3d5/contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b", size = 218283 }, + { url = "https://files.pythonhosted.org/packages/53/a1/d20415febfb2267af2d7f06338e82171824d08614084714fb2c1dac9901f/contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3", size = 267879 }, + { url = "https://files.pythonhosted.org/packages/aa/45/5a28a3570ff6218d8bdfc291a272a20d2648104815f01f0177d103d985e1/contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7", size = 251573 }, + { url = "https://files.pythonhosted.org/packages/39/1c/d3f51540108e3affa84f095c8b04f0aa833bb797bc8baa218a952a98117d/contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84", size = 303184 }, + { url = "https://files.pythonhosted.org/packages/00/56/1348a44fb6c3a558c1a3a0cd23d329d604c99d81bf5a4b58c6b71aab328f/contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0", size = 340262 }, + { url = "https://files.pythonhosted.org/packages/2b/23/00d665ba67e1bb666152131da07e0f24c95c3632d7722caa97fb61470eca/contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b", size = 313806 }, + { url = "https://files.pythonhosted.org/packages/5a/42/3cf40f7040bb8362aea19af9a5fb7b32ce420f645dd1590edcee2c657cd5/contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da", size = 319710 }, + { url = "https://files.pythonhosted.org/packages/05/32/f3bfa3fc083b25e1a7ae09197f897476ee68e7386e10404bdf9aac7391f0/contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14", size = 1264107 }, + { url = "https://files.pythonhosted.org/packages/1c/1e/1019d34473a736664f2439542b890b2dc4c6245f5c0d8cdfc0ccc2cab80c/contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8", size = 1322458 }, + { url = "https://files.pythonhosted.org/packages/22/85/4f8bfd83972cf8909a4d36d16b177f7b8bdd942178ea4bf877d4a380a91c/contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294", size = 172643 }, + { url = "https://files.pythonhosted.org/packages/cc/4a/fb3c83c1baba64ba90443626c228ca14f19a87c51975d3b1de308dd2cf08/contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087", size = 218301 }, + { url = "https://files.pythonhosted.org/packages/76/65/702f4064f397821fea0cb493f7d3bc95a5d703e20954dce7d6d39bacf378/contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8", size = 278972 }, + { url = "https://files.pythonhosted.org/packages/80/85/21f5bba56dba75c10a45ec00ad3b8190dbac7fd9a8a8c46c6116c933e9cf/contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b", size = 263375 }, + { url = "https://files.pythonhosted.org/packages/0a/64/084c86ab71d43149f91ab3a4054ccf18565f0a8af36abfa92b1467813ed6/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973", size = 307188 }, + { url = "https://files.pythonhosted.org/packages/3d/ff/d61a4c288dc42da0084b8d9dc2aa219a850767165d7d9a9c364ff530b509/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18", size = 345644 }, + { url = "https://files.pythonhosted.org/packages/ca/aa/00d2313d35ec03f188e8f0786c2fc61f589306e02fdc158233697546fd58/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8", size = 317141 }, + { url = "https://files.pythonhosted.org/packages/8d/6a/b5242c8cb32d87f6abf4f5e3044ca397cb1a76712e3fa2424772e3ff495f/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6", size = 323469 }, + { url = "https://files.pythonhosted.org/packages/6f/a6/73e929d43028a9079aca4bde107494864d54f0d72d9db508a51ff0878593/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2", size = 1260894 }, + { url = "https://files.pythonhosted.org/packages/2b/1e/1e726ba66eddf21c940821df8cf1a7d15cb165f0682d62161eaa5e93dae1/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", size = 1314829 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "distlib" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/91/e2df406fb4efacdf46871c25cde65d3c6ee5e173b7e5a4547a47bae91920/distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64", size = 609931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "dm-tree" +version = "0.1.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/6d/f1997aac42e0f550c1e952a0b920eaa0bfc4d27d0421499881b934b969fc/dm-tree-0.1.8.tar.gz", hash = "sha256:0fcaabbb14e7980377439e7140bd05552739ca5e515ecb3119f234acee4b9430", size = 35384 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/64/901b324804793743f0fdc9e47db893bf0ded9e074850fab2440af330fe83/dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad16ceba90a56ec47cf45b21856d14962ac314787975ef786efb5e6e9ca75ec7", size = 167628 }, + { url = "https://files.pythonhosted.org/packages/b1/65/4f10a68dde5fa0c91043c9c899e9bc79b1657ba932d39a5f8525c0058e68/dm_tree-0.1.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:803bfc53b4659f447ac694dbd04235f94a73ef7c1fd1e0df7c84ac41e0bc963b", size = 115351 }, + { url = "https://files.pythonhosted.org/packages/08/e2/4c29cb9876456517f21979ddcbb6048f28a3b52c61aa9d14d42adafcdca4/dm_tree-0.1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:378cc8ad93c5fe3590f405a309980721f021c790ca1bdf9b15bb1d59daec57f5", size = 110661 }, + { url = "https://files.pythonhosted.org/packages/fe/89/386332bbd7567c4ccc13aa2e58f733237503fc75fb389955d3b06b9fb967/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1607ce49aa42f010d1e5e616d92ce899d66835d4d8bea49679582435285515de", size = 146727 }, + { url = "https://files.pythonhosted.org/packages/a3/e7/b0c04ea5af82c19fd5984bfe980f4012601c4708634c7c51a952b17c93b2/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343a4a4ebaa127451ff971254a4be4084eb4bdc0b2513c32b46f6f728fd03f9e", size = 174689 }, + { url = "https://files.pythonhosted.org/packages/13/0d/09a4ecb54c03db53d9eb5bbc81609d89de26e3762743f003282c1b48debb/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa42a605d099ee7d41ba2b5fb75e21423951fd26e5d50583a00471238fb3021d", size = 150338 }, + { url = "https://files.pythonhosted.org/packages/4a/27/c5e3580a952a07e5a1428ae952874796870dc8db789f3d774e886160a9f4/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b7764de0d855338abefc6e3ee9fe40d301668310aa3baea3f778ff051f4393", size = 152800 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/522041457444b67125ac9527208bb3148f63d7dce0a86ffa589ec763a10e/dm_tree-0.1.8-cp311-cp311-win_amd64.whl", hash = "sha256:a5d819c38c03f0bb5b3b3703c60e4b170355a0fc6b5819325bf3d4ceb3ae7e80", size = 101336 }, + { url = "https://files.pythonhosted.org/packages/72/2c/e33dfc96f974ae3cba82c9836371c93fcb4d59d5a82ebb853861618a0b0b/dm_tree-0.1.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ea9e59e0451e7d29aece402d9f908f2e2a80922bcde2ebfd5dcb07750fcbfee8", size = 169495 }, + { url = "https://files.pythonhosted.org/packages/17/af/4030827253a5d50eb8da6f7189bc33d3c850c4109cf3414910e9af677cb7/dm_tree-0.1.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:94d3f0826311f45ee19b75f5b48c99466e4218a0489e81c0f0167bda50cacf22", size = 116525 }, + { url = "https://files.pythonhosted.org/packages/10/10/5f9eed00b1186921e447960443f03cda6374cba8cd5cf7aff2b42ecb8a0e/dm_tree-0.1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:435227cf3c5dc63f4de054cf3d00183790bd9ead4c3623138c74dde7f67f521b", size = 111436 }, + { url = "https://files.pythonhosted.org/packages/4a/da/3d3d04f7a572f7649f48edc9402ff5836e2f90e18445ffde110fd6142889/dm_tree-0.1.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09964470f76a5201aff2e8f9b26842976de7889300676f927930f6285e256760", size = 146828 }, + { url = "https://files.pythonhosted.org/packages/c4/12/0a8c2152655ca39c1059c762ea1dc12784166c735126eb0ab929c518ef4e/dm_tree-0.1.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75c5d528bb992981c20793b6b453e91560784215dffb8a5440ba999753c14ceb", size = 175054 }, + { url = "https://files.pythonhosted.org/packages/c9/d4/8cbb857612ca69763ee4f4f97c7b91659df1d373d62237cb9c772e55ae97/dm_tree-0.1.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a94aba18a35457a1b5cd716fd7b46c5dafdc4cf7869b4bae665b91c4682a8e", size = 152834 }, + { url = "https://files.pythonhosted.org/packages/ad/e3/96f5267fe5a47c882dce7f3d06b26ddd756681fc4fbedd55d51b78b08bca/dm_tree-0.1.8-cp312-cp312-win_amd64.whl", hash = "sha256:96a548a406a6fb15fe58f6a30a57ff2f2aafbf25f05afab00c8f5e5977b6c715", size = 101754 }, +] + +[[package]] +name = "docstring-parser" +version = "0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, +] + +[[package]] +name = "execnet" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, +] + +[[package]] +name = "executing" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 }, +] + +[[package]] +name = "feedparser" +version = "6.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sgmllib3k" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343 }, +] + +[[package]] +name = "fhaviary" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/35/5b7ba2243c59c00c11155ff0940a3beaef01c9115cafb11260acac5ab006/fhaviary-0.6.0.tar.gz", hash = "sha256:172803e5a5e49dfd0dab06589ca27a3b8840b30c0d1279b5cd9a0d3f4bf65555", size = 155037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/bf/0a0f0bbab7e6f30483864c3952050a9c76c1b70cd895d169619ec75f1a77/fhaviary-0.6.0-py3-none-any.whl", hash = "sha256:b766d7187a23c6ac043cda093bce6b5ecfb77f6a4f4256d4f0fb9c1571a0fc28", size = 31570 }, +] + +[package.optional-dependencies] +llm = [ + { name = "litellm" }, +] + +[[package]] +name = "filelock" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/76/3981447fd369539aba35797db99a8e2ff7ed01d9aa63e9344a31658b8d81/filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec", size = 18008 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/95/f9310f35376024e1086c59cbb438d319fc9a4ef853289ce7c661539edbd4/filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609", size = 16170 }, +] + +[[package]] +name = "fonttools" +version = "4.53.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/cb/cd80a0da995adde8ade6044a8744aee0da5efea01301cadf770f7fbe7dcc/fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4", size = 3452797 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/6a/206391c869ab22d1374e2575cad7cab36b93b9e3d37f48f4696eed2c6e9e/fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1", size = 2762654 }, + { url = "https://files.pythonhosted.org/packages/f5/7e/4060d88dbfaf446e1c9f0fe9cf13dba36ba47c4da85ce5c1df084ce47e7d/fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923", size = 2247865 }, + { url = "https://files.pythonhosted.org/packages/e1/67/fff766817e17d67208f8a1e72de15066149485acb5e4ff0816b11fd5fca3/fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719", size = 4873046 }, + { url = "https://files.pythonhosted.org/packages/a4/22/0a0ad59d9367997fd74a00ad2e88d10559122e09f105e94d34c155aecc0a/fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3", size = 4920859 }, + { url = "https://files.pythonhosted.org/packages/0b/c4/b4e2f1699a5e2244373a6e8175f862f49f377b444adc6c7b1fe1f5b3d04d/fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb", size = 4885904 }, + { url = "https://files.pythonhosted.org/packages/64/e7/b9a07c386adf8ad0348163fbcaab74daed6ef18ddb3f49b61b5c19900aeb/fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2", size = 5054708 }, + { url = "https://files.pythonhosted.org/packages/e9/53/2a79462ae38d7943e63290209c04fef89677c67b29cb329cdc549c18d4d5/fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88", size = 2158885 }, + { url = "https://files.pythonhosted.org/packages/c8/e1/059700c154bd7170d1c37061239836d2e51ff608f47075450f06dd3c292a/fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02", size = 2205133 }, + { url = "https://files.pythonhosted.org/packages/87/63/8271f50f3e7bff8b78e03914c4c2893f2f21bd4db2975c60d11ecfbdd174/fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58", size = 2756146 }, + { url = "https://files.pythonhosted.org/packages/dd/bd/cb8fd2dddd68089c112bf42a88afe188b8ace73f94406539857dcc9347a6/fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8", size = 2244990 }, + { url = "https://files.pythonhosted.org/packages/ae/71/2b9761e25697bdaf3dfe8269541bd4324f3eb0e4cc13f71d7f90cd272394/fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60", size = 4787604 }, + { url = "https://files.pythonhosted.org/packages/db/2b/5779cfd48625e013c2dfcf0c246474d5b1f5d061a5f1e476037bf9fff3a3/fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f", size = 4871141 }, + { url = "https://files.pythonhosted.org/packages/b8/3d/ac3cec35a503bf789d03e9d155a220c9e574f4f1573f00a3bea55695d535/fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2", size = 4764714 }, + { url = "https://files.pythonhosted.org/packages/ac/9f/27135ac0328e22cca1ba23ee6a1a1f971c13e9f0387adc5598d4635c501d/fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f", size = 5023568 }, + { url = "https://files.pythonhosted.org/packages/04/40/44d6a94e52e91fe104f9ca95944466af34828992cbc66b666f541de137f1/fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670", size = 2147572 }, + { url = "https://files.pythonhosted.org/packages/6d/9a/b695930e1b4e6929cc60e294489421632a05c105ac8c56ee63ef56a47872/fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab", size = 2193313 }, + { url = "https://files.pythonhosted.org/packages/e4/b9/0394d67056d4ad36a3807b439571934b318f1df925593a95e9ec0516b1a7/fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d", size = 1090472 }, +] + +[[package]] +name = "frozenlist" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/3d/2102257e7acad73efc4a0c306ad3953f68c504c16982bbdfee3ad75d8085/frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", size = 37820 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/bc/8d33f2d84b9368da83e69e42720cff01c5e199b5a868ba4486189a4d8fa9/frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", size = 97060 }, + { url = "https://files.pythonhosted.org/packages/af/b2/904500d6a162b98a70e510e743e7ea992241b4f9add2c8063bf666ca21df/frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", size = 55347 }, + { url = "https://files.pythonhosted.org/packages/5b/9c/f12b69997d3891ddc0d7895999a00b0c6a67f66f79498c0e30f27876435d/frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", size = 53374 }, + { url = "https://files.pythonhosted.org/packages/ac/6e/e0322317b7c600ba21dec224498c0c5959b2bce3865277a7c0badae340a9/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", size = 273288 }, + { url = "https://files.pythonhosted.org/packages/a7/76/180ee1b021568dad5b35b7678616c24519af130ed3fa1e0f1ed4014e0f93/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", size = 284737 }, + { url = "https://files.pythonhosted.org/packages/05/08/40159d706a6ed983c8aca51922a93fc69f3c27909e82c537dd4054032674/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", size = 280267 }, + { url = "https://files.pythonhosted.org/packages/e0/18/9f09f84934c2b2aa37d539a322267939770362d5495f37783440ca9c1b74/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", size = 258778 }, + { url = "https://files.pythonhosted.org/packages/b3/c9/0bc5ee7e1f5cc7358ab67da0b7dfe60fbd05c254cea5c6108e7d1ae28c63/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", size = 272276 }, + { url = "https://files.pythonhosted.org/packages/12/5d/147556b73a53ad4df6da8bbb50715a66ac75c491fdedac3eca8b0b915345/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", size = 272424 }, + { url = "https://files.pythonhosted.org/packages/83/61/2087bbf24070b66090c0af922685f1d0596c24bb3f3b5223625bdeaf03ca/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", size = 260881 }, + { url = "https://files.pythonhosted.org/packages/a8/be/a235bc937dd803258a370fe21b5aa2dd3e7bfe0287a186a4bec30c6cccd6/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", size = 282327 }, + { url = "https://files.pythonhosted.org/packages/5d/e7/b2469e71f082948066b9382c7b908c22552cc705b960363c390d2e23f587/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74", size = 281502 }, + { url = "https://files.pythonhosted.org/packages/db/1b/6a5b970e55dffc1a7d0bb54f57b184b2a2a2ad0b7bca16a97ca26d73c5b5/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", size = 272292 }, + { url = "https://files.pythonhosted.org/packages/1a/05/ebad68130e6b6eb9b287dacad08ea357c33849c74550c015b355b75cc714/frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", size = 44446 }, + { url = "https://files.pythonhosted.org/packages/b3/21/c5aaffac47fd305d69df46cfbf118768cdf049a92ee6b0b5cb029d449dcf/frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", size = 50459 }, + { url = "https://files.pythonhosted.org/packages/b4/db/4cf37556a735bcdb2582f2c3fa286aefde2322f92d3141e087b8aeb27177/frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", size = 93937 }, + { url = "https://files.pythonhosted.org/packages/46/03/69eb64642ca8c05f30aa5931d6c55e50b43d0cd13256fdd01510a1f85221/frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", size = 53656 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/c543c13824a615955f57e082c8a5ee122d2d5368e80084f2834e6f4feced/frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", size = 51868 }, + { url = "https://files.pythonhosted.org/packages/a9/b8/438cfd92be2a124da8259b13409224d9b19ef8f5a5b2507174fc7e7ea18f/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", size = 280652 }, + { url = "https://files.pythonhosted.org/packages/54/72/716a955521b97a25d48315c6c3653f981041ce7a17ff79f701298195bca3/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", size = 286739 }, + { url = "https://files.pythonhosted.org/packages/65/d8/934c08103637567084568e4d5b4219c1016c60b4d29353b1a5b3587827d6/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", size = 289447 }, + { url = "https://files.pythonhosted.org/packages/70/bb/d3b98d83ec6ef88f9bd63d77104a305d68a146fd63a683569ea44c3085f6/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", size = 265466 }, + { url = "https://files.pythonhosted.org/packages/0b/f2/b8158a0f06faefec33f4dff6345a575c18095a44e52d4f10c678c137d0e0/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", size = 281530 }, + { url = "https://files.pythonhosted.org/packages/ea/a2/20882c251e61be653764038ece62029bfb34bd5b842724fff32a5b7a2894/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", size = 281295 }, + { url = "https://files.pythonhosted.org/packages/4c/f9/8894c05dc927af2a09663bdf31914d4fb5501653f240a5bbaf1e88cab1d3/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", size = 268054 }, + { url = "https://files.pythonhosted.org/packages/37/ff/a613e58452b60166507d731812f3be253eb1229808e59980f0405d1eafbf/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", size = 286904 }, + { url = "https://files.pythonhosted.org/packages/cc/6e/0091d785187f4c2020d5245796d04213f2261ad097e0c1cf35c44317d517/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", size = 290754 }, + { url = "https://files.pythonhosted.org/packages/a5/c2/e42ad54bae8bcffee22d1e12a8ee6c7717f7d5b5019261a8c861854f4776/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", size = 282602 }, + { url = "https://files.pythonhosted.org/packages/b6/61/56bad8cb94f0357c4bc134acc30822e90e203b5cb8ff82179947de90c17f/frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", size = 44063 }, + { url = "https://files.pythonhosted.org/packages/3e/dc/96647994a013bc72f3d453abab18340b7f5e222b7b7291e3697ca1fcfbd5/frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", size = 50452 }, + { url = "https://files.pythonhosted.org/packages/83/10/466fe96dae1bff622021ee687f68e5524d6392b0a2f80d05001cd3a451ba/frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", size = 11552 }, +] + +[[package]] +name = "fsspec" +version = "2024.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/7c/12b0943011daaaa9c35c2a2e22e5eb929ac90002f08f1259d69aedad84de/fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8", size = 286206 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/a0/6aaea0c2fbea2f89bfd5db25fb1e3481896a423002ebe4e55288907a97a3/fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b", size = 179253 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "html2text" +version = "2024.2.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32", size = 56527 } + +[[package]] +name = "httpcore" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/b0/5e8b8674f8d203335a62fdfcfa0d11ebe09e23613c3391033cbba35f7926/httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61", size = 83234 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/d4/e5d7e4f2174f8a4d63c8897d79eb8fe2503f7ecc03282fee1fa2719c2704/httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5", size = 77926 }, +] + +[[package]] +name = "httpx" +version = "0.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.24.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/24/b98fce967b7d63700e5805b915012ba25bb538a81fcf11e97f3cc3f4f012/huggingface_hub-0.24.6.tar.gz", hash = "sha256:cc2579e761d070713eaa9c323e3debe39d5b464ae3a7261c39a9195b27bb8000", size = 349200 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/8f/d6718641c14d98a5848c6a24d2376028d292074ffade0702940a4b1dde76/huggingface_hub-0.24.6-py3-none-any.whl", hash = "sha256:a990f3232aa985fe749bc9474060cbad75e8b2f115f6665a9fda5b9c97818970", size = 417509 }, +] + +[[package]] +name = "identify" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/f4/8e8f7db397a7ce20fbdeac5f25adaf567fc362472432938d25556008e03a/identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", size = 99116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/6c/a4f39abe7f19600b74528d0c717b52fff0b300bb0161081510d39c53cb00/identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0", size = 98962 }, +] + +[[package]] +name = "idna" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/e349c5e6d4543326c6883ee9491e3921e0d07b55fdf3cce184b40d63e72a/idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603", size = 189467 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/7e/d71db821f177828df9dea8c42ac46473366f191be53080e552e628aad991/idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", size = 66894 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/bd/fa8ce65b0a7d4b6d143ec23b0f5fd3f7ab80121078c465bc02baeaab22dc/importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5", size = 54320 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/14/362d31bf1076b21e1bcdcb0dc61944822ff263937b804a79231df2774d28/importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1", size = 26269 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "ipython" +version = "8.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/24/d4fabaca03c8804bf0b8d994c8ae3a20e57e9330d277fb43d83e558dec5e/ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e", size = 5494984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a2/6c725958e6f135d8e5de081e69841bb2c1d84b3fc259d02eb092b8fc203a/ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c", size = 818986 }, +] + +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + +[[package]] +name = "jedi" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/99/99b493cec4bf43176b678de30f81ed003fd6a647a301b9c927280c600f0a/jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", size = 1227821 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/9f/bc63f0f0737ad7a60800bfd472a4836661adae21f9c2535f3957b1e54ceb/jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0", size = 1569361 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "jiter" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/1a/aa64be757afc614484b370a4d9fc1747dc9237b37ce464f7f9d9ca2a3d38/jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a", size = 158300 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/5f/3ac960ed598726aae46edea916e6df4df7ff6fe084bc60774b95cf3154e6/jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553", size = 284131 }, + { url = "https://files.pythonhosted.org/packages/03/eb/2308fa5f5c14c97c4c7720fef9465f1fa0771826cddb4eec9866bdd88846/jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3", size = 299310 }, + { url = "https://files.pythonhosted.org/packages/3c/f6/dba34ca10b44715fa5302b8e8d2113f72eb00a9297ddf3fa0ae4fd22d1d1/jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6", size = 332282 }, + { url = "https://files.pythonhosted.org/packages/69/f7/64e0a7439790ec47f7681adb3871c9d9c45fff771102490bbee5e92c00b7/jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4", size = 342370 }, + { url = "https://files.pythonhosted.org/packages/55/31/1efbfff2ae8e4d919144c53db19b828049ad0622a670be3bbea94a86282c/jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9", size = 363591 }, + { url = "https://files.pythonhosted.org/packages/30/c3/7ab2ca2276426a7398c6dfb651e38dbc81954c79a3bfbc36c514d8599499/jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614", size = 378551 }, + { url = "https://files.pythonhosted.org/packages/47/e7/5d88031cd743c62199b125181a591b1671df3ff2f6e102df85c58d8f7d31/jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e", size = 319152 }, + { url = "https://files.pythonhosted.org/packages/4c/2d/09ea58e1adca9f0359f3d41ef44a1a18e59518d7c43a21f4ece9e72e28c0/jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06", size = 357377 }, + { url = "https://files.pythonhosted.org/packages/7d/2f/83ff1058cb56fc3ff73e0d3c6440703ddc9cdb7f759b00cfbde8228fc435/jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403", size = 511091 }, + { url = "https://files.pythonhosted.org/packages/ae/c9/4f85f97c9894382ab457382337aea0012711baaa17f2ed55c0ff25f3668a/jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646", size = 492948 }, + { url = "https://files.pythonhosted.org/packages/4d/f2/2e987e0eb465e064c5f52c2f29c8d955452e3b316746e326269263bfb1b7/jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb", size = 195183 }, + { url = "https://files.pythonhosted.org/packages/ab/59/05d1c3203c349b37c4dd28b02b9b4e5915a7bcbd9319173b4548a67d2e93/jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae", size = 191032 }, + { url = "https://files.pythonhosted.org/packages/aa/bd/c3950e2c478161e131bed8cb67c36aed418190e2a961a1c981e69954e54b/jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a", size = 283511 }, + { url = "https://files.pythonhosted.org/packages/80/1c/8ce58d8c37a589eeaaa5d07d131fd31043886f5e77ab50c00a66d869a361/jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df", size = 296974 }, + { url = "https://files.pythonhosted.org/packages/4d/b8/6faeff9eed8952bed93a77ea1cffae7b946795b88eafd1a60e87a67b09e0/jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248", size = 331897 }, + { url = "https://files.pythonhosted.org/packages/4f/54/1d9a2209b46d39ce6f0cef3ad87c462f9c50312ab84585e6bd5541292b35/jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544", size = 342962 }, + { url = "https://files.pythonhosted.org/packages/2a/de/90360be7fc54b2b4c2dfe79eb4ed1f659fce9c96682e6a0be4bbe71371f7/jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba", size = 363844 }, + { url = "https://files.pythonhosted.org/packages/ba/ad/ef32b173191b7a53ea8a6757b80723cba321f8469834825e8c71c96bde17/jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f", size = 378709 }, + { url = "https://files.pythonhosted.org/packages/07/de/353ce53743c0defbbbd652e89c106a97dbbac4eb42c95920b74b5056b93a/jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e", size = 319038 }, + { url = "https://files.pythonhosted.org/packages/3f/92/42d47310bf9530b9dece9e2d7c6d51cf419af5586ededaf5e66622d160e2/jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a", size = 357763 }, + { url = "https://files.pythonhosted.org/packages/bd/8c/2bb76a9a84474d48fdd133d3445db8a4413da4e87c23879d917e000a9d87/jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e", size = 511031 }, + { url = "https://files.pythonhosted.org/packages/33/4f/9f23d79c0795e0a8e56e7988e8785c2dcda27e0ed37977256d50c77c6a19/jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338", size = 493042 }, + { url = "https://files.pythonhosted.org/packages/df/67/8a4f975aa834b8aecdb6b131422390173928fd47f42f269dcc32034ab432/jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4", size = 195405 }, + { url = "https://files.pythonhosted.org/packages/15/81/296b1e25c43db67848728cdab34ac3eb5c5cbb4955ceb3f51ae60d4a5e3d/jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5", size = 189720 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b9/cc0cc592e7c195fb8a650c1d5990b10175cf13b4c97465c72ec841de9e4b/jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", size = 13983 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/07/44bd408781594c4d0a027666ef27fab1e441b109dc3b76b4f836f8fd04fe/jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c", size = 18482 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/77429fa0a58f941d6e1c58da9efe08597d2e86bf2b2cce6626834f49d07b/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", size = 122442 }, + { url = "https://files.pythonhosted.org/packages/e5/20/8c75caed8f2462d63c7fd65e16c832b8f76cda331ac9e615e914ee80bac9/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", size = 65762 }, + { url = "https://files.pythonhosted.org/packages/f4/98/fe010f15dc7230f45bc4cf367b012d651367fd203caaa992fd1f5963560e/kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", size = 64319 }, + { url = "https://files.pythonhosted.org/packages/8b/1b/b5d618f4e58c0675654c1e5051bcf42c776703edb21c02b8c74135541f60/kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", size = 1334260 }, + { url = "https://files.pythonhosted.org/packages/b8/01/946852b13057a162a8c32c4c8d2e9ed79f0bb5d86569a40c0b5fb103e373/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", size = 1426589 }, + { url = "https://files.pythonhosted.org/packages/70/d1/c9f96df26b459e15cf8a965304e6e6f4eb291e0f7a9460b4ad97b047561e/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", size = 1541080 }, + { url = "https://files.pythonhosted.org/packages/d3/73/2686990eb8b02d05f3de759d6a23a4ee7d491e659007dd4c075fede4b5d0/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052", size = 1470049 }, + { url = "https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", size = 1426376 }, + { url = "https://files.pythonhosted.org/packages/05/83/2857317d04ea46dc5d115f0df7e676997bbd968ced8e2bd6f7f19cfc8d7f/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", size = 2222231 }, + { url = "https://files.pythonhosted.org/packages/0d/b5/866f86f5897cd4ab6d25d22e403404766a123f138bd6a02ecb2cdde52c18/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", size = 2368634 }, + { url = "https://files.pythonhosted.org/packages/c1/ee/73de8385403faba55f782a41260210528fe3273d0cddcf6d51648202d6d0/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", size = 2329024 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/cd101d8cd2cdfaa42dc06c433df17c8303d31129c9fdd16c0ea37672af91/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", size = 2468484 }, + { url = "https://files.pythonhosted.org/packages/e1/72/84f09d45a10bc57a40bb58b81b99d8f22b58b2040c912b7eb97ebf625bf2/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", size = 2284078 }, + { url = "https://files.pythonhosted.org/packages/d2/d4/71828f32b956612dc36efd7be1788980cb1e66bfb3706e6dec9acad9b4f9/kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", size = 46645 }, + { url = "https://files.pythonhosted.org/packages/a1/65/d43e9a20aabcf2e798ad1aff6c143ae3a42cf506754bcb6a7ed8259c8425/kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", size = 56022 }, + { url = "https://files.pythonhosted.org/packages/35/b3/9f75a2e06f1b4ca00b2b192bc2b739334127d27f1d0625627ff8479302ba/kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", size = 48536 }, + { url = "https://files.pythonhosted.org/packages/97/9c/0a11c714cf8b6ef91001c8212c4ef207f772dd84540104952c45c1f0a249/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", size = 121808 }, + { url = "https://files.pythonhosted.org/packages/f2/d8/0fe8c5f5d35878ddd135f44f2af0e4e1d379e1c7b0716f97cdcb88d4fd27/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", size = 65531 }, + { url = "https://files.pythonhosted.org/packages/80/c5/57fa58276dfdfa612241d640a64ca2f76adc6ffcebdbd135b4ef60095098/kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", size = 63894 }, + { url = "https://files.pythonhosted.org/packages/8b/e9/26d3edd4c4ad1c5b891d8747a4f81b1b0aba9fb9721de6600a4adc09773b/kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", size = 1369296 }, + { url = "https://files.pythonhosted.org/packages/b6/67/3f4850b5e6cffb75ec40577ddf54f7b82b15269cc5097ff2e968ee32ea7d/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", size = 1461450 }, + { url = "https://files.pythonhosted.org/packages/52/be/86cbb9c9a315e98a8dc6b1d23c43cffd91d97d49318854f9c37b0e41cd68/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", size = 1579168 }, + { url = "https://files.pythonhosted.org/packages/0f/00/65061acf64bd5fd34c1f4ae53f20b43b0a017a541f242a60b135b9d1e301/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", size = 1507308 }, + { url = "https://files.pythonhosted.org/packages/21/e4/c0b6746fd2eb62fe702118b3ca0cb384ce95e1261cfada58ff693aeec08a/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", size = 1464186 }, + { url = "https://files.pythonhosted.org/packages/0a/0f/529d0a9fffb4d514f2782c829b0b4b371f7f441d61aa55f1de1c614c4ef3/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", size = 2247877 }, + { url = "https://files.pythonhosted.org/packages/d1/e1/66603ad779258843036d45adcbe1af0d1a889a07af4635f8b4ec7dccda35/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", size = 2404204 }, + { url = "https://files.pythonhosted.org/packages/8d/61/de5fb1ca7ad1f9ab7970e340a5b833d735df24689047de6ae71ab9d8d0e7/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", size = 2352461 }, + { url = "https://files.pythonhosted.org/packages/ba/d2/0edc00a852e369827f7e05fd008275f550353f1f9bcd55db9363d779fc63/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", size = 2501358 }, + { url = "https://files.pythonhosted.org/packages/84/15/adc15a483506aec6986c01fb7f237c3aec4d9ed4ac10b756e98a76835933/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", size = 2314119 }, + { url = "https://files.pythonhosted.org/packages/36/08/3a5bb2c53c89660863a5aa1ee236912269f2af8762af04a2e11df851d7b2/kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", size = 46367 }, + { url = "https://files.pythonhosted.org/packages/19/93/c05f0a6d825c643779fc3c70876bff1ac221f0e31e6f701f0e9578690d70/kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", size = 55884 }, + { url = "https://files.pythonhosted.org/packages/d2/f9/3828d8f21b6de4279f0667fb50a9f5215e6fe57d5ec0d61905914f5b6099/kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", size = 48528 }, + { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913 }, + { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627 }, + { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888 }, + { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145 }, + { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448 }, + { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750 }, + { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175 }, + { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963 }, + { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220 }, + { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463 }, + { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842 }, + { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635 }, + { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556 }, + { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364 }, + { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887 }, + { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530 }, +] + +[[package]] +name = "latexcodec" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/e7/ed339caf3662976949e4fdbfdf4a6db818b8d2aa1cf2b5f73af89e936bba/latexcodec-3.0.0.tar.gz", hash = "sha256:917dc5fe242762cc19d963e6548b42d63a118028cdd3361d62397e3b638b6bc5", size = 31023 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/bf/ea8887e9f31a8f93ca306699d11909c6140151393a4216f0d9f85a004077/latexcodec-3.0.0-py3-none-any.whl", hash = "sha256:6f3477ad5e61a0a99bd31a6a370c34e88733a6bad9c921a3ffcfacada12f41a7", size = 18150 }, +] + +[[package]] +name = "ldp" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "dm-tree" }, + { name = "fhaviary" }, + { name = "httpx" }, + { name = "litellm" }, + { name = "networkx", extra = ["default"] }, + { name = "numpy" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "tenacity" }, + { name = "tiktoken" }, + { name = "tqdm" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "usearch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/2a/789e4b56e1dcb55bce8e1e44b500d102cedb7ae75b914c4054ec68c5c49a/ldp-0.5.0.tar.gz", hash = "sha256:eb0a1d5c93807895f1dc6ab3bcfa14dd015dee964b251ae28b5235412460713d", size = 219736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/23/35f888c44c9de702c26b88735458446fde31b0bfe6ca3de3796e214d2c47/ldp-0.5.0-py3-none-any.whl", hash = "sha256:535ec0743ffb5f2df6200d9da2e2b1e6b6d6e7ceecfcabb8dcff726912e3af12", size = 89595 }, +] + +[[package]] +name = "litellm" +version = "1.44.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "requests" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/80/7dc284a9e46c22a1133fdf402715c776b8501337ae7cb1a0501b92b66f39/litellm-1.44.24.tar.gz", hash = "sha256:657c7c5365e90427356bea63cf5dcac81752f5ba6c7f205ec9c5c3ea15d57fb9", size = 8345616 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/1f/64f8ed38f2b540c96814b4aab1e2f5662cea421d9022d7977f5db0de7e3a/litellm-1.44.24-py3-none-any.whl", hash = "sha256:c0aa7f943c031660ad25b72c9ca14accbea6e6e6c79186eb172e1ee69a868eda", size = 8661706 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 }, + { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 }, + { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 }, + { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 }, + { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 }, + { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 }, + { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 }, + { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 }, + { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 }, + { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 }, + { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, + { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, + { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, + { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, + { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, + { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, + { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, + { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, + { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, + { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, +] + +[[package]] +name = "matplotlib" +version = "3.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/d8/3d7f706c69e024d4287c1110d74f7dabac91d9843b99eadc90de9efc8869/matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92", size = 36088381 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/c2/f9d7fe80a8fcce9bb128d1381c6fe41a8d286d7e18395e273002e8e0fa34/matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772", size = 7902925 }, + { url = "https://files.pythonhosted.org/packages/28/ba/8be09886eb56ac04a218a1dc3fa728a5c4cac60b019b4f1687885166da00/matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41", size = 7773193 }, + { url = "https://files.pythonhosted.org/packages/e6/9a/5991972a560db3ab621312a7ca5efec339ae2122f25901c0846865c4b72f/matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f", size = 8202378 }, + { url = "https://files.pythonhosted.org/packages/01/75/6c7ce560e95714a10fcbb3367d1304975a1a3e620f72af28921b796403f3/matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447", size = 8314361 }, + { url = "https://files.pythonhosted.org/packages/6e/49/dc7384c6c092958e0b75e754efbd9e52500154939c3d715789cee9fb8a53/matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e", size = 9091428 }, + { url = "https://files.pythonhosted.org/packages/8b/ce/15b0bb2fb29b3d46211d8ca740b96b5232499fc49200b58b8d571292c9a6/matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7", size = 7829377 }, + { url = "https://files.pythonhosted.org/packages/82/de/54f7f38ce6de79cb77d513bb3eaa4e0b1031e9fd6022214f47943fa53a88/matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9", size = 7892511 }, + { url = "https://files.pythonhosted.org/packages/35/3e/5713b84a02b24b2a4bd4d6673bfc03017e6654e1d8793ece783b7ed4d484/matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d", size = 7769370 }, + { url = "https://files.pythonhosted.org/packages/5b/bd/c404502aa1824456d2862dd6b9b0c1917761a51a32f7f83ff8cf94b6d117/matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7", size = 8193260 }, + { url = "https://files.pythonhosted.org/packages/27/75/de5b9cd67648051cae40039da0c8cbc497a0d99acb1a1f3d087cd66d27b7/matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c", size = 8306310 }, + { url = "https://files.pythonhosted.org/packages/de/e3/2976e4e54d7ee76eaf54b7639fdc10a223d05c2bdded7045233e9871e469/matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e", size = 9086717 }, + { url = "https://files.pythonhosted.org/packages/d2/92/c2b9464a0562feb6ae780bdc152364810862e07ef5e6affa2b7686028db2/matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3", size = 7832805 }, + { url = "https://files.pythonhosted.org/packages/5c/7f/8932eac316b32f464b8f9069f151294dcd892c8fbde61fe8bcd7ba7f7f7e/matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9", size = 7893012 }, + { url = "https://files.pythonhosted.org/packages/90/89/9db9db3dd0ff3e2c49e452236dfe29e60b5586a88f8928ca1d153d0da8b5/matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa", size = 7769810 }, + { url = "https://files.pythonhosted.org/packages/67/26/d2661cdc2e1410b8929c5f12dfd521e4528abfed1b3c3d5a28ac48258b43/matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b", size = 8193779 }, + { url = "https://files.pythonhosted.org/packages/95/70/4839eaa672bf4eacc98ebc8d23633e02b6daf39e294e7433c4ab11a689be/matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413", size = 8306260 }, + { url = "https://files.pythonhosted.org/packages/88/62/7b263b2cb2724b45d3a4f9c8c6137696cc3ef037d44383fb01ac2a9555c2/matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b", size = 9086073 }, + { url = "https://files.pythonhosted.org/packages/b0/6d/3572fe243c74112fef120f0bc86f5edd21f49b60e8322fc7f6a01fe945dd/matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49", size = 7833041 }, + { url = "https://files.pythonhosted.org/packages/03/8f/9d505be3eb2f40ec731674fb6b47d10cc3147bbd6a9ea7a08c8da55415c6/matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03", size = 7933657 }, + { url = "https://files.pythonhosted.org/packages/5d/68/44b458b9794bcff2a66921f8c9a8110a50a0bb099bd5f7cabb428a1dc765/matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30", size = 7799276 }, + { url = "https://files.pythonhosted.org/packages/47/79/8486d4ddcaaf676314b5fb58e8fe19d1a6210a443a7c31fa72d4215fcb87/matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51", size = 8221027 }, + { url = "https://files.pythonhosted.org/packages/56/62/72a472181578c3d035dcda0d0fa2e259ba2c4cb91132588a348bb705b70d/matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c", size = 8329097 }, + { url = "https://files.pythonhosted.org/packages/01/8a/760f7fce66b39f447ad160800619d0bd5d0936d2b4633587116534a4afe0/matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e", size = 9093770 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "multidict" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 }, + { url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 }, + { url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 }, + { url = "https://files.pythonhosted.org/packages/d8/6d/9c87b73a13d1cdea30b321ef4b3824449866bd7f7127eceed066ccb9b9ff/multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", size = 131067 }, + { url = "https://files.pythonhosted.org/packages/cc/1e/1b34154fef373371fd6c65125b3d42ff5f56c7ccc6bfff91b9b3c60ae9e0/multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", size = 138507 }, + { url = "https://files.pythonhosted.org/packages/fb/e0/0bc6b2bac6e461822b5f575eae85da6aae76d0e2a79b6665d6206b8e2e48/multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", size = 133905 }, + { url = "https://files.pythonhosted.org/packages/ba/af/73d13b918071ff9b2205fcf773d316e0f8fefb4ec65354bbcf0b10908cc6/multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", size = 129004 }, + { url = "https://files.pythonhosted.org/packages/74/21/23960627b00ed39643302d81bcda44c9444ebcdc04ee5bedd0757513f259/multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", size = 121308 }, + { url = "https://files.pythonhosted.org/packages/8b/5c/cf282263ffce4a596ed0bb2aa1a1dddfe1996d6a62d08842a8d4b33dca13/multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", size = 132608 }, + { url = "https://files.pythonhosted.org/packages/d7/3e/97e778c041c72063f42b290888daff008d3ab1427f5b09b714f5a8eff294/multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", size = 127029 }, + { url = "https://files.pythonhosted.org/packages/47/ac/3efb7bfe2f3aefcf8d103e9a7162572f01936155ab2f7ebcc7c255a23212/multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", size = 137594 }, + { url = "https://files.pythonhosted.org/packages/42/9b/6c6e9e8dc4f915fc90a9b7798c44a30773dea2995fdcb619870e705afe2b/multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", size = 134556 }, + { url = "https://files.pythonhosted.org/packages/1d/10/8e881743b26aaf718379a14ac58572a240e8293a1c9d68e1418fb11c0f90/multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", size = 130993 }, + { url = "https://files.pythonhosted.org/packages/45/84/3eb91b4b557442802d058a7579e864b329968c8d0ea57d907e7023c677f2/multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", size = 26405 }, + { url = "https://files.pythonhosted.org/packages/9f/0b/ad879847ecbf6d27e90a6eabb7eff6b62c129eefe617ea45eae7c1f0aead/multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", size = 28795 }, + { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 }, + { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 }, + { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 }, + { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 }, + { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 }, + { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 }, + { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 }, + { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 }, + { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 }, + { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 }, + { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 }, + { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 }, + { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 }, + { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 }, + { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 }, + { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 }, + { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 }, + { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 }, + { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 }, + { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 }, + { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 }, + { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 }, + { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 }, + { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 }, + { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 }, + { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 }, + { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 }, + { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 }, + { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 }, + { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, +] + +[[package]] +name = "mypy" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/aa/cc56fb53ebe14c64f1fe91d32d838d6f4db948b9494e200d2f61b820b85d/mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385", size = 10859630 }, + { url = "https://files.pythonhosted.org/packages/04/c8/b19a760fab491c22c51975cf74e3d253b8c8ce2be7afaa2490fbf95a8c59/mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca", size = 10037973 }, + { url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 }, + { url = "https://files.pythonhosted.org/packages/fc/a6/37f7544666b63a27e46c48f49caeee388bf3ce95f9c570eb5cfba5234405/mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4", size = 12897010 }, + { url = "https://files.pythonhosted.org/packages/84/8b/459a513badc4d34acb31c736a0101c22d2bd0697b969796ad93294165cfb/mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6", size = 9562873 }, + { url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 }, + { url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 }, + { url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 }, + { url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 }, + { url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 }, + { url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "networkx" +version = "3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/e6/b164f94c869d6b2c605b5128b7b0cfe912795a87fc90e78533920001f3ec/networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9", size = 2126579 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/e9/5f72929373e1a0e8d142a130f3f97e6ff920070f87f91c4e13e40e0fba5a/networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2", size = 1702396 }, +] + +[package.optional-dependencies] +default = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "scipy" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numpy" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/5f/9003bb3e632f2b58f5e3a3378902dcc73c5518070736c6740fe52454e8e1/numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd", size = 18874860 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/86/2c01070424a42b286ea0271203682c3d3e81e10ce695545b35768307b383/numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550", size = 21154850 }, + { url = "https://files.pythonhosted.org/packages/ef/4e/d3426d9e620a18bbb979f28e4dc7f9a2c35eb7cf726ffcb33545ebdd3e6a/numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f", size = 13789477 }, + { url = "https://files.pythonhosted.org/packages/c6/6e/fb6b1b2da9f4c757f55b202f10b6af0fe4fee87ace6e830228a12ab8ae5d/numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0", size = 5351769 }, + { url = "https://files.pythonhosted.org/packages/58/9a/07c8a9dc7254f3265ae014e33768d1cfd8eb73ee6cf215f4ec3b497e4255/numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95", size = 6890872 }, + { url = "https://files.pythonhosted.org/packages/08/4e/3b50fa3b1e045793056ed5a1fc6f89dd897ff9cb00900ca6377fe552d442/numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca", size = 13984256 }, + { url = "https://files.pythonhosted.org/packages/d9/37/108d692f7e2544b9ae972c7bfa06c26717871c273ccec86470bc3132b04d/numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf", size = 16337778 }, + { url = "https://files.pythonhosted.org/packages/95/2d/df81a1be3be6d3a92fd12dfd6c26a0dc026b276136ec1056562342a484a2/numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e", size = 16710448 }, + { url = "https://files.pythonhosted.org/packages/8f/34/4b2e604c5c44bd64b6c85e89d88871b41e60233b3ddf97419b37ae5b0c72/numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2", size = 14489002 }, + { url = "https://files.pythonhosted.org/packages/9f/0d/67c04b6bfefd0abbe7f60f7e4f11e3aca15d688faec1d1df089966105a9a/numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d", size = 6533215 }, + { url = "https://files.pythonhosted.org/packages/94/7a/4c00332a3ca79702bbc86228afd0e84e6f91b47222ec8cdf00677dd16481/numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e", size = 12870550 }, + { url = "https://files.pythonhosted.org/packages/36/11/c573ef66c004f991989c2c6218229d9003164525549409aec5ec9afc0285/numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e", size = 20884403 }, + { url = "https://files.pythonhosted.org/packages/6b/6c/a9fbef5fd2f9685212af2a9e47485cde9357c3e303e079ccf85127516f2d/numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe", size = 13493375 }, + { url = "https://files.pythonhosted.org/packages/34/f2/1316a6b08ad4c161d793abe81ff7181e9ae2e357a5b06352a383b9f8e800/numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f", size = 5088823 }, + { url = "https://files.pythonhosted.org/packages/be/15/fabf78a6d4a10c250e87daf1cd901af05e71501380532ac508879cc46a7e/numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521", size = 6619825 }, + { url = "https://files.pythonhosted.org/packages/9f/8a/76ddef3e621541ddd6984bc24d256a4e3422d036790cbbe449e6cad439ee/numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b", size = 13696705 }, + { url = "https://files.pythonhosted.org/packages/cb/22/2b840d297183916a95847c11f82ae11e248fa98113490b2357f774651e1d/numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201", size = 16041649 }, + { url = "https://files.pythonhosted.org/packages/c7/e8/6f4825d8f576cfd5e4d6515b9eec22bd618868bdafc8a8c08b446dcb65f0/numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a", size = 16409358 }, + { url = "https://files.pythonhosted.org/packages/bf/f8/5edf1105b0dc24fd66fc3e9e7f3bca3d920cde571caaa4375ec1566073c3/numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313", size = 14172488 }, + { url = "https://files.pythonhosted.org/packages/f4/c2/dddca3e69a024d2f249a5b68698328163cbdafb7e65fbf6d36373bbabf12/numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed", size = 6237195 }, + { url = "https://files.pythonhosted.org/packages/b7/98/5640a09daa3abf0caeaefa6e7bf0d10c0aa28a77c84e507d6a716e0e23df/numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270", size = 12568082 }, + { url = "https://files.pythonhosted.org/packages/6b/9e/8bc6f133bc6d359ccc9ec051853aded45504d217685191f31f46d36b7065/numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5", size = 20834810 }, + { url = "https://files.pythonhosted.org/packages/32/1b/429519a2fa28681814c511574017d35f3aab7136d554cc65f4c1526dfbf5/numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5", size = 13507739 }, + { url = "https://files.pythonhosted.org/packages/25/18/c732d7dd9896d11e4afcd487ac65e62f9fa0495563b7614eb850765361fa/numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136", size = 5074465 }, + { url = "https://files.pythonhosted.org/packages/3e/37/838b7ae9262c370ab25312bab365492016f11810ffc03ebebbd54670b669/numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0", size = 6606418 }, + { url = "https://files.pythonhosted.org/packages/8b/b9/7ff3bfb71e316a5b43a124c4b7a5881ab12f3c32636014bef1f757f19dbd/numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb", size = 13692464 }, + { url = "https://files.pythonhosted.org/packages/42/78/75bcf16e6737cd196ff7ecf0e1fd3f953293a34dff4fd93fb488e8308536/numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df", size = 16037763 }, + { url = "https://files.pythonhosted.org/packages/23/99/36bf5ffe034d06df307bc783e25cf164775863166dcd878879559fe0379f/numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78", size = 16410374 }, + { url = "https://files.pythonhosted.org/packages/7f/16/04c5dab564887d4cd31a9ed30e51467fa70d52a4425f5a9bd1eed5b3d34c/numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556", size = 14169873 }, + { url = "https://files.pythonhosted.org/packages/09/e0/d1b5adbf1731886c4186c59a9fa208585df9452a43a2b60e79af7c649717/numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b", size = 6234118 }, + { url = "https://files.pythonhosted.org/packages/d0/9c/2391ee6e9ebe77232ddcab29d92662b545e99d78c3eb3b4e26d59b9ca1ca/numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0", size = 12561742 }, + { url = "https://files.pythonhosted.org/packages/38/0e/c4f754f9e73f9bb520e8bf418c646f2c4f70c5d5f2bc561e90f884593193/numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553", size = 20858403 }, + { url = "https://files.pythonhosted.org/packages/32/fc/d69092b9171efa0cb8079577e71ce0cac0e08f917d33f6e99c916ed51d44/numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480", size = 13519851 }, + { url = "https://files.pythonhosted.org/packages/14/2a/d7cf2cd9f15b23f623075546ea64a2c367cab703338ca22aaaecf7e704df/numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f", size = 5115444 }, + { url = "https://files.pythonhosted.org/packages/8e/00/e87b2cb4afcecca3b678deefb8fa53005d7054f3b5c39596e5554e5d98f8/numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468", size = 6628903 }, + { url = "https://files.pythonhosted.org/packages/ab/9d/337ae8721b3beec48c3413d71f2d44b2defbf3c6f7a85184fc18b7b61f4a/numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef", size = 13665945 }, + { url = "https://files.pythonhosted.org/packages/c0/90/ee8668e84c5d5cc080ef3beb622c016adf19ca3aa51afe9dbdcc6a9baf59/numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f", size = 16023473 }, + { url = "https://files.pythonhosted.org/packages/38/a0/57c24b2131879183051dc698fbb53fd43b77c3fa85b6e6311014f2bc2973/numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c", size = 16400624 }, + { url = "https://files.pythonhosted.org/packages/bb/4c/14a41eb5c9548c6cee6af0936eabfd985c69230ffa2f2598321431a9aa0a/numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec", size = 14155072 }, +] + +[[package]] +name = "openai" +version = "1.44.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/24/33d5a56f59ba171ae8b60ae6326ef39fe7c36c94bedd08eb5dc785d50aad/openai-1.44.1.tar.gz", hash = "sha256:e0ffdab601118329ea7529e684b606a72c6c9d4f05be9ee1116255fcf5593874", size = 294846 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/cc/d76a24613ffc50e091e514138f2950c868a55aea10ae4ffe8a6163678abf/openai-1.44.1-py3-none-any.whl", hash = "sha256:07e2c2758d1c94151c740b14dab638ba0d04bcb41a2e397045c90e7661cdf741", size = 373457 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "pandas" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, + { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, + { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, + { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, + { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, + { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, + { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, + { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, + { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, + { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, + { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, + { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, + { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, +] + +[[package]] +name = "paper-qa" +version = "5.0.0a2.dev49+g1bd6946.d20240911" +source = { editable = "." } +dependencies = [ + { name = "aiohttp" }, + { name = "anyio" }, + { name = "fhaviary", extra = ["llm"] }, + { name = "html2text" }, + { name = "httpx" }, + { name = "litellm" }, + { name = "numpy" }, + { name = "pybtex" }, + { name = "pycryptodome" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pymupdf" }, + { name = "rich" }, + { name = "setuptools" }, + { name = "tantivy" }, + { name = "tenacity" }, + { name = "tiktoken" }, +] + +[package.optional-dependencies] +ldp = [ + { name = "ldp" }, +] +typing = [ + { name = "types-pyyaml" }, + { name = "types-setuptools" }, +] +zotero = [ + { name = "pyzotero" }, +] + +[package.dev-dependencies] +dev = [ + { name = "build" }, + { name = "ipython" }, + { name = "ldp" }, + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pydantic" }, + { name = "pylint-pydantic" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-recording" }, + { name = "pytest-rerunfailures" }, + { name = "pytest-subtests" }, + { name = "pytest-sugar" }, + { name = "pytest-timer", extra = ["colorama"] }, + { name = "pytest-xdist" }, + { name = "python-dotenv" }, + { name = "pyzotero" }, + { name = "refurb" }, + { name = "requests" }, + { name = "types-pyyaml" }, + { name = "types-setuptools" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp" }, + { name = "anyio" }, + { name = "fhaviary", extras = ["llm"], specifier = ">=0.6" }, + { name = "html2text" }, + { name = "httpx" }, + { name = "ldp", marker = "extra == 'ldp'", specifier = ">=0.4" }, + { name = "litellm", specifier = ">=1.44" }, + { name = "numpy" }, + { name = "pybtex" }, + { name = "pycryptodome" }, + { name = "pydantic", specifier = "~=2.0" }, + { name = "pydantic-settings" }, + { name = "pymupdf" }, + { name = "pyzotero", marker = "extra == 'zotero'" }, + { name = "rich" }, + { name = "setuptools" }, + { name = "tantivy" }, + { name = "tenacity" }, + { name = "tiktoken", specifier = ">=0.4.0" }, + { name = "types-pyyaml", marker = "extra == 'typing'" }, + { name = "types-setuptools", marker = "extra == 'typing'" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "build" }, + { name = "ipython", specifier = ">=8" }, + { name = "mypy", specifier = ">=1.8" }, + { name = "paper-qa", extras = ["ldp", "typing"] }, + { name = "pre-commit", specifier = "~=3.4" }, + { name = "pydantic", specifier = "~=2.0" }, + { name = "pylint-pydantic" }, + { name = "pytest", specifier = ">=8" }, + { name = "pytest-asyncio" }, + { name = "pytest-recording" }, + { name = "pytest-rerunfailures" }, + { name = "pytest-subtests" }, + { name = "pytest-sugar" }, + { name = "pytest-timer", extras = ["colorama"] }, + { name = "pytest-xdist" }, + { name = "python-dotenv" }, + { name = "pyzotero" }, + { name = "refurb", specifier = ">=2" }, + { name = "requests" }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "10.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a0/d7cab8409cdc7d39b037c85ac46d92434fb6595432e069251b38e5c8dd0e/platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c", size = 21276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/8b/d497999c4017b80678017ddce745cf675489c110681ad3c84a55eddfd3e7/platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617", size = 18417 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pre-commit" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.47" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/6d/0279b119dafc74c1220420028d490c4399b790fc1256998666e3a341879f/prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360", size = 425859 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/23/22750c4b768f09386d1c3cc4337953e8936f48a888fa6dddfb669b2c9088/prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10", size = 386411 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pybtex" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "latexcodec" }, + { name = "pyyaml" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/9b/fd39836a6397fb363446d83075a7b9c2cc562f4c449292e039ed36084376/pybtex-0.24.0.tar.gz", hash = "sha256:818eae35b61733e5c007c3fcd2cfb75ed1bc8b4173c1f70b56cc4c0802d34755", size = 402879 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/5f/40d8e90f985a05133a8895fc454c6127ecec3de8b095dd35bba91382f803/pybtex-0.24.0-py2.py3-none-any.whl", hash = "sha256:e1e0c8c69998452fea90e9179aa2a98ab103f3eed894405b7264e517cc2fcc0f", size = 561354 }, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/ed/19223a0a0186b8a91ebbdd2852865839237a21c74f1fbc4b8d5b62965239/pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7", size = 4794232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/96/b0d494defb3346378086848a8ece5ddfd138a66c4a05e038fca873b2518c/pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044", size = 2427142 }, + { url = "https://files.pythonhosted.org/packages/24/80/56a04e2ae622d7f38c1c01aef46a26c6b73a2ad15c9705a8e008b5befb03/pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a", size = 1590045 }, + { url = "https://files.pythonhosted.org/packages/ea/94/82ebfa5c83d980907ceebf79b00909a569d258bdfd9b0264d621fa752cfd/pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2", size = 2061748 }, + { url = "https://files.pythonhosted.org/packages/af/20/5f29ec45462360e7f61e8688af9fe4a0afae057edfabdada662e11bf97e7/pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c", size = 2135687 }, + { url = "https://files.pythonhosted.org/packages/e5/1f/6bc4beb4adc07b847e5d3fddbec4522c2c3aa05df9e61b91dc4eff6a4946/pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25", size = 2164262 }, + { url = "https://files.pythonhosted.org/packages/30/4b/cbc67cda0efd55d7ddcc98374c4b9c853022a595ed1d78dd15c961bc7f6e/pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128", size = 2054347 }, + { url = "https://files.pythonhosted.org/packages/0d/08/01987ab75ca789247a88c8b2f0ce374ef7d319e79589e0842e316a272662/pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c", size = 2192762 }, + { url = "https://files.pythonhosted.org/packages/b5/bf/798630923b67f4201059c2d690105998f20a6a8fb9b5ab68d221985155b3/pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4", size = 2155230 }, + { url = "https://files.pythonhosted.org/packages/39/12/5fe7f5b9212dda9f5a26f842a324d6541fe1ca8059602124ff30db1e874b/pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72", size = 1723464 }, + { url = "https://files.pythonhosted.org/packages/1f/90/d131c0eb643290230dfa4108b7c2d135122d88b714ad241d77beb4782a76/pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9", size = 1759588 }, + { url = "https://files.pythonhosted.org/packages/17/87/c7153fcd400df0f4a67d7d92cdb6b5e43f309c22434374b8a61849dfb280/pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a", size = 1639310 }, + { url = "https://files.pythonhosted.org/packages/68/9a/88d984405b087e8c8dd9a9d4c81a6fa675454e5fcf2ae01d9553b3128637/pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e", size = 1708332 }, +] + +[[package]] +name = "pydantic" +version = "2.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/15/3d989541b9c8128b96d532cfd2dd10131ddcc75a807330c00feb3d42a5bd/pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2", size = 768511 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/28/fff23284071bc1ba419635c7e86561c8b9b8cf62a5bcb459b92d7625fd38/pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612", size = 434363 }, +] + +[[package]] +name = "pydantic-core" +version = "2.23.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/cc/07bec3fb337ff80eacd6028745bd858b9642f61ee58cfdbfb64451c1def0/pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690", size = 402277 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/60/ef8eaad365c1d94962d158633f66313e051f7b90cead647e65a96993da22/pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27", size = 1843251 }, + { url = "https://files.pythonhosted.org/packages/57/f4/20aa352e03379a3b5d6c2fb951a979f70718138ea747e3f756d63dda69da/pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45", size = 1776367 }, + { url = "https://files.pythonhosted.org/packages/f1/b9/e5482ac4ea2d128925759d905fb05a08ca98e67ed1d8ab7401861997c6c8/pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611", size = 1800135 }, + { url = "https://files.pythonhosted.org/packages/78/9f/387353f6b6b2ed023f973cffa4e2384bb2e52d15acf5680bc70c50f6c48f/pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61", size = 1805896 }, + { url = "https://files.pythonhosted.org/packages/4f/70/9a153f19394e2ef749f586273ebcdb3de97e2fa97e175b957a8e5a2a77f9/pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5", size = 2001492 }, + { url = "https://files.pythonhosted.org/packages/a5/1c/79d976846fcdcae0c657922d0f476ca287fa694e69ac1fc9d397b831e1cc/pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0", size = 2659827 }, + { url = "https://files.pythonhosted.org/packages/fd/89/cdd76ae363cabae23a4b70df50d603c81c517415ff9d5d65e72e35251cf6/pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8", size = 2055160 }, + { url = "https://files.pythonhosted.org/packages/1a/82/7d62c3dd4e2e101a81ac3fa138d986bfbad9727a6275fc2b4a5efb98bdbd/pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8", size = 1922282 }, + { url = "https://files.pythonhosted.org/packages/85/e6/ef09f395c974d08674464dd3d49066612fe7cc0466ef8ce9427cadf13e5b/pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48", size = 1965827 }, + { url = "https://files.pythonhosted.org/packages/a4/5e/e589474af850c77c3180b101b54bc98bf812ad09728ba2cff4989acc9734/pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5", size = 2110810 }, + { url = "https://files.pythonhosted.org/packages/e0/ff/626007d5b7ac811f9bcac6d8af3a574ccee4505c1f015d25806101842f0c/pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1", size = 1715479 }, + { url = "https://files.pythonhosted.org/packages/4f/ff/6dc33f3b71e34ef633e35d6476d245bf303fc3eaf18a00f39bb54f78faf3/pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa", size = 1918281 }, + { url = "https://files.pythonhosted.org/packages/8f/35/6d81bc4aa7d06e716f39e2bffb0eabcbcebaf7bab94c2f8278e277ded0ea/pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305", size = 1845250 }, + { url = "https://files.pythonhosted.org/packages/18/42/0821cd46f76406e0fe57df7a89d6af8fddb22cce755bcc2db077773c7d1a/pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb", size = 1769993 }, + { url = "https://files.pythonhosted.org/packages/e5/55/b969088e48bd8ea588548a7194d425de74370b17b385cee4d28f5a79013d/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa", size = 1791250 }, + { url = "https://files.pythonhosted.org/packages/43/c1/1d460d09c012ac76b68b2a1fd426ad624724f93b40e24a9a993763f12c61/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162", size = 1802530 }, + { url = "https://files.pythonhosted.org/packages/70/8e/fd3c9eda00fbdadca726f17a0f863ecd871a65b3a381b77277ae386d3bcd/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801", size = 1997848 }, + { url = "https://files.pythonhosted.org/packages/f0/67/13fa22d7b09395e83721edc31bae2bd5c5e2c36a09d470c18f5d1de46958/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb", size = 2662790 }, + { url = "https://files.pythonhosted.org/packages/fa/1b/1d689c53d15ab67cb0df1c3a2b1df873b50409581e93e4848289dce57e2f/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326", size = 2074114 }, + { url = "https://files.pythonhosted.org/packages/3d/d9/b565048609db77760b9a0900f6e0a3b2f33be47cd3c4a433f49653a0d2b5/pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c", size = 1918153 }, + { url = "https://files.pythonhosted.org/packages/41/94/8ee55c51333ed8df3a6f1e73c6530c724a9a37d326e114c9e3b24faacff9/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c", size = 1969019 }, + { url = "https://files.pythonhosted.org/packages/f7/49/0233bae5778a5526cef000447a93e8d462f4f13e2214c13c5b23d379cb25/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab", size = 2121325 }, + { url = "https://files.pythonhosted.org/packages/42/a1/2f262db2fd6f9c2c9904075a067b1764cc6f71c014be5c6c91d9de52c434/pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c", size = 1725252 }, + { url = "https://files.pythonhosted.org/packages/9a/00/a57937080b49500df790c4853d3e7bc605bd0784e4fcaf1a159456f37ef1/pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b", size = 1920660 }, + { url = "https://files.pythonhosted.org/packages/e1/3c/32958c0a5d1935591b58337037a1695782e61261582d93d5a7f55441f879/pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f", size = 1845068 }, + { url = "https://files.pythonhosted.org/packages/92/a1/7e628e19b78e6ffdb2c92cccbb7eca84bfd3276cee4cafcae8833452f458/pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2", size = 1770095 }, + { url = "https://files.pythonhosted.org/packages/bb/17/d15fd8ce143cd1abb27be924eeff3c5c0fe3b0582f703c5a5273c11e67ce/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791", size = 1790964 }, + { url = "https://files.pythonhosted.org/packages/24/cc/37feff1792f09dc33207fbad3897373229279d1973c211f9562abfdf137d/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423", size = 1802384 }, + { url = "https://files.pythonhosted.org/packages/44/d8/ca9acd7f5f044d9ff6e43d7f35aab4b1d5982b4773761eabe3317fc68e30/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63", size = 1997824 }, + { url = "https://files.pythonhosted.org/packages/35/0f/146269dba21b10d5bf86f9a7a7bbeab4ce1db06f466a1ab5ec3dec68b409/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9", size = 2662907 }, + { url = "https://files.pythonhosted.org/packages/5a/7d/9573f006e39cd1a7b7716d1a264e3f4f353cf0a6042c04c01c6e31666f62/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5", size = 2073953 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/25200aaafd1e97e2ec3c1eb4b357669dd93911f2eba252bc60b6ba884fff/pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855", size = 1917822 }, + { url = "https://files.pythonhosted.org/packages/3e/b4/ac069c58e3cee70c69f03693222cc173fdf740d20d53167bceafc1efc7ca/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4", size = 1968838 }, + { url = "https://files.pythonhosted.org/packages/d1/3d/9f96bbd6212b4b0a6dc6d037e446208d3420baba2b2b81e544094b18a859/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d", size = 2121468 }, + { url = "https://files.pythonhosted.org/packages/ac/50/7399d536d6600d69059a87fff89861332c97a7b3471327a3663c7576e707/pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8", size = 1725373 }, + { url = "https://files.pythonhosted.org/packages/24/ba/9ac8744ab636c1161c598cc5e8261379b6b0f1d63c31242bf9d5ed41ed32/pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1", size = 1920594 }, +] + +[[package]] +name = "pydantic-settings" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/ac/6eae73dfa0bae45e3ec9fa7485e3cb67731169e2ae446d8469d06f69ff1e/pydantic_settings-2.5.0.tar.gz", hash = "sha256:204828c02481a2e7135466b26a7d65d9e15a17d52d1d8f59cacdf9ad625e1140", size = 70874 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/89/f09ebd53c47f788521ae0381b7a33c63de309b7abee91e32d040085ae99b/pydantic_settings-2.5.0-py3-none-any.whl", hash = "sha256:eae04a3dd9adf521a4c959dcefb984e0f3b1d841999daf02f961dcc4d31d2f7f", size = 26846 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pylint" +version = "3.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/e8/d59ce8e54884c9475ed6510685ef4311a10001674c28703b23da30f3b24d/pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e", size = 1511922 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/4d/c73bc0fca447b918611985c325cd7017fb762050eb9c6ac6fa7d9ac6fbe4/pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b", size = 519906 }, +] + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pylint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/d2/3b9728910bc69232ec38d8fb7053c03c887bfe7e6e170649b683dd351750/pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4", size = 10674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/ee/49d11aee31061bcc1d2726bd8334a2883ddcdbde7d7744ed6b3bd11704ed/pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507", size = 11171 }, +] + +[[package]] +name = "pylint-pydantic" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "pylint" }, + { name = "pylint-plugin-utils" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/80/34b429c6534be99ef3d6d20bd794b26fda0682d38e2d57f85df258beaac2/pylint_pydantic-0.3.2-py3-none-any.whl", hash = "sha256:e5cec02370aa68ac8eff138e5d573b0ac049bab864e9a6c3a9057cf043440aa1", size = 15951 }, +] + +[[package]] +name = "pymupdf" +version = "1.24.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pymupdfb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/57/da06ca4886afc71a624e4b463d05f45c8a822596ede939957295e229eb4e/PyMuPDF-1.24.10.tar.gz", hash = "sha256:bd3ebd6d3fb8a845582098362f885bfb0a31ae4272587efc2c55c5e29fe7327a", size = 46988085 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/35/6af0bb4bafe9d54893a04d9639f73b1b754efe0235997052d75fb6b7edc1/PyMuPDF-1.24.10-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:5fbd67cce759fc0126902137409cf9da6313b776c4d5ff0d5200f336350f86a3", size = 3194012 }, + { url = "https://files.pythonhosted.org/packages/bf/2b/c254cf49dfcf2469a674407a680f5b2b174b866e84d322f5767baf4d3ad3/PyMuPDF-1.24.10-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:2b14dbdf7c415bb0fa849527abbe7b4f1f55ae23b9355d132951f634438c59ac", size = 2974781 }, + { url = "https://files.pythonhosted.org/packages/1c/77/78800d3a711f92060f8e338a5df9330ffb5950f4fb3beeba01e15c03c4c6/PyMuPDF-1.24.10-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:1a87440a6cbc0d5ad513425baa0f4747841898fca6e37350ca3e6b29e5f40c01", size = 3210393 }, + { url = "https://files.pythonhosted.org/packages/c5/39/3aaa1e8822c55c71bb37911b5b1c3157ef38d731581224b29a682d80a17b/PyMuPDF-1.24.10-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:c0d1ccdc062ea9961063790831e838bc43fcf9a8436a8b9f55898addf97c0f86", size = 3482650 }, + { url = "https://files.pythonhosted.org/packages/5b/73/6b5c2dc59539b79cb9430ff946d7dff308af146f7c8bc7b96c963e12970d/PyMuPDF-1.24.10-cp311-none-musllinux_1_2_x86_64.whl", hash = "sha256:f68671363be5a2ba104ab7d3bad821d2994cbe3f3408538bbc27d32e6dc9f923", size = 3600588 }, + { url = "https://files.pythonhosted.org/packages/71/e9/d3bf062325b4821726a2f9ce9d75b63f594ae24bc38c31f55b4285f1f5e1/PyMuPDF-1.24.10-cp311-none-win32.whl", hash = "sha256:49f83556cd1a7d05b36a54ccc01fce324da8a4e6854e36cc5cd94d321e428565", size = 2694768 }, + { url = "https://files.pythonhosted.org/packages/30/3f/356a70c105d4410c29529f1ca8c53b5d176b448a4409238b4dcd133507a4/PyMuPDF-1.24.10-cp311-none-win_amd64.whl", hash = "sha256:05b8d360766b87f4abd186eba16a56b92bae513b2361b13f633fe6256329292e", size = 3214889 }, + { url = "https://files.pythonhosted.org/packages/75/84/7231344d98355a40fb57c4025391dfb4116e2c3e9d98d5cc83f80c5ea942/PyMuPDF-1.24.10-cp312-none-macosx_10_9_x86_64.whl", hash = "sha256:f323aa7bb55e0214e632bfe24fa140bd5dcfeac2d3977bdce46e760385140513", size = 3230169 }, + { url = "https://files.pythonhosted.org/packages/b2/bc/975b4fe4400b00c912dad1874c43d31486150e6f39d7dae758751c27e2dd/PyMuPDF-1.24.10-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:50d2972558d25ce46a8634b58787b28dbeff9b3fe4299530fc9c8c9921061e83", size = 2980118 }, + { url = "https://files.pythonhosted.org/packages/5b/dc/0f22c77ac4f8e6b8316072519513d5f0111fffe96d357051db0ddf043032/PyMuPDF-1.24.10-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:0e3969c2fdff682b3b2c6a2b463adde068d6d8e20e2133ef6c8503469259646a", size = 3216830 }, + { url = "https://files.pythonhosted.org/packages/a3/1b/1b41b27aab571b835f8d983492b80ed64548e3b5c4d169e23c639727d43b/PyMuPDF-1.24.10-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:cd78ee1ebefdfe72bc36fd4b731cc8c694eb8ef5337d8ea956b0e94cd88751fc", size = 3491118 }, + { url = "https://files.pythonhosted.org/packages/2d/3c/f1ffbc6e13ab37900c2aa71e434bbba922770091242e2b059acdb14f779e/PyMuPDF-1.24.10-cp312-none-musllinux_1_2_x86_64.whl", hash = "sha256:696eed91d2ee44e76277dfeb6bd904c84ae005378588949df6ed9be9e03b9817", size = 3612589 }, + { url = "https://files.pythonhosted.org/packages/53/fb/158909af75c84968ea7e6659a75fd67bd462103c599033b23ffd6bc173be/PyMuPDF-1.24.10-cp312-none-win32.whl", hash = "sha256:1e5413e1aeab2f18e1ca1b3ff17057a4a7c5cbf4ff14abc93203da88fc1a1dd8", size = 2701190 }, + { url = "https://files.pythonhosted.org/packages/91/4a/4a54d3f6a779ac5eed92e82fe3c1bb426bc40f9ea57c8656839198944a82/PyMuPDF-1.24.10-cp312-none-win_amd64.whl", hash = "sha256:227a4473fce8fa32b9268da68781048795503b67dc045867fc201e1334204bf1", size = 3228084 }, +] + +[[package]] +name = "pymupdfb" +version = "1.24.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/ff/ecfcb41414b51976974d74c8e35fef0a0e5b47c7046a11c860553f5dccf0/PyMuPDFb-1.24.10.tar.gz", hash = "sha256:007b91fa9b528c5c0eecea2e49c486ac02e878274f9e31522bdd948adc5f8327", size = 37502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/94/b217dc987b4ac0e3793984427112d6032563b741e27763f7761c2231d022/PyMuPDFb-1.24.10-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:cd6b24630d90dce9ab3e59d06c5e616686f8d7ec626be1311721fcb062aa0078", size = 15536229 }, + { url = "https://files.pythonhosted.org/packages/16/7a/f634c76d8331cb8dedcfaced17424cc469ee20b7f53cf29c9ef17a01b461/PyMuPDFb-1.24.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fda2c34b206f724b1b5685b67188e2a57bcaa5c99bc40a0a5bc62057514c5cdf", size = 15149482 }, + { url = "https://files.pythonhosted.org/packages/62/97/67b5da2edd034e66dadd0ec530e277afb14fe866a3b3b01d9fad154bc6f8/PyMuPDFb-1.24.10-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4f50a7472f9bb10cbc7a1cd589ee4626ca030b8a4a02749f9a29eb6f00c0e0db", size = 15711338 }, + { url = "https://files.pythonhosted.org/packages/62/b9/ad3f076e86328880797fe7e98c43b2879df56cf6cb75ac3058da06d6e6cb/PyMuPDFb-1.24.10-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:409f1270ef2e70d845e80149ff3db9cfed578274042316cba55cc3e3882421ea", size = 15921939 }, + { url = "https://files.pythonhosted.org/packages/15/e7/02160ea905a7ba16d6e1ca51759ae1c1045785ebebae57ba30e82617f934/PyMuPDFb-1.24.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:aca96b6e9ee3096a26810592f4d899f4d3cf3cf0c902ae7e8cca09bce4d946c4", size = 17076991 }, + { url = "https://files.pythonhosted.org/packages/d3/c0/e1ed840440131f71b068cdb3b620a69ec27543b1012a6bd855d8d05f1629/PyMuPDFb-1.24.10-py3-none-win32.whl", hash = "sha256:2d231b42fe3bf79837df235e7fbdf7ff8b46bf4ca1346d0f0124fb1cdd343ce8", size = 11731706 }, + { url = "https://files.pythonhosted.org/packages/70/cb/8459d6c179befd7c6eee555334f054e9a6dcdd9f8671891e1da19e0ce526/PyMuPDFb-1.24.10-py3-none-win_amd64.whl", hash = "sha256:27ea65c701608b6b7632703339ca33ea6d513843b26dbe9bdefb2f56f7b9b196", size = 13186168 }, +] + +[[package]] +name = "pyparsing" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/08/13f3bce01b2061f2bbd582c9df82723de943784cf719a35ac886c652043a/pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032", size = 900231 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", size = 104100 }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/07/6f63dda440d4abb191b91dc383b472dae3dd9f37e4c1e4a5c3db150531c6/pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965", size = 7838 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", size = 9184 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, +] + +[[package]] +name = "pytest-recording" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "vcrpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/2a/ea6b8036ae01979eae02d8ad5a7da14dec90d9176b613e49fb8d134c78fc/pytest_recording-0.13.2.tar.gz", hash = "sha256:000c3babbb466681457fd65b723427c1779a0c6c17d9e381c3142a701e124877", size = 25270 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/52/8e67a969e9fad3fa5ec4eab9f2a7348ff04692065c7deda21d76e9112703/pytest_recording-0.13.2-py3-none-any.whl", hash = "sha256:3820fe5743d1ac46e807989e11d073cb776a60bdc544cf43ebca454051b22d13", size = 12783 }, +] + +[[package]] +name = "pytest-rerunfailures" +version = "14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/a4/6de45fe850759e94aa9a55cda807c76245af1941047294df26c851dfb4a9/pytest-rerunfailures-14.0.tar.gz", hash = "sha256:4a400bcbcd3c7a4ad151ab8afac123d90eca3abe27f98725dc4d9702887d2e92", size = 21350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/e7/e75bd157331aecc190f5f8950d7ea3d2cf56c3c57fb44da70e60b221133f/pytest_rerunfailures-14.0-py3-none-any.whl", hash = "sha256:4197bdd2eaeffdbf50b5ea6e7236f47ff0e44d1def8dae08e409f536d84e7b32", size = 12709 }, +] + +[[package]] +name = "pytest-subtests" +version = "0.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/fe/e691d2f4ce061a475f488cad1ef58431556affea323dde5c764fd7515a70/pytest_subtests-0.13.1.tar.gz", hash = "sha256:989e38f0f1c01bc7c6b2e04db7d9fd859db35d77c2c1a430c831a70cbf3fde2d", size = 15936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/ac/fc132cb88e8f2042cebcb6ef0ffac40017c514fbadf3931e0b4bcb4bdfb6/pytest_subtests-0.13.1-py3-none-any.whl", hash = "sha256:ab616a22f64cd17c1aee65f18af94dbc30c444f8683de2b30895c3778265e3bd", size = 8038 }, +] + +[[package]] +name = "pytest-sugar" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pytest" }, + { name = "termcolor" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/ac/5754f5edd6d508bc6493bc37d74b928f102a5fff82d9a80347e180998f08/pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a", size = 14992 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/fb/889f1b69da2f13691de09a111c16c4766a433382d44aa0ecf221deded44a/pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd", size = 10171 }, +] + +[[package]] +name = "pytest-timer" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/f7/bc223798d5cccf2f03ed224a6fdceae005d06ae33fbd302741da9eb9e95a/pytest-timer-1.0.0.tar.gz", hash = "sha256:c65a36970e4425c0fd43bdf63b60359b353382632b08418c7c25918ec18a3829", size = 5812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/48/486b6ff8b910852affeab5b628910f8faa861450a37c21c19902b798ebda/pytest_timer-1.0.0-py3-none-any.whl", hash = "sha256:60f16a6d98dd5e8ce3e57ece829592f6e081e18be9f1b20e5bc93e5e7196b065", size = 5331 }, +] + +[package.optional-dependencies] +colorama = [ + { name = "colorama" }, +] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "pyzotero" +version = "1.5.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bibtexparser" }, + { name = "feedparser" }, + { name = "pytz" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/a7/156832aa4994031e97714e47896d8e192d7ec15a3188d05b2ac30a7af926/pyzotero-1.5.20.tar.gz", hash = "sha256:10b033d9e569e22d49e592899a48726848e29121fb6150e5689e272994dbd788", size = 526300 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/f6/2c80f11a128cf854d6bb90c77976fc60520cd1e1c351c445697b8c35c17b/pyzotero-1.5.20-py3-none-any.whl", hash = "sha256:c98f4b16ddd36bf579d109931cf1416f960ad79da27dd3504a1a3691c76844ec", size = 22284 }, +] + +[[package]] +name = "referencing" +version = "0.35.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684 }, +] + +[[package]] +name = "refurb" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/83/56ecbe3af6462e7a87cc4a302c2889e7ce447e9502ea76b7a739d1d46123/refurb-2.0.0.tar.gz", hash = "sha256:8a8f1e7c131ef7dc460cbecbeaf536f5eb0ecb657c099d7823941f0e65b1cfe1", size = 91453 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/3e/f0a47001b29205d96c9f2bf7f7383fdeadd7c35488e6dadd9afa3b6283e8/refurb-2.0.0-py3-none-any.whl", hash = "sha256:fa9e950dc6edd7473642569c118f8714eefd1e6f21a15ee4210a1be853aaaf80", size = 138560 }, +] + +[[package]] +name = "regex" +version = "2024.7.24" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/51/64256d0dc72816a4fe3779449627c69ec8fee5a5625fd60ba048f53b3478/regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506", size = 393485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/ec/261f8434a47685d61e59a4ef3d9ce7902af521219f3ebd2194c7adb171a6/regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281", size = 470810 }, + { url = "https://files.pythonhosted.org/packages/f0/47/f33b1cac88841f95fff862476a9e875d9a10dae6912a675c6f13c128e5d9/regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b", size = 282126 }, + { url = "https://files.pythonhosted.org/packages/fc/1b/256ca4e2d5041c0aa2f1dc222f04412b796346ab9ce2aa5147405a9457b4/regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a", size = 278920 }, + { url = "https://files.pythonhosted.org/packages/91/03/4603ec057c0bafd2f6f50b0bdda4b12a0ff81022decf1de007b485c356a6/regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73", size = 785420 }, + { url = "https://files.pythonhosted.org/packages/75/f8/13b111fab93e6273e26de2926345e5ecf6ddad1e44c4d419d7b0924f9c52/regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2", size = 828164 }, + { url = "https://files.pythonhosted.org/packages/4a/80/bc3b9d31bd47ff578758af929af0ac1d6169b247e26fa6e87764007f3d93/regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e", size = 812621 }, + { url = "https://files.pythonhosted.org/packages/8b/77/92d4a14530900d46dddc57b728eea65d723cc9fcfd07b96c2c141dabba84/regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51", size = 786609 }, + { url = "https://files.pythonhosted.org/packages/35/58/06695fd8afad4c8ed0a53ec5e222156398b9fe5afd58887ab94ea68e4d16/regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364", size = 775290 }, + { url = "https://files.pythonhosted.org/packages/1b/0f/50b97ee1fc6965744b9e943b5c0f3740792ab54792df73d984510964ef29/regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee", size = 772849 }, + { url = "https://files.pythonhosted.org/packages/8f/64/565ff6cf241586ab7ae76bb4138c4d29bc1d1780973b457c2db30b21809a/regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c", size = 778428 }, + { url = "https://files.pythonhosted.org/packages/e5/fe/4ceabf4382e44e1e096ac46fd5e3bca490738b24157116a48270fd542e88/regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce", size = 849436 }, + { url = "https://files.pythonhosted.org/packages/68/23/1868e40d6b594843fd1a3498ffe75d58674edfc90d95e18dd87865b93bf2/regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1", size = 849484 }, + { url = "https://files.pythonhosted.org/packages/f3/52/bff76de2f6e2bc05edce3abeb7e98e6309aa022fc06071100a0216fbeb50/regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e", size = 776712 }, + { url = "https://files.pythonhosted.org/packages/f2/72/70ade7b0b5fe5c6df38fdfa2a5a8273e3ea6a10b772aa671b7e889e78bae/regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c", size = 257716 }, + { url = "https://files.pythonhosted.org/packages/04/4d/80e04f4e27ab0cbc9096e2d10696da6d9c26a39b60db52670fd57614fea5/regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52", size = 269662 }, + { url = "https://files.pythonhosted.org/packages/0f/26/f505782f386ac0399a9237571833f187414882ab6902e2e71a1ecb506835/regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86", size = 471748 }, + { url = "https://files.pythonhosted.org/packages/bb/1d/ea9a21beeb433dbfca31ab82867d69cb67ff8674af9fab6ebd55fa9d3387/regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad", size = 282841 }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c6182095baf0a10169c34e87133a8e73b2e816a80035669b1278e927685e/regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9", size = 279114 }, + { url = "https://files.pythonhosted.org/packages/72/58/b5161bf890b6ca575a25685f19a4a3e3b6f4a072238814f8658123177d84/regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289", size = 789749 }, + { url = "https://files.pythonhosted.org/packages/09/fb/5381b19b62f3a3494266be462f6a015a869cf4bfd8e14d6e7db67e2c8069/regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9", size = 831666 }, + { url = "https://files.pythonhosted.org/packages/3d/6d/2a21c85f970f9be79357d12cf4b97f4fc6bf3bf6b843c39dabbc4e5f1181/regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c", size = 817544 }, + { url = "https://files.pythonhosted.org/packages/f9/ae/5f23e64f6cf170614237c654f3501a912dfb8549143d4b91d1cd13dba319/regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440", size = 790854 }, + { url = "https://files.pythonhosted.org/packages/29/0a/d04baad1bbc49cdfb4aef90c4fc875a60aaf96d35a1616f1dfe8149716bc/regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610", size = 779242 }, + { url = "https://files.pythonhosted.org/packages/3a/27/b242a962f650c3213da4596d70e24c7c1c46e3aa0f79f2a81164291085f8/regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5", size = 776932 }, + { url = "https://files.pythonhosted.org/packages/9c/ae/de659bdfff80ad2c0b577a43dd89dbc43870a4fc4bbf604e452196758e83/regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799", size = 784521 }, + { url = "https://files.pythonhosted.org/packages/d4/ac/eb6a796da0bdefbf09644a7868309423b18d344cf49963a9d36c13502d46/regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05", size = 854548 }, + { url = "https://files.pythonhosted.org/packages/56/77/fde8d825dec69e70256e0925af6c81eea9acf0a634d3d80f619d8dcd6888/regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94", size = 853345 }, + { url = "https://files.pythonhosted.org/packages/ff/04/2b79ad0bb9bc05ab4386caa2c19aa047a66afcbdfc2640618ffc729841e4/regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38", size = 781414 }, + { url = "https://files.pythonhosted.org/packages/bf/71/d0af58199283ada7d25b20e416f5b155f50aad99b0e791c0966ff5a1cd00/regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc", size = 258125 }, + { url = "https://files.pythonhosted.org/packages/95/b3/10e875c45c60b010b66fc109b899c6fc4f05d485fe1d54abff98ce791124/regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908", size = 269162 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "13.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/40f084cb7db51c9d1fa29a7120717892aeda9a7711f6225692c957a93535/rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a", size = 222080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/11/dadb85e2bd6b1f1ae56669c3e1f0410797f9605d752d68fb47b77f525b31/rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", size = 241608 }, +] + +[[package]] +name = "rpds-py" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/64/b693f262791b818880d17268f3f8181ef799b0d187f6f731b1772e05a29a/rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121", size = 25814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/2a/191374c52d7be0b056cc2a04d718d2244c152f915d4a8d2db2aacc526189/rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489", size = 318369 }, + { url = "https://files.pythonhosted.org/packages/0e/6a/2c9fdcc6d235ac0d61ec4fd9981184689c3e682abd05e3caa49bccb9c298/rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318", size = 311303 }, + { url = "https://files.pythonhosted.org/packages/d2/b2/725487d29633f64ef8f9cbf4729111a0b61702c8f8e94db1653930f52cce/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db", size = 366424 }, + { url = "https://files.pythonhosted.org/packages/7a/8c/668195ab9226d01b7cf7cd9e59c1c0be1df05d602df7ec0cf46f857dcf59/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5", size = 368359 }, + { url = "https://files.pythonhosted.org/packages/52/28/356f6a39c1adeb02cf3e5dd526f5e8e54e17899bef045397abcfbf50dffa/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5", size = 394886 }, + { url = "https://files.pythonhosted.org/packages/a2/65/640fb1a89080a8fb6f4bebd3dafb65a2edba82e2e44c33e6eb0f3e7956f1/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6", size = 432416 }, + { url = "https://files.pythonhosted.org/packages/a7/e8/85835077b782555d6b3416874b702ea6ebd7db1f145283c9252968670dd5/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209", size = 354819 }, + { url = "https://files.pythonhosted.org/packages/4f/87/1ac631e923d65cbf36fbcfc6eaa702a169496de1311e54be142f178e53ee/rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3", size = 373282 }, + { url = "https://files.pythonhosted.org/packages/e4/ce/cb316f7970189e217b998191c7cf0da2ede3d5437932c86a7210dc1e9994/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272", size = 541540 }, + { url = "https://files.pythonhosted.org/packages/90/d7/4112d7655ec8aff168ecc91d4ceb51c557336edde7e6ccf6463691a2f253/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad", size = 547640 }, + { url = "https://files.pythonhosted.org/packages/ab/44/4f61d64dfed98cc71623f3a7fcb612df636a208b4b2c6611eaa985e130a9/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58", size = 525555 }, + { url = "https://files.pythonhosted.org/packages/35/f2/a862d81eacb21f340d584cd1c749c289979f9a60e9229f78bffc0418a199/rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0", size = 199338 }, + { url = "https://files.pythonhosted.org/packages/cc/ec/77d0674f9af4872919f3738018558dd9d37ad3f7ad792d062eadd4af7cba/rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c", size = 213585 }, + { url = "https://files.pythonhosted.org/packages/89/b7/f9682c5cc37fcc035f4a0fc33c1fe92ec9cbfdee0cdfd071cf948f53e0df/rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6", size = 321468 }, + { url = "https://files.pythonhosted.org/packages/b8/ad/fc82be4eaceb8d444cb6fc1956ce972b3a0795104279de05e0e4131d0a47/rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b", size = 313062 }, + { url = "https://files.pythonhosted.org/packages/0e/1c/6039e80b13a08569a304dc13476dc986352dca4598e909384db043b4e2bb/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739", size = 370168 }, + { url = "https://files.pythonhosted.org/packages/dc/c9/5b9aa35acfb58946b4b785bc8e700ac313669e02fb100f3efa6176a83e81/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c", size = 371376 }, + { url = "https://files.pythonhosted.org/packages/7b/dd/0e0dbeb70d8a5357d2814764d467ded98d81d90d3570de4fb05ec7224f6b/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee", size = 397200 }, + { url = "https://files.pythonhosted.org/packages/e4/da/a47d931eb688ccfd77a7389e45935c79c41e8098d984d87335004baccb1d/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96", size = 426824 }, + { url = "https://files.pythonhosted.org/packages/0f/f7/a59a673594e6c2ff2dbc44b00fd4ecdec2fc399bb6a7bd82d612699a0121/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4", size = 357967 }, + { url = "https://files.pythonhosted.org/packages/5f/61/3ba1905396b2cb7088f9503a460b87da33452da54d478cb9241f6ad16d00/rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef", size = 378905 }, + { url = "https://files.pythonhosted.org/packages/08/31/6d0df9356b4edb0a3a077f1ef714e25ad21f9f5382fc490c2383691885ea/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821", size = 546348 }, + { url = "https://files.pythonhosted.org/packages/ae/15/d33c021de5cb793101df9961c3c746dfc476953dbbf5db337d8010dffd4e/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940", size = 553152 }, + { url = "https://files.pythonhosted.org/packages/70/2d/5536d28c507a4679179ab15aa0049440e4d3dd6752050fa0843ed11e9354/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", size = 528807 }, + { url = "https://files.pythonhosted.org/packages/e3/62/7ebe6ec0d3dd6130921f8cffb7e34afb7f71b3819aa0446a24c5e81245ec/rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", size = 200993 }, + { url = "https://files.pythonhosted.org/packages/ec/2f/b938864d66b86a6e4acadefdc56de75ef56f7cafdfd568a6464605457bd5/rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", size = 214458 }, + { url = "https://files.pythonhosted.org/packages/99/32/43b919a0a423c270a838ac2726b1c7168b946f2563fd99a51aaa9692d00f/rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", size = 321465 }, + { url = "https://files.pythonhosted.org/packages/58/a9/c4d899cb28e9e47b0ff12462e8f827381f243176036f17bef9c1604667f2/rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", size = 312900 }, + { url = "https://files.pythonhosted.org/packages/8f/90/9e51670575b5dfaa8c823369ef7d943087bfb73d4f124a99ad6ef19a2b26/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", size = 370973 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/523f2a03f853fc0d4c1acbef161747e9ab7df0a8abf6236106e333540921/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", size = 370890 }, + { url = "https://files.pythonhosted.org/packages/51/ca/2458a771f16b0931de4d384decbe43016710bc948036c8f4562d6e063437/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", size = 397174 }, + { url = "https://files.pythonhosted.org/packages/00/7d/6e06807f6305ea2408b364efb0eef83a6e21b5e7b5267ad6b473b9a7e416/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", size = 426449 }, + { url = "https://files.pythonhosted.org/packages/8c/d1/6c9e65260a819a1714510a7d69ac1d68aa23ee9ce8a2d9da12187263c8fc/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", size = 357698 }, + { url = "https://files.pythonhosted.org/packages/5d/fb/ecea8b5286d2f03eec922be7173a03ed17278944f7c124348f535116db15/rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", size = 378530 }, + { url = "https://files.pythonhosted.org/packages/e3/e3/ac72f858957f52a109c588589b73bd2fad4a0fc82387fb55fb34aeb0f9cd/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", size = 545753 }, + { url = "https://files.pythonhosted.org/packages/b2/a4/a27683b519d5fc98e4390a3b130117d80fd475c67aeda8aac83c0e8e326a/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", size = 552443 }, + { url = "https://files.pythonhosted.org/packages/a1/ed/c074d248409b4432b1ccb2056974175fa0af2d1bc1f9c21121f80a358fa3/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", size = 528380 }, + { url = "https://files.pythonhosted.org/packages/d5/bd/04caf938895d2d78201e89c0c8a94dfd9990c34a19ff52fb01d0912343e3/rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", size = 200540 }, + { url = "https://files.pythonhosted.org/packages/95/cc/109eb8b9863680411ae703664abacaa035820c7755acc9686d5dd02cdd2e/rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", size = 214111 }, +] + +[[package]] +name = "scipy" +version = "1.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/ab/070ccfabe870d9f105b04aee1e2860520460ef7ca0213172abfe871463b9/scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675", size = 39076999 }, + { url = "https://files.pythonhosted.org/packages/a7/c5/02ac82f9bb8f70818099df7e86c3ad28dae64e1347b421d8e3adf26acab6/scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2", size = 29894570 }, + { url = "https://files.pythonhosted.org/packages/ed/05/7f03e680cc5249c4f96c9e4e845acde08eb1aee5bc216eff8a089baa4ddb/scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617", size = 23103567 }, + { url = "https://files.pythonhosted.org/packages/5e/fc/9f1413bef53171f379d786aabc104d4abeea48ee84c553a3e3d8c9f96a9c/scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8", size = 25499102 }, + { url = "https://files.pythonhosted.org/packages/c2/4b/b44bee3c2ddc316b0159b3d87a3d467ef8d7edfd525e6f7364a62cd87d90/scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37", size = 35586346 }, + { url = "https://files.pythonhosted.org/packages/93/6b/701776d4bd6bdd9b629c387b5140f006185bd8ddea16788a44434376b98f/scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2", size = 41165244 }, + { url = "https://files.pythonhosted.org/packages/06/57/e6aa6f55729a8f245d8a6984f2855696c5992113a5dc789065020f8be753/scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2", size = 42817917 }, + { url = "https://files.pythonhosted.org/packages/ea/c2/5ecadc5fcccefaece775feadcd795060adf5c3b29a883bff0e678cfe89af/scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94", size = 44781033 }, + { url = "https://files.pythonhosted.org/packages/c0/04/2bdacc8ac6387b15db6faa40295f8bd25eccf33f1f13e68a72dc3c60a99e/scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", size = 39128781 }, + { url = "https://files.pythonhosted.org/packages/c8/53/35b4d41f5fd42f5781dbd0dd6c05d35ba8aa75c84ecddc7d44756cd8da2e/scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", size = 29939542 }, + { url = "https://files.pythonhosted.org/packages/66/67/6ef192e0e4d77b20cc33a01e743b00bc9e68fb83b88e06e636d2619a8767/scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", size = 23148375 }, + { url = "https://files.pythonhosted.org/packages/f6/32/3a6dedd51d68eb7b8e7dc7947d5d841bcb699f1bf4463639554986f4d782/scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc", size = 25578573 }, + { url = "https://files.pythonhosted.org/packages/f0/5a/efa92a58dc3a2898705f1dc9dbaf390ca7d4fba26d6ab8cfffb0c72f656f/scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310", size = 35319299 }, + { url = "https://files.pythonhosted.org/packages/8e/ee/8a26858ca517e9c64f84b4c7734b89bda8e63bec85c3d2f432d225bb1886/scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", size = 40849331 }, + { url = "https://files.pythonhosted.org/packages/a5/cd/06f72bc9187840f1c99e1a8750aad4216fc7dfdd7df46e6280add14b4822/scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", size = 42544049 }, + { url = "https://files.pythonhosted.org/packages/aa/7d/43ab67228ef98c6b5dd42ab386eae2d7877036970a0d7e3dd3eb47a0d530/scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", size = 44521212 }, + { url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 }, + { url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 }, + { url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 }, + { url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 }, + { url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 }, + { url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 }, + { url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 }, +] + +[[package]] +name = "setuptools" +version = "74.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/2c/f0a538a2f91ce633a78daaeb34cbfb93a54bd2132a6de1f6cec028eee6ef/setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6", size = 1356467 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/9c/9ad11ac06b97e55ada655f8a6bea9d1d3f06e120b178cd578d80e558191d/setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308", size = 1262071 }, +] + +[[package]] +name = "sgmllib3k" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750 } + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "tantivy" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/e2/0a7b8ce41e9717544648ce1fa511cd37f5d634cc6daef48c03157ec7462b/tantivy-0.22.0.tar.gz", hash = "sha256:dce07fa2910c94934aa3d96c91087936c24e4a5802d839625d67edc6d1c95e5c", size = 60732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/89/68db476f0fa84d67aa1e2887e4b52006b595a074767c26f8d9b0d3f513c9/tantivy-0.22.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:ec693abf38f229bc1361b0d34029a8bb9f3ee5bb956a3e745e0c4a66ea815bec", size = 3206695 }, + { url = "https://files.pythonhosted.org/packages/ff/58/703ab7b0c539b61e6f52872e534a19ce00c1cb0e961dad016a9978b526b8/tantivy-0.22.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e385839badc12b81e38bf0a4d865ee7c3a992fea9f5ce4117adae89369e7d1eb", size = 6233384 }, + { url = "https://files.pythonhosted.org/packages/27/58/1b855e6b9a4d3a0e4b774c89d6897e4909877eba8746f5c3764bd62e816f/tantivy-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6c097d94be1af106676c86c02b185f029484fdbd9a2b9f17cb980e840e7bdad", size = 4511919 }, + { url = "https://files.pythonhosted.org/packages/21/24/494683795959278520122fd5bf8bcc0a1e381a5df10fd00fb4334359a391/tantivy-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c47a5cdec306ea8594cb6e7effd4b430932ebfd969f9e8f99e343adf56a79bc9", size = 4487802 }, + { url = "https://files.pythonhosted.org/packages/b5/eb/41a2cdbcb473ee02c15e06afc55c49b8716ada775829f82fec28bfa88ac4/tantivy-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:ba0ca878ed025d79edd9c51cda80b0105be8facbaec180fea64a17b80c74e7db", size = 2592844 }, + { url = "https://files.pythonhosted.org/packages/ca/f0/88bb16d4e9728d6f2c53fa26cd9b14b98201dae636754155ea89918714d6/tantivy-0.22.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:925682f3acb65c85c2a5a5b131401b9f30c184ea68aa73a8cc7c2ea6115e8ae3", size = 3199219 }, + { url = "https://files.pythonhosted.org/packages/ab/2b/a361cc7228663f6fa14a82de6568bc728c8c2c212ca979d17482ea7cdffd/tantivy-0.22.0-cp312-cp312-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:d75760e45a329313001354d6ca415ff12d9d812343792ae133da6bfbdc4b04a5", size = 6216468 }, + { url = "https://files.pythonhosted.org/packages/2a/c6/ff057ac128dd0bd9508255af85235f176d0c27b4f9e668d22a39b7a5d248/tantivy-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd909d122b5af457d955552c304f8d5d046aee7024c703c62652ad72af89f3c7", size = 4508125 }, + { url = "https://files.pythonhosted.org/packages/f0/cc/b8f7faf35403b9ed5fd349a15cc8637ae3ce75983272fecc32f07b9dbe47/tantivy-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99266ffb204721eb2bd5b3184aa87860a6cff51b4563f808f78fa22d85a8093", size = 4479785 }, + { url = "https://files.pythonhosted.org/packages/90/1a/c52e93ebc2c4a3269e5c5d7c7bde179864d60b9468a621619968b634b702/tantivy-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:9ed6b813a1e7769444e33979b46b470b2f4c62d983c2560ce9486fb9be1491c9", size = 2591792 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "termcolor" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/56/d7d66a84f96d804155f6ff2873d065368b25a07222a6fd51c4f24ef6d764/termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a", size = 12664 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5f/8c716e47b3a50cbd7c146f45881e11d9414def768b7cd9c5e6650ec2a80a/termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63", size = 7719 }, +] + +[[package]] +name = "tiktoken" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6", size = 33437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/eb/57492b2568eea1d546da5cc1ae7559d924275280db80ba07e6f9b89a914b/tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f", size = 961468 }, + { url = "https://files.pythonhosted.org/packages/30/ef/e07dbfcb2f85c84abaa1b035a9279575a8da0236305491dc22ae099327f7/tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f", size = 907005 }, + { url = "https://files.pythonhosted.org/packages/ea/9b/f36db825b1e9904c3a2646439cb9923fc1e09208e2e071c6d9dd64ead131/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b", size = 1049183 }, + { url = "https://files.pythonhosted.org/packages/61/b4/b80d1fe33015e782074e96bbbf4108ccd283b8deea86fb43c15d18b7c351/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992", size = 1080830 }, + { url = "https://files.pythonhosted.org/packages/2a/40/c66ff3a21af6d62a7e0ff428d12002c4e0389f776d3ff96dcaa0bb354eee/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1", size = 1092967 }, + { url = "https://files.pythonhosted.org/packages/2e/80/f4c9e255ff236e6a69ce44b927629cefc1b63d3a00e2d1c9ed540c9492d2/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89", size = 1142682 }, + { url = "https://files.pythonhosted.org/packages/b1/10/c04b4ff592a5f46b28ebf4c2353f735c02ae7f0ce1b165d00748ced6467e/tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb", size = 799009 }, + { url = "https://files.pythonhosted.org/packages/1d/46/4cdda4186ce900608f522da34acf442363346688c71b938a90a52d7b84cc/tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908", size = 960446 }, + { url = "https://files.pythonhosted.org/packages/b6/30/09ced367d280072d7a3e21f34263dfbbf6378661e7a0f6414e7c18971083/tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410", size = 906652 }, + { url = "https://files.pythonhosted.org/packages/e6/7b/c949e4954441a879a67626963dff69096e3c774758b9f2bb0853f7b4e1e7/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704", size = 1047904 }, + { url = "https://files.pythonhosted.org/packages/50/81/1842a22f15586072280364c2ab1e40835adaf64e42fe80e52aff921ee021/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350", size = 1079836 }, + { url = "https://files.pythonhosted.org/packages/6d/87/51a133a3d5307cf7ae3754249b0faaa91d3414b85c3d36f80b54d6817aa6/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4", size = 1092472 }, + { url = "https://files.pythonhosted.org/packages/a5/1f/c93517dc6d3b2c9e988b8e24f87a8b2d4a4ab28920a3a3f3ea338397ae0c/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97", size = 1141881 }, + { url = "https://files.pythonhosted.org/packages/bf/4b/48ca098cb580c099b5058bf62c4cb5e90ca6130fa43ef4df27088536245b/tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f", size = 799281 }, +] + +[[package]] +name = "tokenizers" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/3a/508a4875f69e12b08fb3dabfc746039fe763838ff45d6e42229ed09a41c2/tokenizers-0.20.0.tar.gz", hash = "sha256:39d7acc43f564c274085cafcd1dae9d36f332456de1a31970296a6b8da4eac8d", size = 337421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f6/ae042eeae413bae9af5adceed7fe6f30fb0abc9868a55916d4e07c8ea1fb/tokenizers-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6636b798b3c4d6c9b1af1a918bd07c867808e5a21c64324e95318a237e6366c3", size = 2625296 }, + { url = "https://files.pythonhosted.org/packages/62/8b/dab4d716e9a00c1581443213283c9fdfdb982cdad6ecc046bae9c7e42fc8/tokenizers-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ec603e42eaf499ffd58b9258162add948717cf21372458132f14e13a6bc7172", size = 2516726 }, + { url = "https://files.pythonhosted.org/packages/95/1e/800e0896ea43ab86d70cfc6ed6a30d6aefcab498eff49db79cc92e08e1fe/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cce124264903a8ea6f8f48e1cc7669e5ef638c18bd4ab0a88769d5f92debdf7f", size = 2891801 }, + { url = "https://files.pythonhosted.org/packages/02/80/22ceab06d120df5b589f993248bceef177a932024ae8ee033ec3da5cc87f/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07bbeba0231cf8de07aa6b9e33e9779ff103d47042eeeb859a8c432e3292fb98", size = 2753762 }, + { url = "https://files.pythonhosted.org/packages/22/7c/02431f0711162ab3994e4099b9ece4b6a00755e3180bf5dfe70da0c13836/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06c0ca8397b35d38b83a44a9c6929790c1692957d88541df061cb34d82ebbf08", size = 3010928 }, + { url = "https://files.pythonhosted.org/packages/bc/14/193b7e58017e9592799498686df718c5f68bfb72205d3075ce9cdd441db7/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca6557ac3b83d912dfbb1f70ab56bd4b0594043916688e906ede09f42e192401", size = 3032435 }, + { url = "https://files.pythonhosted.org/packages/71/ae/c7fc7a614ce78cab7b8f82f7a24a074837cbc7e0086960cbe4801b2b3c83/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a5ad94c9e80ac6098328bee2e3264dbced4c6faa34429994d473f795ec58ef4", size = 3328437 }, + { url = "https://files.pythonhosted.org/packages/a5/0e/e4421e6b8c8b3ae093bef22faa28c50d7dbd654f661edc5f5880a93dbf10/tokenizers-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5c7f906ee6bec30a9dc20268a8b80f3b9584de1c9f051671cb057dc6ce28f6", size = 2936532 }, + { url = "https://files.pythonhosted.org/packages/b9/08/ac9c8fe9c1f5b4ef89bcbf543cda890e76c2ea1c2e957bf77fd5fcf72b6c/tokenizers-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:31e087e9ee1b8f075b002bfee257e858dc695f955b43903e1bb4aa9f170e37fe", size = 8965273 }, + { url = "https://files.pythonhosted.org/packages/fb/71/b9626f9f5a33dd1d80bb6d3721f0a4b0b48ced0c702e65aad5c8c7c1ae7e/tokenizers-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3124fb6f3346cb3d8d775375d3b429bf4dcfc24f739822702009d20a4297990", size = 9283768 }, + { url = "https://files.pythonhosted.org/packages/ba/78/70f79f939385579bb25f14cb14ab0eaa49e46a7d099577c2e08e3c3597d8/tokenizers-0.20.0-cp311-none-win32.whl", hash = "sha256:a4bb8b40ba9eefa621fdcabf04a74aa6038ae3be0c614c6458bd91a4697a452f", size = 2126085 }, + { url = "https://files.pythonhosted.org/packages/c0/3c/9228601e180b177755fd9f35cbb229c13f1919a55f07a602b1bd7d716470/tokenizers-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:2b709d371f1fe60a28ef0c5c67815952d455ca7f34dbe7197eaaed3cc54b658e", size = 2327670 }, + { url = "https://files.pythonhosted.org/packages/ce/d4/152f9964cee16b43b9147212e925793df1a469324b29b4c7a6cb60280c99/tokenizers-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:15c81a17d0d66f4987c6ca16f4bea7ec253b8c7ed1bb00fdc5d038b1bb56e714", size = 2613552 }, + { url = "https://files.pythonhosted.org/packages/6e/99/594b518d44ba2b099753816a9c0c33dbdcf77cc3ec5b256690f70d7431c2/tokenizers-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a531cdf1fb6dc41c984c785a3b299cb0586de0b35683842a3afbb1e5207f910", size = 2513918 }, + { url = "https://files.pythonhosted.org/packages/24/fa/77f0cf9b3c662b4de18953fb06126c424059f4b09ca2d1b720beabc6afde/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06caabeb4587f8404e0cd9d40f458e9cba3e815c8155a38e579a74ff3e2a4301", size = 2892465 }, + { url = "https://files.pythonhosted.org/packages/2d/e6/59abfc09f1dc23a47fd03dd8e3bf3fce67d9be2b8ba15a73c9a86b5a646c/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8768f964f23f5b9f50546c0369c75ab3262de926983888bbe8b98be05392a79c", size = 2750862 }, + { url = "https://files.pythonhosted.org/packages/0f/b2/f212ca05c1b246b9429905c18a4d68abacf2a35214eceedb1d65c6c37831/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:626403860152c816f97b649fd279bd622c3d417678c93b4b1a8909b6380b69a8", size = 3012971 }, + { url = "https://files.pythonhosted.org/packages/16/0b/099f5e5b97e8323837a5828f6d21f4bb2a3b529507dc19bd274e48e15825/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c1b88fa9e5ff062326f4bf82681da5a96fca7104d921a6bd7b1e6fcf224af26", size = 3038445 }, + { url = "https://files.pythonhosted.org/packages/62/7c/4e3cb25dc1c5eea6053752f55007071da6b33a96021e0cea4b45b6ef0908/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7e559436a07dc547f22ce1101f26d8b2fad387e28ec8e7e1e3b11695d681d8", size = 3329352 }, + { url = "https://files.pythonhosted.org/packages/32/20/a8fe63317d4f3c015cbd5b6dec0ce08e2722685ca836ad4a44dec53d000f/tokenizers-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48afb75e50449848964e4a67b0da01261dd3aa8df8daecf10db8fd7f5b076eb", size = 2938786 }, + { url = "https://files.pythonhosted.org/packages/06/e8/78f1c0f356d0a6e4e4e450e2419ace1918bfab875100c3047021a8261ba0/tokenizers-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:baf5d0e1ff44710a95eefc196dd87666ffc609fd447c5e5b68272a7c3d342a1d", size = 8967350 }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3a1edfc1ffb876ffc1f668c8fa2b2ffb57edf8e9188af49218cf41f9cd9f/tokenizers-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e5e56df0e8ed23ba60ae3848c3f069a0710c4b197218fe4f89e27eba38510768", size = 9284785 }, + { url = "https://files.pythonhosted.org/packages/00/75/426a93399ba5e6e879215e1abb696adb83b1e2a98d65b47b8ba4262b3d17/tokenizers-0.20.0-cp312-none-win32.whl", hash = "sha256:ec53e5ecc142a82432f9c6c677dbbe5a2bfee92b8abf409a9ecb0d425ee0ce75", size = 2125012 }, + { url = "https://files.pythonhosted.org/packages/a5/45/9c19187645401ec30884379ada74aa6e71fb5eaf20485a82ea37a0fd3659/tokenizers-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:f18661ece72e39c0dfaa174d6223248a15b457dbd4b0fc07809b8e6d3ca1a234", size = 2314154 }, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/83/6ba9844a41128c62e810fddddd72473201f3eacde02046066142a2d96cc5/tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad", size = 169504 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20240808" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/08/6f5737f645571b7a0b1ebd2fe8b5cf1ee4ec3e707866ca96042a86fc1d10/types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", size = 12359 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/ad/ffbad24e2bc8f20bf047ec22af0c0a92f6ce2071eb21c9103df600cda6de/types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35", size = 15298 }, +] + +[[package]] +name = "types-setuptools" +version = "74.1.0.20240907" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/80/7098cc47e64861d9591dd18ad2dffde9b63da6037dbad38f2eadd1124866/types-setuptools-74.1.0.20240907.tar.gz", hash = "sha256:0abdb082552ca966c1e5fc244e4853adc62971f6cd724fb1d8a3713b580e5a65", size = 42970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/5a/636fbcd7baf3d4d9f971581587bafba37d8b3dff498233be43f394e0eb9f/types_setuptools-74.1.0.20240907-py3-none-any.whl", hash = "sha256:15b38c8e63ca34f42f6063ff4b1dd662ea20086166d5ad6a102e670a52574120", size = 68092 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, +] + +[[package]] +name = "urllib3" +version = "1.26.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225 }, +] + +[[package]] +name = "usearch" +version = "2.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "tqdm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/29/fbcda825d7b14f24f3b9b543f88415ddbc5e7fa1f64394064fb0d2ba4d93/usearch-2.15.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0c403f10a1f810f97f628a56a051b62eb03a8312082c3d7e1613742eb9d9ef15", size = 704398 }, + { url = "https://files.pythonhosted.org/packages/60/2d/12eecee36caba60e3ccef2ac753a936bdde2f34d9ae6e1381141547e7ad7/usearch-2.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf1ab2b68a2a1ae8175e60529b8061839e53a528ea192514ca0880e070d9f8e3", size = 377817 }, + { url = "https://files.pythonhosted.org/packages/3a/62/371dd595b7dc2d2a030f61607cf6c3bb58e31212eebfab0c8207498f8226/usearch-2.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93bb7e650b8990f1105fdf37b628d8b06c212231a74ff2eaa1b0768a8faebeba", size = 364648 }, + { url = "https://files.pythonhosted.org/packages/a3/6c/2946088dacbfa6e7deb8e2f4a659a6a08700c65806130e7875457657fae8/usearch-2.15.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e31ad6ffd8a49b933c4507a980b1e36eca42ca978f615f24e7f495b70c41c939", size = 1268388 }, + { url = "https://files.pythonhosted.org/packages/dd/fb/5500d0d04a9694dbf0dc7d5a425953e5de2b79000f6052fe1432a6f30d11/usearch-2.15.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eebfb4b150cb1d4651996c1a2b49c9576529cf3d2b67b8c7f5f26df9d2eae411", size = 1461215 }, + { url = "https://files.pythonhosted.org/packages/ea/dd/c4c9001a45e174a5c075b0dec76b00fd7e8ad5efbcbd5df4e8ce9b2f09b2/usearch-2.15.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:672ac913a23ef32fa1e3d8a5dc73373c0cc3ddf1609a3964c2f2caae24715f67", size = 2199161 }, + { url = "https://files.pythonhosted.org/packages/c5/94/4c8230720e8400478fc5662a2a87783e0c62f23f338244cd56b9beea721d/usearch-2.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b42bb7317a770e975429a2c0bb9abd11e42ea010a4e68a916b7476dd20fd2f62", size = 2330982 }, + { url = "https://files.pythonhosted.org/packages/19/d8/f1b058b1d6fc6cd819d3647d5603233eba17786a61bf9c1221f2213ad9bb/usearch-2.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:bbdc71cc459daaf937d1fbc812b760af950a246df144e0ce833bc51953fcabb7", size = 281856 }, + { url = "https://files.pythonhosted.org/packages/6b/96/7b5e3935604615f8789ce29cb863d337751881ea25962cd90dcf0a436c32/usearch-2.15.1-cp311-cp311-win_arm64.whl", hash = "sha256:50a23312061d90a5eb619fca4fa1636624e8932e622186b955bac4910a3c8b17", size = 261995 }, + { url = "https://files.pythonhosted.org/packages/64/b0/d2caba6577de9ee68361192199fc604264a86d0e08b68d4d12504679e699/usearch-2.15.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c762770548b94e7cae749f1e4357b00d5a9f2ddcd8615c495d6d15bc54d9dce2", size = 710219 }, + { url = "https://files.pythonhosted.org/packages/42/d8/7648c436c39eb90016c5d1001ea2116668f4874312d2affd3819ad3064d2/usearch-2.15.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cd4d7163aaa972906212d90ba0c128c18418b0a093f7410003b02f9316937035", size = 381559 }, + { url = "https://files.pythonhosted.org/packages/b6/42/daeab4558552784bb231f96c1845498286c3a2f2680fc396697f13ef9262/usearch-2.15.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61190502a8448b8ceed807adde69a951ee423cae85955486264c3b8b3db9b50e", size = 366311 }, + { url = "https://files.pythonhosted.org/packages/bd/2d/1f773b009cd6b668fc4865ce16628f33061323b5e493880663e4f3ee554c/usearch-2.15.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa324fb6a086f44e53a1ef480e3857d8a6db948e829916767af27c32a6b8c33f", size = 1269633 }, + { url = "https://files.pythonhosted.org/packages/bb/4c/dbd2f86ebb83393a47fd966b490840cea94f39e82c9f4e2e9ccb3c7284f6/usearch-2.15.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8cb9d0fff2b73c23b1344ed75d382af8b0f426b85db2f2a6a80816af8a7720d4", size = 1465092 }, + { url = "https://files.pythonhosted.org/packages/82/e4/c5f2842da05b3d9e37b72bcee7d91e860871eeba60a2f0f2418651d94938/usearch-2.15.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:93134604a1dbefd2b83b9eb9a71f80d9a9aac1db94c3c5cfd18ecc6cff1a4d44", size = 2198308 }, + { url = "https://files.pythonhosted.org/packages/69/41/c4905f3e9c5bb72ed88e99ac0358e3d0d69b486378d6194ecc555825ce94/usearch-2.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e961ef7e5b408d9aa3706f945bef7eda36a3d72411b79118101ff7c722df987", size = 2334937 }, + { url = "https://files.pythonhosted.org/packages/f8/23/08054d4cb19ea4c59731dc72176a65723951b9745ac281b745423f758a57/usearch-2.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:4ed5c05f460c4202d9e13f173b3c0895f4195f1107c2671d23dfc53b4e7a0007", size = 283118 }, + { url = "https://files.pythonhosted.org/packages/24/ff/0e5232ed5c539afc0ce1c07e7fc046a01d4769d601e0fe30d83458efa447/usearch-2.15.1-cp312-cp312-win_arm64.whl", hash = "sha256:d32e1cd5624efe64c735f70f34ff969a628ccee964f213d58c6fb8bab1d6244e", size = 262874 }, +] + +[[package]] +name = "vcrpy" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "urllib3", marker = "platform_python_implementation == 'PyPy'" }, + { name = "wrapt" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/59/9fe85bf7af469bdb0ab8416c76cde630cdff6d1790ecb87e5a58f259c89c/vcrpy-6.0.1.tar.gz", hash = "sha256:9e023fee7f892baa0bbda2f7da7c8ac51165c1c6e38ff8688683a12a4bde9278", size = 84836 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/eb/922cfd27d6593363c3e50b7808bcc234ec996128813fd34341685bb307b7/vcrpy-6.0.1-py2.py3-none-any.whl", hash = "sha256:621c3fb2d6bd8aa9f87532c688e4575bcbbde0c0afeb5ebdb7e14cac409edfdd", size = 41880 }, +] + +[[package]] +name = "virtualenv" +version = "20.26.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/8a/134f65c3d6066153b84fc176c58877acd8165ed0b79a149ff50502597284/virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c", size = 9385017 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/ea/12f774a18b55754c730c8383dad8f10d7b87397d1cb6b2b944c87381bb3b/virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55", size = 6013327 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "wrapt" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", size = 53972 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/03/c188ac517f402775b90d6f312955a5e53b866c964b32119f2ed76315697e/wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", size = 37313 }, + { url = "https://files.pythonhosted.org/packages/0f/16/ea627d7817394db04518f62934a5de59874b587b792300991b3c347ff5e0/wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", size = 38164 }, + { url = "https://files.pythonhosted.org/packages/7f/a7/f1212ba098f3de0fd244e2de0f8791ad2539c03bef6c05a9fcb03e45b089/wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", size = 80890 }, + { url = "https://files.pythonhosted.org/packages/b7/96/bb5e08b3d6db003c9ab219c487714c13a237ee7dcc572a555eaf1ce7dc82/wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", size = 73118 }, + { url = "https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", size = 80746 }, + { url = "https://files.pythonhosted.org/packages/11/fb/18ec40265ab81c0e82a934de04596b6ce972c27ba2592c8b53d5585e6bcd/wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", size = 85668 }, + { url = "https://files.pythonhosted.org/packages/0f/ef/0ecb1fa23145560431b970418dce575cfaec555ab08617d82eb92afc7ccf/wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", size = 78556 }, + { url = "https://files.pythonhosted.org/packages/25/62/cd284b2b747f175b5a96cbd8092b32e7369edab0644c45784871528eb852/wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", size = 85712 }, + { url = "https://files.pythonhosted.org/packages/e5/a7/47b7ff74fbadf81b696872d5ba504966591a3468f1bc86bca2f407baef68/wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", size = 35327 }, + { url = "https://files.pythonhosted.org/packages/cf/c3/0084351951d9579ae83a3d9e38c140371e4c6b038136909235079f2e6e78/wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", size = 37523 }, + { url = "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", size = 37614 }, + { url = "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", size = 38316 }, + { url = "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", size = 86322 }, + { url = "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", size = 79055 }, + { url = "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", size = 87291 }, + { url = "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", size = 90374 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", size = 83896 }, + { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738 }, + { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568 }, + { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653 }, + { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362 }, +] + +[[package]] +name = "yarl" +version = "1.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/3d/4924f9ed49698bac5f112bc9b40aa007bbdcd702462c1df3d2e1383fb158/yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53", size = 162095 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/f1/f3e6be722461cab1e7c6aea657685897956d6e4743940d685d167914e31c/yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68", size = 188410 }, + { url = "https://files.pythonhosted.org/packages/4b/c1/21cc66b263fdc2ec10b6459aed5b239f07eed91a77438d88f0e1bd70e202/yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe", size = 114293 }, + { url = "https://files.pythonhosted.org/packages/31/7a/0ecab63a166a22357772f4a2852c859e2d5a7b02a5c58803458dd516e6b4/yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675", size = 112548 }, + { url = "https://files.pythonhosted.org/packages/57/5d/78152026864475e841fdae816499345364c8e364b45ea6accd0814a295f0/yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63", size = 485002 }, + { url = "https://files.pythonhosted.org/packages/d3/70/2e880d74aeb4908d45c6403e46bbd4aa866ae31ddb432318d9b8042fe0f6/yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27", size = 504850 }, + { url = "https://files.pythonhosted.org/packages/06/58/5676a47b6d2751853f89d1d68b6a54d725366da6a58482f2410fa7eb38af/yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5", size = 499291 }, + { url = "https://files.pythonhosted.org/packages/4d/e5/b56d535703a63a8d86ac82059e630e5ba9c0d5626d9c5ac6af53eed815c2/yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92", size = 487818 }, + { url = "https://files.pythonhosted.org/packages/f3/b4/6b95e1e0983593f4145518980b07126a27e2a4938cb6afb8b592ce6fc2c9/yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b", size = 470447 }, + { url = "https://files.pythonhosted.org/packages/a8/e5/5d349b7b04ed4247d4f717f271fce601a79d10e2ac81166c13f97c4973a9/yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a", size = 484544 }, + { url = "https://files.pythonhosted.org/packages/fa/dc/ce90e9d85ef2233e81148a9658e4ea8372c6de070ce96c5c8bd3ff365144/yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83", size = 482409 }, + { url = "https://files.pythonhosted.org/packages/4c/a1/17c0a03615b0cd213aee2e318a0fbd3d07259c37976d85af9eec6184c589/yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff", size = 512970 }, + { url = "https://files.pythonhosted.org/packages/6c/ed/1e317799d54c79a3e4846db597510f5c84fb7643bb8703a3848136d40809/yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c", size = 515203 }, + { url = "https://files.pythonhosted.org/packages/7a/37/9a4e2d73953956fa686fa0f0c4a0881245f39423fa75875d981b4f680611/yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e", size = 497323 }, + { url = "https://files.pythonhosted.org/packages/a3/c3/a25ae9c85c0e50a8722aecc486ac5ba53b28d1384548df99b2145cb69862/yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6", size = 101226 }, + { url = "https://files.pythonhosted.org/packages/90/6d/c62ba0ae0232a0b0012706a7735a16b44a03216fedfb6ea0bcda79d1e12c/yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b", size = 110471 }, + { url = "https://files.pythonhosted.org/packages/3b/05/379002019a0c9d5dc0c4cc6f71e324ea43461ae6f58e94ee87e07b8ffa90/yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0", size = 189044 }, + { url = "https://files.pythonhosted.org/packages/23/d5/e62cfba5ceaaf92ee4f9af6f9c9ab2f2b47d8ad48687fa69570a93b0872c/yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265", size = 114867 }, + { url = "https://files.pythonhosted.org/packages/b1/10/6abc0bd7e7fe7c6b9b9e9ce0ff558912c9ecae65a798f5442020ef9e4177/yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867", size = 112737 }, + { url = "https://files.pythonhosted.org/packages/37/a5/ad026afde5efe1849f4f55bd9f9a2cb5b006511b324db430ae5336104fb3/yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd", size = 482887 }, + { url = "https://files.pythonhosted.org/packages/f8/82/b8bee972617b800319b4364cfcd69bfaf7326db052e91a56e63986cc3e05/yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef", size = 498635 }, + { url = "https://files.pythonhosted.org/packages/af/ad/ac688503b134e02e8505415f0b8e94dc8e92a97e82abdd9736658389b5ae/yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8", size = 496198 }, + { url = "https://files.pythonhosted.org/packages/ce/f2/b6cae0ad1afed6e95f82ab2cb9eb5b63e41f1463ece2a80c39d80cf6167a/yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870", size = 489068 }, + { url = "https://files.pythonhosted.org/packages/c8/f4/355e69b5563154b40550233ffba8f6099eac0c99788600191967763046cf/yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2", size = 468286 }, + { url = "https://files.pythonhosted.org/packages/26/3d/3c37f3f150faf87b086f7915724f2fcb9ff2f7c9d3f6c0f42b7722bd9b77/yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/94/ee/d591abbaea3b14e0f68bdec5cbcb75f27107190c51889d518bafe5d8f120/yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa", size = 484947 }, + { url = "https://files.pythonhosted.org/packages/57/70/ad1c65a13315f03ff0c63fd6359dd40d8198e2a42e61bf86507602a0364f/yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff", size = 505610 }, + { url = "https://files.pythonhosted.org/packages/4c/8c/6086dec0f8d7df16d136b38f373c49cf3d2fb94464e5a10bf788b36f3f54/yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239", size = 515951 }, + { url = "https://files.pythonhosted.org/packages/49/79/e0479e9a3bbb7bdcb82779d89711b97cea30902a4bfe28d681463b7071ce/yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45", size = 501273 }, + { url = "https://files.pythonhosted.org/packages/8e/85/eab962453e81073276b22f3d1503dffe6bfc3eb9cd0f31899970de05d490/yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447", size = 101139 }, + { url = "https://files.pythonhosted.org/packages/5d/de/618b3e5cab10af8a2ed3eb625dac61c1d16eb155d1f56f9fdb3500786c12/yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639", size = 110504 }, + { url = "https://files.pythonhosted.org/packages/07/b7/948e4f427817e0178f3737adf6712fea83f76921e11e2092f403a8a9dc4a/yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c", size = 185061 }, + { url = "https://files.pythonhosted.org/packages/f3/67/8d91ad79a3b907b4fef27fafa912350554443ba53364fff3c347b41105cb/yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e", size = 113056 }, + { url = "https://files.pythonhosted.org/packages/a1/77/6b2348a753702fa87f435cc33dcec21981aaca8ef98a46566a7b29940b4a/yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93", size = 110958 }, + { url = "https://files.pythonhosted.org/packages/8e/3e/6eadf32656741549041f549a392f3b15245d3a0a0b12a9bc22bd6b69621f/yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d", size = 470326 }, + { url = "https://files.pythonhosted.org/packages/3d/a4/1b641a8c7899eeaceec45ff105a2e7206ec0eb0fb9d86403963cc8521c5e/yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7", size = 484778 }, + { url = "https://files.pythonhosted.org/packages/8a/f5/80c142f34779a5c26002b2bf1f73b9a9229aa9e019ee6f9fd9d3e9704e78/yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089", size = 485568 }, + { url = "https://files.pythonhosted.org/packages/f8/f2/6b40ffea2d5d3a11f514ab23c30d14f52600c36a3210786f5974b6701bb8/yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5", size = 477801 }, + { url = "https://files.pythonhosted.org/packages/4c/1a/e60c116f3241e4842ed43c104eb2751abe02f6bac0301cdae69e4fda9c3a/yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5", size = 455361 }, + { url = "https://files.pythonhosted.org/packages/b9/98/fe0aeee425a4bc5cd3ed86e867661d2bfa782544fa07a8e3dcd97d51ae3d/yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786", size = 473893 }, + { url = "https://files.pythonhosted.org/packages/6b/9b/677455d146bd3cecd350673f0e4bb28854af66726493ace3b640e9c5552b/yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318", size = 476407 }, + { url = "https://files.pythonhosted.org/packages/33/ca/ce85766247a9a9b56654428fb78a3e14ea6947a580a9c4e891b3aa7da322/yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82", size = 490848 }, + { url = "https://files.pythonhosted.org/packages/6d/d6/717f0f19bcf2c4705ad95550b4b6319a0d8d1d4f137ea5e223207f00df50/yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a", size = 501084 }, + { url = "https://files.pythonhosted.org/packages/14/b5/b93c70d9a462b802c8df65c64b85f49d86b4ba70c393fbad95cf7ec053cb/yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da", size = 491776 }, + { url = "https://files.pythonhosted.org/packages/03/0f/5a52eaa402a6a93265ba82f42c6f6085ccbe483e1b058ad34207e75812b1/yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979", size = 485250 }, + { url = "https://files.pythonhosted.org/packages/dd/97/946d26a5d82706a6769399cabd472c59f9a3227ce1432afb4739b9c29572/yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367", size = 492590 }, + { url = "https://files.pythonhosted.org/packages/5b/b3/841f7d706137bdc8b741c6826106b6f703155076d58f1830f244da857451/yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38", size = 38648 }, +] + +[[package]] +name = "zipp" +version = "3.20.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/8b/1239a3ef43a0d0ebdca623fb6413bc7702c321400c5fdd574f0b7aa0fbb4/zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b", size = 23848 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/9e/c96f7a4cd0bf5625bb409b7e61e99b1130dc63a98cb8b24aeabae62d43e8/zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064", size = 8988 }, +]