diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..15623d28d1638062f1aeeee064f53daa3127f86a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +__pycache__/ +*.egg-info +.ipynb_checkpoints/ diff --git a/duckdb-nsql/LICENSE b/duckdb-nsql/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/duckdb-nsql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/duckdb-nsql/README.md b/duckdb-nsql/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bc0c9c7006cba3173edb2f373b32b3bfc1f52825 --- /dev/null +++ b/duckdb-nsql/README.md @@ -0,0 +1,63 @@ +# DuckDB-NSQL +Numbers Station Text to SQL model for DuckDB. + +NSQL is a family of autoregressive open-source foundational models (FMs) that are particularly designed for SQL generation tasks. We are thrilled to introduce DuckDB-NSQL in this repository, an FM tailored for local DuckDB SQL analytics tasks. All model weights can be found on HuggingFace. + +| Model Name | Size | Link | +| --------------------------------------| ---- | -------------------------------------------------------------- | +| motherduckdb/DuckDB-NSQL-7B-v0.1 | 7B | [link](https://huggingface.co/motherduckdb/DuckDB-NSQL-7B-v0.1) | +| motherduckdb/DuckDB-NSQL-7B-v0.1-GGUF | 7B | [link](https://huggingface.co/motherduckdb/DuckDB-NSQL-7B-v0.1-GGUF)| + +## Setup +To install all the necessary dependencies, please run +``` +pip install -r requirements.txt +``` + +## Usage +Please refer to the examples in the `examples/` folder to learn how to connect to a local DuckDB and directly query your data. A simple notebook is provided in the `examples/` directory for reference. + +To host the model with llama.cpp, please execute the following: + +```python +# Import necessary modules +from llama_cpp import Llama +from wurlitzer import pipes + +# Set up client with model path and context size +with pipes() as (out, err): + client = Llama( + model_path="DuckDB-NSQL-7B-v0.1-q8_0.gguf", + n_ctx=2048, + ) +``` + +To load the DuckDB database and query against it, please execute the following: + +```python +# Import necessary modules +import duckdb +from utils import generate_sql + +# Connect to DuckDB database +con = duckdb.connect("nyc.duckdb") + +# Sample question for SQL generation +question = "alter taxi table and add struct column with name test and keys a:int, b:double" + +# Generate SQL, check validity, and print +sql = generate_sql(question, con, client) +print(sql) +``` + +## Training Data + +The training data for this model consists of two parts: 1) 200k synthetically generated DuckDB SQL queries, based on the DuckDB v.0.9.2 documentation, and 2) labeled text-to-SQL pairs from [NSText2SQL](https://huggingface.co/datasets/NumbersStation/NSText2SQL) transpiled to DuckDB SQL using [sqlglot](https://github.com/tobymao/sqlglot). + +## Evaluate the benchmark + +Please refer to the `eval/` folder to check the details for evaluating the model against our proposed DuckDB benchmark. + +## Acknowledgement + +We would like to express our appreciation to all authors of the evaluation scripts. Their work made this project possible. diff --git a/duckdb-nsql/eval/README.md b/duckdb-nsql/eval/README.md new file mode 100644 index 0000000000000000000000000000000000000000..96dfd45fdf6506e4cb878d5054416649aed85531 --- /dev/null +++ b/duckdb-nsql/eval/README.md @@ -0,0 +1,90 @@ +This folder contains the suite for evaluating the DuckDB-Text2SQL model. + +Please install the dependencies listed in the requirements.txt file located in the parent folder. + +## Setup +To evaluate against the benchmark dataset, you need to prepare the evaluation script using this benchmark. + +``` +mkdir metrics +cd metrics +git clone git@github.com:ElementAI/test-suite-sql-eval.git test_suite_sql_eval +cd .. +``` + +You need to add a new remote to evaluate against duckdb in the test-suite-sql-eval folder. And check the latest duckdb-only branch (640a12975abf75a94e917caca149d56dbc6bcdd7). + +``` +git remote add till https://github.com/tdoehmen/test-suite-sql-eval.git +git fetch till +git checkout till/duckdb-only +``` + +Next, prepare the docs for retrieval. +``` +mkdir docs +cd docs +git clone https://github.com/duckdb/duckdb-web.git +cd .. +``` + +#### Dataset +The benchmark dataset is located in the `data/` folder and includes all databases (`data/databases`), table schemas (`data/tables.json`), and examples (`data/dev.json`). + +#### Eval +Start a manifest session with the model you want to evaluate. + +```bash +python -m manifest.api.app \ + --model_type huggingface \ + --model_generation_type text-generation \ + --model_name_or_path motherduckdb/DuckDB-NSQL-7B-v0.1 \ + --fp16 \ + --device 0 +``` + +Then, from the `DuckDB-NSQL` main folder, run: + +```bash +python eval/predict.py \ + predict \ + eval/data/dev.json \ + eval/data/tables.json \ + --output-dir output/ \ + --stop-tokens ';' \ + --stop-tokens '--' \ + --stop-tokens '```' \ + --stop-tokens '###' \ + --overwrite-manifest \ + --manifest-client huggingface \ + --manifest-connection http://localhost:5000 \ + --prompt-format duckdbinst +``` +This will format the prompt using the duckdbinst style. + +To evaluate the prediction, first run the following in a Python shell: + +```python +try: + import duckdb + + con = duckdb.connect() + con.install_extension("httpfs") + con.load_extension("httpfs") +except Exception as e: + print(f"Error loading duckdb extensions: {e}") +``` + +Then, run the evaluation script: + +```bash +python eval/evaluate.py \ + evaluate \ + --gold eval/data/dev.json \ + --db eval/data/databases/ \ + --tables eval/data/tables.json \ + --output-dir output/ \ + --pred [PREDICITON_FILE] +``` + +To view the output, all the information is located in the prediction file in the [output-dir]. Here, `query` is gold and `pred` is predicted. diff --git a/duckdb-nsql/eval/constants.py b/duckdb-nsql/eval/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..2eef83af1224445e1d38d0a4ab531590de12e473 --- /dev/null +++ b/duckdb-nsql/eval/constants.py @@ -0,0 +1,37 @@ +"""Constants.""" + +from prompt_formatters import ( + DuckDBFormatter, + MotherDuckFormatter, + DuckDBInstFormatter, + DuckDBInstNoShorthandFormatter, + RajkumarFormatter, + DuckDBChat, + DuckDBInstFormatterLlamaShort, + DuckDBInstFormatterGraniteShort, + DuckDBInstFormatterLlama, + DuckDBInstFormatterLlamaBasic, + DuckDBInstFormatterGranite, + DuckDBInstFormatterPhi, + DuckDBInstFormatterGPTmini, + DuckDBInstFormatterPhiAzure, + DuckDBInstFormatterLlamaSyntax, +) + +PROMPT_FORMATTERS = { + "rajkumar": RajkumarFormatter, + "duckdb": DuckDBFormatter, + "motherduck": MotherDuckFormatter, + "duckdbinst": DuckDBInstFormatter, + "duckdbinstllamashort": DuckDBInstFormatterLlamaShort, + "duckdbinstgraniteshort": DuckDBInstFormatterGraniteShort, + "duckdbinstllama": DuckDBInstFormatterLlama, + "duckdbinstgranite": DuckDBInstFormatterGranite, + "duckdbinstnoshort": DuckDBInstNoShorthandFormatter, + "duckdbchat": DuckDBChat, + "duckdbinstphi": DuckDBInstFormatterPhi, + "duckdbinstgptmini": DuckDBInstFormatterPhi, + "duckdbinstphiazure": DuckDBInstFormatterPhiAzure, + "duckdbinstllamabasic": DuckDBInstFormatterLlamaBasic, + "duckdbinstllamasyntax": DuckDBInstFormatterLlamaSyntax +} diff --git a/duckdb-nsql/eval/data/databases/flightinfo/flightinfo.duckdb b/duckdb-nsql/eval/data/databases/flightinfo/flightinfo.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..4bd4d41ec1a7d3baac0f389b33f1ba4e8e443984 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/flightinfo/flightinfo.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/hn/hn.duckdb b/duckdb-nsql/eval/data/databases/hn/hn.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..272c27adf35bc35c24d955310bfcfff121c868d8 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/hn/hn.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/json/json.duckdb b/duckdb-nsql/eval/data/databases/json/json.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..818e2761c52ddd0c1239163c6e6ab99d3c4bbf8a Binary files /dev/null and b/duckdb-nsql/eval/data/databases/json/json.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/laptop/laptop.duckdb b/duckdb-nsql/eval/data/databases/laptop/laptop.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..4a288a19fe1132951e6b7ea648a583bb09c7ad38 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/laptop/laptop.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/laptop_array/laptop_array.duckdb b/duckdb-nsql/eval/data/databases/laptop_array/laptop_array.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..bc1026f985f1ce85bd0cf7533804697a42a81063 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/laptop_array/laptop_array.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/laptop_json/laptop_json.duckdb b/duckdb-nsql/eval/data/databases/laptop_json/laptop_json.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..568c7870634d557917d71bfd20d03176690fb4ec Binary files /dev/null and b/duckdb-nsql/eval/data/databases/laptop_json/laptop_json.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/laptop_struct/laptop_struct.duckdb b/duckdb-nsql/eval/data/databases/laptop_struct/laptop_struct.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..c06b729b1fdde7ff9c772f86749eded127f9e72d Binary files /dev/null and b/duckdb-nsql/eval/data/databases/laptop_struct/laptop_struct.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/none/none.duckdb b/duckdb-nsql/eval/data/databases/none/none.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..ababe675cb4d91b04d53fbe3248702064e5d6695 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/none/none.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/nyc/nyc.duckdb b/duckdb-nsql/eval/data/databases/nyc/nyc.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..0653662f9b11f1db2b50f5be4e2520efba7dc046 Binary files /dev/null and b/duckdb-nsql/eval/data/databases/nyc/nyc.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/product/product.duckdb b/duckdb-nsql/eval/data/databases/product/product.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..dfa4860db09a8dc08f36c0f7bf7773e3f969335e Binary files /dev/null and b/duckdb-nsql/eval/data/databases/product/product.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/transactions/transactions.duckdb b/duckdb-nsql/eval/data/databases/transactions/transactions.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..8fc2f0cd968a6cc56c6096a81227feb44ef09b6f Binary files /dev/null and b/duckdb-nsql/eval/data/databases/transactions/transactions.duckdb differ diff --git a/duckdb-nsql/eval/data/databases/who/who.duckdb b/duckdb-nsql/eval/data/databases/who/who.duckdb new file mode 100644 index 0000000000000000000000000000000000000000..60528a49b7dca642fde9bdbe887f45c8a59da6ab Binary files /dev/null and b/duckdb-nsql/eval/data/databases/who/who.duckdb differ diff --git a/duckdb-nsql/eval/data/dev.json b/duckdb-nsql/eval/data/dev.json new file mode 100644 index 0000000000000000000000000000000000000000..c9ca8d5214d400437c9244d7381df2b6175d3aa0 --- /dev/null +++ b/duckdb-nsql/eval/data/dev.json @@ -0,0 +1,602 @@ +[ + { + "db_id": "hn", + "query": "SELECT COUNT(*) as domain_count, \nSUBSTRING(SPLIT_PART(url, '//', 2), 1, POSITION('/' IN SPLIT_PART(url, '//', 2)) - 1) as domain \nFROM hacker_news\nWHERE url IS NOT NULL GROUP BY domain ORDER BY domain_count DESC LIMIT 10;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "what are the top domains being shared on hacker_news?", + "category": "hard" + }, + { + "db_id": "laptop", + "query": "SELECT c.firstname, c.lastname, COUNT(*) AS num_pcs_bought\nFROM customers c\nJOIN sales s ON c.customer_id = s.customer_id\nJOIN pcs p ON s.model = p.model\nGROUP BY c.customer_id, c.firstname, c.lastname\nORDER BY num_pcs_bought DESC\nLIMIT 1;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Who bought the most PCs, print also the users name?", + "category": "medium" + }, + { + "db_id": "transactions", + "query": "select users.id, users.name, sum(transactions.amount) as balance from users join transactions on users.id = transactions.user_id group by users.id, users.name having balance = 0", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "list the names off account holders who have negative balances", + "category": "easy" + }, + { + "db_id": "laptop", + "query": "SELECT model FROM products WHERE maker = 'B';", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "List only the model number of all products made by maker B.", + "category": "easy" + }, + { + "db_id": "laptop", + "query": "SELECT model FROM products WHERE maker <> 'B';", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "List the model numbers of all products not made by maker B.", + "category": "easy" + }, + { + "db_id": "laptop", + "query": "SELECT AVG(speed) FROM pcs WHERE speed >= 3.00", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Return the average speed all PCs with speed >= 3.00", + "category": "easy" + }, + { + "db_id": "laptop", + "query": "SELECT MAX(price) FROM printers WHERE color = 'TRUE' AND type='laser'", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Return the price of the most expensive color laser printer", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT MIN(paid) FROM sales WHERE type_of_payment LIKE '%visa%'", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Return the minimum amount paid by customers who used a visa card (debit or credit) to purchase a product", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT customer_id FROM customers WHERE firstname LIKE '%e%' OR lastname LIKE '%e%'", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find the customer_id of customers who have the letter 'e' either in their first name or in their last name", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT model, price/0.85 AS 'price (USD)' FROM laptops WHERE ram >= 1024", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Assume all prices in the table Laptops are in Euro. List the prices of laptops with at least 1024 ram. You should return the price in USD in a column called 'price (USD)'. Assume that 1 USD = 0.85 EURO. Name the price column 'price (USD)'.", + "category": "hard" + }, + { + "db_id": "laptop", + "query": "SELECT maker FROM products GROUP BY maker HAVING COUNT(maker) > 4;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Return a list of makers that make more than four different products.", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT model FROM laptops WHERE speed > 1.7 ORDER BY speed DESC;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "List all the laptop model numbers that have a speed greater than 1.7 in descending order of speed.", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT firstname \n FROM sales \n JOIN customers ON sales.customer_id = customers.customer_id \n GROUP BY firstname \n ORDER BY COUNT(firstname);", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "List firstnames of customers in an ascending order based on the number of purchases made by customers with this firstname.", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT DISTINCT maker FROM products JOIN pcs ON products.model = pcs.model WHERE ram > 1500;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "List all the makers (with only one entry per maker) who make PCs with RAM greater than 1500.", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT city, AVG(paid) as 'avg_spend' FROM sales JOIN customers ON sales.customer_id = customers.customer_id GROUP BY city", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find the city and the average amount of money spent by customers in each city. Name the column for the amount 'avg_spend'", + "category": "medium" + }, + { + "db_id": "laptop", + "query": "SELECT color, MAX(price) as 'max_price' FROM printers GROUP BY color;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find the maximum price for each color of printer. Name the column for the maximum price 'max_price'", + "category": "medium" + }, + { + "db_id": "who", + "query": "select country_name, max(pm25_concentration) as worst_pm25_for_country\nfrom ambient_air_quality\ngroup by country_name\norder by worst_pm25_for_country desc\nlimit 1", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find the country with the worst single reading of air quality (highest PM 2.5 value). Show the PM 2.5 value as well.", + "category": "medium" + }, + { + "db_id": "who", + "query": "select country_name, avg(pm25_concentration) as worst_avg_pm25_for_country\nfrom ambient_air_quality\ngroup by country_name\norder by worst_avg_pm25_for_country desc\nlimit 1", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find the country with the worst average air quality (highest PM 2.5 value). Show the PM 2.5 value as well.", + "category": "medium" + }, + { + "db_id": "who", + "query": "select distinct country_name from ambient_air_quality order by country_name", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find all countries for which WHO air quality data is available. Sort alphabetically.", + "category": "medium" + }, + { + "db_id": "who", + "query": "select year, avg(pm25_concentration) from ambient_air_quality \nwhere country_name = 'Singapore'\ngroup by year\norder by year", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Find Singapore air quality defined as PM2.5 concentration over time", + "category": "medium" + }, + { + "db_id": "nyc", + "query": "SELECT COLUMNS('^trip_') FROM rideshare LIMIT 10;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "select only the column names from the rideshare table that start with trip_ and return the first 10 values", + "category": "duckdb" + }, + { + "db_id": "nyc", + "query": "SELECT * FROM rideshare USING SAMPLE 1%;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "select a 1% sample from the nyc.rideshare table", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * EXCLUDE (customer_id) FROM customers;\n", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "select all columns from the customer table, except customer_id", + "category": "duckdb" + }, + { + "db_id": "nyc", + "query": "SUMMARIZE rideshare;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "show summary statistics of the rideshare table", + "category": "duckdb" + }, + { + "db_id": "none", + "query": "SELECT * FROM read_csv_auto(\n'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv')", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "read a CSV from https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv", + "category": "duckdb" + }, + { + "db_id": "none", + "query": "COPY (SELECT * FROM read_csv_auto(\n'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'))\nTO 'titanic.parquet' (FORMAT 'parquet');", + "setup_sql": ";", + "validation_sql": "SELECT * FROM 'titanic.parquet'", + "question": "read a CSV from https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv and convert it to a parquet file called \"titanic\"", + "category": "duckdb" + }, + { + "db_id": "none", + "query": "CREATE TABLE titanic AS (SELECT * FROM read_csv_auto(\n'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'))", + "setup_sql": ";", + "validation_sql": "SELECT * FROM titanic;", + "question": "create a table called \"titanic\" from CSV file https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv", + "category": "duckdb" + }, + { + "db_id": "none", + "query": "PRAGMA default_null_order='NULLS LAST';", + "setup_sql": ";", + "validation_sql": "SELECT current_setting('default_null_order');", + "question": "configure duckdb to put null values last when sorting", + "category": "duckdb" + }, + { + "db_id": "none", + "query": "CREATE TABLE IF NOT EXISTS products (\n maker varchar(10),\n model varchar(10),\n type varchar(10));", + "setup_sql": ";", + "validation_sql": "SELECT * FROM products;", + "question": "create a table about products, that contains a maker, model and type column", + "category": "ddl" + }, + { + "db_id": "product", + "query": "INSERT INTO products (maker, model, type)\nVALUES\n ('A', '1001', 'pc');", + "setup_sql": ";", + "validation_sql": "SELECT * FROM products;", + "question": "add a row with values for model \"1001\" of type \"pc\", from maker \"A\" to products table", + "category": "ddl" + }, + { + "db_id": "none", + "query": "CALL pragma_version();\n", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get current version of duckdb", + "category": "duckdb" + }, + { + "db_id": "nyc", + "query": "PRAGMA table_info('rideshare');", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "list all columns in table nyc.rideshare", + "category": "duckdb" + }, + { + "db_id": "nyc", + "query": "PRAGMA show_tables;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "show all tables in the curent database", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT customer_id, model, sum(paid) FROM sales GROUP BY ALL", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "how much did each customer spend per model type?", + "category": "easy" + }, + { + "db_id": "nyc", + "query": "SELECT Max(datediff('minute', tpep_pickup_datetime, tpep_dropoff_datetime)) from nyc.taxi", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "What was the longest taxi ride in minutes?", + "category": "hard" + }, + { + "db_id": "who", + "query": "with per_region as (\n select avg(pm10_concentration) as avg_pm10, who_region from ambient_air_quality group by who_region\n), max_region as (\n select who_region from per_region where avg_pm10 = (select max(avg_pm10) from per_region)\n), min_city_value_in_max_region as (\n select min(pm10_concentration) from ambient_air_quality where who_region in (from max_region)\n), min_city_in_max_region as (\n select city from ambient_air_quality where pm10_concentration in (from min_city_value_in_max_region) and who_region in (from max_region)\n)\nfrom min_city_in_max_region", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "What is the city with the lowest pm10 concentration in the region with the highest average pm10 concentration?", + "category": "hard" + }, + { + "db_id": "hn", + "query": "SELECT *, regexp_extract(text, '([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,63})',0) email from hacker_news where email[:4]='test'", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Get all posts on hn that contain an email address starting with test. Return all original columns, plus a new column containing the email address.", + "category": "hard" + }, + { + "db_id": "json", + "query": "SELECT employee.id, employee.first_name FROM employee_json", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Extract id and first_name properties as individual columns from the employee struct", + "category": "duckdb" + }, + { + "db_id": "who", + "query": "SELECT who_region[1]::INT as region, * EXCLUDE (who_region) FROM who.ambient_air_quality", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "count quality measurements per region. Make sure to return the region code (first char of who_region) as integer and sort by region.", + "category": "duckdb" + }, + { + "db_id": "flightinfo", + "query": "SELECT seat.seat_number FROM seat \nJOIN direct_flight ON direct_flight.flight_number = seat.flight_number \nJOIN airport AS departure_airport ON departure_airport.iata_code = direct_flight.departure_airport_iata_code \nJOIN airport AS arriving_airport ON arriving_airport.iata_code = direct_flight.arriving_airport_iata_code \nJOIN city AS departure_city ON departure_city.city_zipcode = departure_airport.city_zip_code \nJOIN city AS arriving_city ON arriving_city.city_zipcode = arriving_airport.city_zip_code \nWHERE departure_city.city_name = 'Bruxelles' AND arriving_city.city_name = 'Newark';", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "Which seats were available on the flight from Bruxelles to Newark?", + "category": "hard" + }, + { + "db_id": "laptop", + "query": "COPY customers FROM 'customers_12_12_2023.csv';", + "setup_sql": "COPY customers TO 'customers_12_12_2023.csv';", + "validation_sql": "SELECT * FROM customers;", + "question": "copy content of csv file customers_12_12_2023.csv into customers table", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "COPY customers FROM 'customers_12_12_2023.csv' (DELIMITER '\\t');", + "setup_sql": "COPY customers TO 'customers_12_12_2023.csv' (FORMAT CSV, DELIMITER '\\t');", + "validation_sql": "SELECT * FROM customers;", + "question": "copy content of csv file costomers_12_12_2023.csv into customers table with tab separator", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "COPY customers FROM 'customers_partitioned/city=Amsterdam/*.parquet';", + "setup_sql": "COPY customers TO 'customers_partitioned' (FORMAT PARQUET, PARTITION_BY (city), OVERWRITE_OR_IGNORE True);", + "validation_sql": "SELECT * FROM customers;;", + "question": "copy any parquet files from 'customers_partitioned/city=Amsterdam/' into customers table", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "COPY customers(customer_id) FROM 'customers_customer_ids.csv';", + "setup_sql": "COPY customers(customer_id) TO 'customers_customer_ids.csv';", + "validation_sql": "SELECT * FROM customers;", + "question": "copy only the customer_id column from the customers_customer_ids.csv into the customers tables", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "CREATE TABLE test_tbl AS SELECT * FROM read_json_auto('test.json');", + "setup_sql": "COPY customers TO 'test.json'\n", + "validation_sql": "SELECT * FROM test_tbl;", + "question": "read json file from test.json and create new table from it called 'test_tbl'", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * FROM read_csv_auto('test.csv');", + "setup_sql": "COPY customers TO 'test.csv';", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "read csv from test.csv", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * FROM read_csv_auto('test.csv', columns={'customer_id': 'VARCHAR', 'firstname': 'VARCHAR', 'lastname': 'VARCHAR'});", + "setup_sql": "COPY customers(customer_id, firstname, lastname) TO 'test.csv';", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "read csv from test.csv with predefined column and types - customer_id: string, firstname: string, lastname: string", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * EXCLUDE (ram, hd) FROM pcs;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "select all columns from pcs table except for ram and hd", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT COLUMNS('name$') FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "select all columns ending with 'name' from customers table", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT LENGTH(COLUMNS('name$')) FROM customers", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "for each column ending with 'name' in the customers table, compute the string length", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * REPLACE (upper(city) AS city) FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get all columns from customer table, and make all city names uppercase", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "EXPLAIN SELECT * FROM customers", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "show query plan for query: SELECT * from customers", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT ascii(lastname) FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get the first character of the firstname column and cast it to an INT", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT model, speed::INTEGER FROM laptops;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get laptop name and speed, return the speed as integer", + "category": "duckdb" + }, + { + "db_id": "laptop_array", + "query": "SELECT phone_numbers[1] FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get the first phone number of each customer", + "category": "duckdb" + }, + { + "db_id": "laptop_array", + "query": "INSERT INTO customers(customer_id, phone_numbers) VALUES (5, ['12312323', '23123344']);", + "setup_sql": ";", + "validation_sql": "SELECT * FROM customers;", + "question": "insert two phone numbers to customer with id 5 [\\\"12312323\\\", and '23123344']", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "ALTER TABLE customers ADD COLUMN phone_numbers VARCHAR[];", + "setup_sql": ";", + "validation_sql": "DESCRIBE customers;", + "question": "how to add a new column phone_numbers to the customers table, with array type varchar", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT firstname[1] FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get the first letter of the customers firstname", + "category": "duckdb" + }, + { + "db_id": "laptop_array", + "query": "SELECT phone_numbers[:2] FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get the first two phone numbers from the phone numbers array of each customer", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT {'a':1, 'b':2, 'c':3};", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "create a struct with keys a, b, c and values 1,2,3", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT [1,2,3];\n", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "create array with values 1,2,3", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "CREATE TABLE test (embeddings FLOAT[100]);", + "setup_sql": ";", + "validation_sql": "DESCRIBE test;", + "question": "create table test with a fix-sized array column with 100 dimenions, called embeddings", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "CREATE TABLE test (person STRUCT(name VARCHAR, id INTEGER));", + "setup_sql": ";", + "validation_sql": "DESCRIBE test;", + "question": "create table test with a struct column called person with properties name and id", + "category": "duckdb" + }, + { + "db_id": "laptop_struct", + "query": "SELECT person.name, person.id FROM test;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get persons name and persons id from the test table.", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "UPDATE customers SET email = NULL;", + "setup_sql": ";", + "validation_sql": "SELECT email FROM customers;", + "question": "remove all values from email column in customers table", + "category": "duckdb" + }, + { + "db_id": "laptop_json", + "query": "ALTER TABLE customers ALTER COLUMN email SET DATA TYPE VARCHAR;", + "setup_sql": ";", + "validation_sql": "DESCRIBE customers;", + "question": "make customer email of type VARCHAR", + "category": "duckdb" + }, + { + "db_id": "laptop_json", + "query": "INSERT INTO customers (customer_id, email) VALUES (5,'{\"from\": \"test2@gmail.com\", \"to\": \"test@gmail.com\"}');", + "setup_sql": ";", + "validation_sql": "SELECT * FROM customers;", + "question": "insert json into customer email for customer id 5: {'from': 'test2@gmail.com', 'to': 'test@gmail.com'}", + "category": "duckdb" + }, + { + "db_id": "laptop_json", + "query": "SELECT customers.email->>'from' FROM customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "get 'from' field from customer email json", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SUMMARIZE customers;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "summarize the customer table", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * FROM customers USING SAMPLE 10% (reservoir);", + "setup_sql": ";", + "validation_sql": "SELECT count(*) FROM ddb_benchmark_result;", + "question": "sample 10% from the customers table using reservoir sampling", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SET threads = 10;", + "setup_sql": ";", + "validation_sql": "SELECT current_setting('threads');", + "question": "set number of threads to 10", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SET memory_limit = '20G';\n", + "setup_sql": ";", + "validation_sql": "SELECT current_setting('memory_limit');", + "question": "set memory limit to 20 gigabyte", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * EXCLUDE (price), avg(price) FROM laptops GROUP BY ALL;", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "show the average price of laptop and group by the remaining columns", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "SELECT * FROM laptops WHERE price > 1000 ORDER BY ALL;\n", + "setup_sql": ";", + "validation_sql": "SELECT * FROM ddb_benchmark_result;", + "question": "show all laptops with price above 1000 and order by all columns", + "category": "duckdb" + }, + { + "db_id": "laptop", + "query": "ATTACH 'who.ddb';", + "setup_sql": ";", + "validation_sql": "SHOW DATABASES;", + "question": "attach database file who.ddb", + "category": "duckdb" + } +] \ No newline at end of file diff --git a/duckdb-nsql/eval/data/tables.json b/duckdb-nsql/eval/data/tables.json new file mode 100644 index 0000000000000000000000000000000000000000..8d93984548a40b326439dbdd385a3cf0c7bafba9 --- /dev/null +++ b/duckdb-nsql/eval/data/tables.json @@ -0,0 +1,3465 @@ +[ + { + "db_id": "hn", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "title" + ], + [ + 0, + "url" + ], + [ + 0, + "text" + ], + [ + 0, + "dead" + ], + [ + 0, + "by" + ], + [ + 0, + "score" + ], + [ + 0, + "time" + ], + [ + 0, + "timestamp" + ], + [ + 0, + "type" + ], + [ + 0, + "id" + ], + [ + 0, + "parent" + ], + [ + 0, + "descendants" + ], + [ + 0, + "ranking" + ], + [ + 0, + "deleted" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "title" + ], + [ + 0, + "url" + ], + [ + 0, + "text" + ], + [ + 0, + "dead" + ], + [ + 0, + "by" + ], + [ + 0, + "score" + ], + [ + 0, + "time" + ], + [ + 0, + "timestamp" + ], + [ + 0, + "type" + ], + [ + 0, + "id" + ], + [ + 0, + "parent" + ], + [ + 0, + "descendants" + ], + [ + 0, + "ranking" + ], + [ + 0, + "deleted" + ] + ], + "column_types": [ + "text", + "varchar", + "varchar", + "varchar", + "boolean", + "varchar", + "bigint", + "bigint", + "timestamp", + "varchar", + "bigint", + "bigint", + "bigint", + "bigint", + "boolean" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "hacker_news" + ], + "table_names_original": [ + "hacker_news" + ] + }, + { + "db_id": "laptop", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_types": [ + "text", + "char", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "char", + "double", + "int", + "int", + "double", + "double", + "char", + "double", + "int", + "int", + "double", + "char", + "varchar", + "varchar", + "double", + "char", + "char", + "varchar", + "char", + "char", + "int", + "date", + "double", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ], + "table_names_original": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ] + }, + { + "db_id": "transactions", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "id" + ], + [ + 0, + "name" + ], + [ + 1, + "user_id" + ], + [ + 1, + "amount" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "id" + ], + [ + 0, + "name" + ], + [ + 1, + "user_id" + ], + [ + 1, + "amount" + ] + ], + "column_types": [ + "text", + "int", + "varchar", + "int", + "int" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "users", + "transactions" + ], + "table_names_original": [ + "users", + "transactions" + ] + }, + { + "db_id": "who", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "who_region" + ], + [ + 0, + "iso3" + ], + [ + 0, + "country_name" + ], + [ + 0, + "city" + ], + [ + 0, + "year" + ], + [ + 0, + "version" + ], + [ + 0, + "pm10_concentration" + ], + [ + 0, + "pm25_concentration" + ], + [ + 0, + "no2_concentration" + ], + [ + 0, + "pm10_tempcov" + ], + [ + 0, + "pm25_tempcov" + ], + [ + 0, + "no2_tempcov" + ], + [ + 0, + "type_of_stations" + ], + [ + 0, + "reference" + ], + [ + 0, + "web_link" + ], + [ + 0, + "population" + ], + [ + 0, + "population_source" + ], + [ + 0, + "latitude" + ], + [ + 0, + "longitude" + ], + [ + 0, + "who_ms" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "who_region" + ], + [ + 0, + "iso3" + ], + [ + 0, + "country_name" + ], + [ + 0, + "city" + ], + [ + 0, + "year" + ], + [ + 0, + "version" + ], + [ + 0, + "pm10_concentration" + ], + [ + 0, + "pm25_concentration" + ], + [ + 0, + "no2_concentration" + ], + [ + 0, + "pm10_tempcov" + ], + [ + 0, + "pm25_tempcov" + ], + [ + 0, + "no2_tempcov" + ], + [ + 0, + "type_of_stations" + ], + [ + 0, + "reference" + ], + [ + 0, + "web_link" + ], + [ + 0, + "population" + ], + [ + 0, + "population_source" + ], + [ + 0, + "latitude" + ], + [ + 0, + "longitude" + ], + [ + 0, + "who_ms" + ] + ], + "column_types": [ + "text", + "varchar", + "varchar", + "varchar", + "varchar", + "bigint", + "varchar", + "bigint", + "bigint", + "bigint", + "bigint", + "bigint", + "bigint", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "float", + "float", + "bigint" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "ambient_air_quality" + ], + "table_names_original": [ + "ambient_air_quality" + ] + }, + { + "db_id": "nyc", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "unique_key" + ], + [ + 0, + "created_date" + ], + [ + 0, + "closed_date" + ], + [ + 0, + "agency" + ], + [ + 0, + "agency_name" + ], + [ + 0, + "complaint_type" + ], + [ + 0, + "descriptor" + ], + [ + 0, + "location_type" + ], + [ + 0, + "incident_zip" + ], + [ + 0, + "incident_address" + ], + [ + 0, + "street_name" + ], + [ + 0, + "cross_street_1" + ], + [ + 0, + "cross_street_2" + ], + [ + 0, + "intersection_street_1" + ], + [ + 0, + "intersection_street_2" + ], + [ + 0, + "address_type" + ], + [ + 0, + "city" + ], + [ + 0, + "landmark" + ], + [ + 0, + "facility_type" + ], + [ + 0, + "status" + ], + [ + 0, + "due_date" + ], + [ + 0, + "resolution_description" + ], + [ + 0, + "resolution_action_updated_date" + ], + [ + 0, + "community_board" + ], + [ + 0, + "bbl" + ], + [ + 0, + "borough" + ], + [ + 0, + "x_coordinate_state_plane" + ], + [ + 0, + "y_coordinate_state_plane" + ], + [ + 0, + "open_data_channel_type" + ], + [ + 0, + "park_facility_name" + ], + [ + 0, + "park_borough" + ], + [ + 0, + "vehicle_type" + ], + [ + 0, + "taxi_company_borough" + ], + [ + 0, + "taxi_pick_up_location" + ], + [ + 0, + "bridge_highway_name" + ], + [ + 0, + "bridge_highway_direction" + ], + [ + 0, + "road_ramp" + ], + [ + 0, + "bridge_highway_segment" + ], + [ + 0, + "latitude" + ], + [ + 0, + "longitude" + ], + [ + 1, + "hvfhs_license_num" + ], + [ + 1, + "dispatching_base_num" + ], + [ + 1, + "originating_base_num" + ], + [ + 1, + "request_datetime" + ], + [ + 1, + "on_scene_datetime" + ], + [ + 1, + "pickup_datetime" + ], + [ + 1, + "dropoff_datetime" + ], + [ + 1, + "PULocationID" + ], + [ + 1, + "DOLocationID" + ], + [ + 1, + "trip_miles" + ], + [ + 1, + "trip_time" + ], + [ + 1, + "base_passenger_fare" + ], + [ + 1, + "tolls" + ], + [ + 1, + "bcf" + ], + [ + 1, + "sales_tax" + ], + [ + 1, + "congestion_surcharge" + ], + [ + 1, + "airport_fee" + ], + [ + 1, + "tips" + ], + [ + 1, + "driver_pay" + ], + [ + 1, + "shared_request_flag" + ], + [ + 1, + "shared_match_flag" + ], + [ + 1, + "access_a_ride_flag" + ], + [ + 1, + "wav_request_flag" + ], + [ + 1, + "wav_match_flag" + ], + [ + 2, + "VendorID" + ], + [ + 2, + "tpep_pickup_datetime" + ], + [ + 2, + "tpep_dropoff_datetime" + ], + [ + 2, + "passenger_count" + ], + [ + 2, + "trip_distance" + ], + [ + 2, + "RatecodeID" + ], + [ + 2, + "store_and_fwd_flag" + ], + [ + 2, + "PULocationID" + ], + [ + 2, + "DOLocationID" + ], + [ + 2, + "payment_type" + ], + [ + 2, + "fare_amount" + ], + [ + 2, + "extra" + ], + [ + 2, + "mta_tax" + ], + [ + 2, + "tip_amount" + ], + [ + 2, + "tolls_amount" + ], + [ + 2, + "improvement_surcharge" + ], + [ + 2, + "total_amount" + ], + [ + 2, + "congestion_surcharge" + ], + [ + 2, + "airport_fee" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "unique_key" + ], + [ + 0, + "created_date" + ], + [ + 0, + "closed_date" + ], + [ + 0, + "agency" + ], + [ + 0, + "agency_name" + ], + [ + 0, + "complaint_type" + ], + [ + 0, + "descriptor" + ], + [ + 0, + "location_type" + ], + [ + 0, + "incident_zip" + ], + [ + 0, + "incident_address" + ], + [ + 0, + "street_name" + ], + [ + 0, + "cross_street_1" + ], + [ + 0, + "cross_street_2" + ], + [ + 0, + "intersection_street_1" + ], + [ + 0, + "intersection_street_2" + ], + [ + 0, + "address_type" + ], + [ + 0, + "city" + ], + [ + 0, + "landmark" + ], + [ + 0, + "facility_type" + ], + [ + 0, + "status" + ], + [ + 0, + "due_date" + ], + [ + 0, + "resolution_description" + ], + [ + 0, + "resolution_action_updated_date" + ], + [ + 0, + "community_board" + ], + [ + 0, + "bbl" + ], + [ + 0, + "borough" + ], + [ + 0, + "x_coordinate_state_plane" + ], + [ + 0, + "y_coordinate_state_plane" + ], + [ + 0, + "open_data_channel_type" + ], + [ + 0, + "park_facility_name" + ], + [ + 0, + "park_borough" + ], + [ + 0, + "vehicle_type" + ], + [ + 0, + "taxi_company_borough" + ], + [ + 0, + "taxi_pick_up_location" + ], + [ + 0, + "bridge_highway_name" + ], + [ + 0, + "bridge_highway_direction" + ], + [ + 0, + "road_ramp" + ], + [ + 0, + "bridge_highway_segment" + ], + [ + 0, + "latitude" + ], + [ + 0, + "longitude" + ], + [ + 1, + "hvfhs_license_num" + ], + [ + 1, + "dispatching_base_num" + ], + [ + 1, + "originating_base_num" + ], + [ + 1, + "request_datetime" + ], + [ + 1, + "on_scene_datetime" + ], + [ + 1, + "pickup_datetime" + ], + [ + 1, + "dropoff_datetime" + ], + [ + 1, + "PULocationID" + ], + [ + 1, + "DOLocationID" + ], + [ + 1, + "trip_miles" + ], + [ + 1, + "trip_time" + ], + [ + 1, + "base_passenger_fare" + ], + [ + 1, + "tolls" + ], + [ + 1, + "bcf" + ], + [ + 1, + "sales_tax" + ], + [ + 1, + "congestion_surcharge" + ], + [ + 1, + "airport_fee" + ], + [ + 1, + "tips" + ], + [ + 1, + "driver_pay" + ], + [ + 1, + "shared_request_flag" + ], + [ + 1, + "shared_match_flag" + ], + [ + 1, + "access_a_ride_flag" + ], + [ + 1, + "wav_request_flag" + ], + [ + 1, + "wav_match_flag" + ], + [ + 2, + "VendorID" + ], + [ + 2, + "tpep_pickup_datetime" + ], + [ + 2, + "tpep_dropoff_datetime" + ], + [ + 2, + "passenger_count" + ], + [ + 2, + "trip_distance" + ], + [ + 2, + "RatecodeID" + ], + [ + 2, + "store_and_fwd_flag" + ], + [ + 2, + "PULocationID" + ], + [ + 2, + "DOLocationID" + ], + [ + 2, + "payment_type" + ], + [ + 2, + "fare_amount" + ], + [ + 2, + "extra" + ], + [ + 2, + "mta_tax" + ], + [ + 2, + "tip_amount" + ], + [ + 2, + "tolls_amount" + ], + [ + 2, + "improvement_surcharge" + ], + [ + 2, + "total_amount" + ], + [ + 2, + "congestion_surcharge" + ], + [ + 2, + "airport_fee" + ] + ], + "column_types": [ + "text", + "bigint", + "timestamp", + "timestamp", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "timestamp", + "varchar", + "timestamp", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "double", + "double", + "varchar", + "varchar", + "varchar", + "timestamp", + "timestamp", + "timestamp", + "timestamp", + "bigint", + "bigint", + "double", + "bigint", + "double", + "double", + "double", + "double", + "double", + "double", + "double", + "double", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "bigint", + "timestamp", + "timestamp", + "double", + "double", + "double", + "varchar", + "bigint", + "bigint", + "bigint", + "double", + "double", + "double", + "double", + "double", + "double", + "double", + "double", + "double" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "service_requests", + "rideshare", + "taxi" + ], + "table_names_original": [ + "service_requests", + "rideshare", + "taxi" + ] + }, + { + "db_id": "product", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "maker" + ], + [ + 0, + "model" + ], + [ + 0, + "type" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "maker" + ], + [ + 0, + "model" + ], + [ + 0, + "type" + ] + ], + "column_types": [ + "text", + "varchar", + "varchar", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "products" + ], + "table_names_original": [ + "products" + ] + }, + { + "db_id": "json", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "employee" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "employee" + ] + ], + "column_types": [ + "text", + "struct(id int, first_name text, last_name text, email text)" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "employee_json" + ], + "table_names_original": [ + "employee_json" + ] + }, + { + "db_id": "flightinfo", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "icao_code" + ], + [ + 0, + "email" + ], + [ + 0, + "name" + ], + [ + 0, + "phone_number" + ], + [ + 0, + "iata_code" + ], + [ + 1, + "title" + ], + [ + 1, + "description" + ], + [ + 1, + "price" + ], + [ + 1, + "service_type" + ], + [ + 1, + "airline_icao_code" + ], + [ + 2, + "iata_code" + ], + [ + 2, + "address" + ], + [ + 2, + "name" + ], + [ + 2, + "phone_number" + ], + [ + 2, + "email" + ], + [ + 2, + "city_zip_code" + ], + [ + 2, + "city_dbpedia" + ], + [ + 3, + "title" + ], + [ + 3, + "cabin_bag_dimension_cm" + ], + [ + 3, + "cabin_bags_no" + ], + [ + 3, + "cabin_bg_weight_kg" + ], + [ + 3, + "checked_bag_dimension_cm" + ], + [ + 3, + "checked_bags_no" + ], + [ + 3, + "checked_bag_weight_kg" + ], + [ + 3, + "excessive_price_perkg" + ], + [ + 3, + "flight_type" + ], + [ + 3, + "airline_icao_code" + ], + [ + 4, + "title" + ], + [ + 4, + "description" + ], + [ + 4, + "airline_icao_code" + ], + [ + 5, + "title" + ], + [ + 5, + "description" + ], + [ + 5, + "due_date" + ], + [ + 5, + "refund_postdue_percentage" + ], + [ + 5, + "refund_predue_percentage" + ], + [ + 5, + "airline_icao_code" + ], + [ + 6, + "city_zipcode" + ], + [ + 6, + "city_name" + ], + [ + 6, + "country_iso_code" + ], + [ + 7, + "country_iso_code" + ], + [ + 7, + "country_name" + ], + [ + 8, + "flight_number" + ], + [ + 8, + "departure_airport_iata_code" + ], + [ + 8, + "arriving_airport_iata_code" + ], + [ + 9, + "number" + ], + [ + 9, + "departure_date" + ], + [ + 9, + "arrival_date" + ], + [ + 9, + "distance_km" + ], + [ + 9, + "is_available" + ], + [ + 9, + "duration_min" + ], + [ + 9, + "airline_icao_code" + ], + [ + 9, + "type" + ], + [ + 10, + "title" + ], + [ + 10, + "description" + ], + [ + 10, + "cabin_class_title" + ], + [ + 10, + "baggage_policy_title" + ], + [ + 10, + "cancelation_policy_title" + ], + [ + 11, + "subflight_number" + ], + [ + 11, + "flight_number" + ], + [ + 12, + "flight_package_title" + ], + [ + 12, + "airline_service_title" + ], + [ + 13, + "seat_number" + ], + [ + 13, + "is_available" + ], + [ + 13, + "flight_number" + ], + [ + 14, + "meal_type" + ], + [ + 14, + "airline_service_title" + ], + [ + 15, + "duration_min" + ], + [ + 15, + "duration_from" + ], + [ + 15, + "duration_to" + ], + [ + 15, + "airport_iatacode" + ], + [ + 15, + "flight_number" + ], + [ + 16, + "flight_number" + ], + [ + 16, + "package_title" + ], + [ + 16, + "trip_id" + ], + [ + 16, + "requested_excessive_baggage_kg" + ], + [ + 16, + "seat_number" + ], + [ + 16, + "chosen_meal_service_price" + ], + [ + 16, + "chosen_wifi_service_price" + ], + [ + 16, + "price" + ], + [ + 17, + "id" + ], + [ + 17, + "tax" + ], + [ + 17, + "booking_date" + ], + [ + 17, + "user_email" + ], + [ + 17, + "type" + ], + [ + 18, + "email" + ], + [ + 18, + "first_name" + ], + [ + 18, + "last_name" + ], + [ + 18, + "birthdate" + ], + [ + 18, + "passport_number" + ], + [ + 18, + "address" + ], + [ + 18, + "password" + ], + [ + 18, + "phone_number" + ], + [ + 19, + "wifi_onboard_service_bandwidth_MB" + ], + [ + 19, + "airline_service_title" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "icao_code" + ], + [ + 0, + "email" + ], + [ + 0, + "name" + ], + [ + 0, + "phone_number" + ], + [ + 0, + "iata_code" + ], + [ + 1, + "title" + ], + [ + 1, + "description" + ], + [ + 1, + "price" + ], + [ + 1, + "service_type" + ], + [ + 1, + "airline_icao_code" + ], + [ + 2, + "iata_code" + ], + [ + 2, + "address" + ], + [ + 2, + "name" + ], + [ + 2, + "phone_number" + ], + [ + 2, + "email" + ], + [ + 2, + "city_zip_code" + ], + [ + 2, + "city_dbpedia" + ], + [ + 3, + "title" + ], + [ + 3, + "cabin_bag_dimension_cm" + ], + [ + 3, + "cabin_bags_no" + ], + [ + 3, + "cabin_bg_weight_kg" + ], + [ + 3, + "checked_bag_dimension_cm" + ], + [ + 3, + "checked_bags_no" + ], + [ + 3, + "checked_bag_weight_kg" + ], + [ + 3, + "excessive_price_perkg" + ], + [ + 3, + "flight_type" + ], + [ + 3, + "airline_icao_code" + ], + [ + 4, + "title" + ], + [ + 4, + "description" + ], + [ + 4, + "airline_icao_code" + ], + [ + 5, + "title" + ], + [ + 5, + "description" + ], + [ + 5, + "due_date" + ], + [ + 5, + "refund_postdue_percentage" + ], + [ + 5, + "refund_predue_percentage" + ], + [ + 5, + "airline_icao_code" + ], + [ + 6, + "city_zipcode" + ], + [ + 6, + "city_name" + ], + [ + 6, + "country_iso_code" + ], + [ + 7, + "country_iso_code" + ], + [ + 7, + "country_name" + ], + [ + 8, + "flight_number" + ], + [ + 8, + "departure_airport_iata_code" + ], + [ + 8, + "arriving_airport_iata_code" + ], + [ + 9, + "number" + ], + [ + 9, + "departure_date" + ], + [ + 9, + "arrival_date" + ], + [ + 9, + "distance_km" + ], + [ + 9, + "is_available" + ], + [ + 9, + "duration_min" + ], + [ + 9, + "airline_icao_code" + ], + [ + 9, + "type" + ], + [ + 10, + "title" + ], + [ + 10, + "description" + ], + [ + 10, + "cabin_class_title" + ], + [ + 10, + "baggage_policy_title" + ], + [ + 10, + "cancelation_policy_title" + ], + [ + 11, + "subflight_number" + ], + [ + 11, + "flight_number" + ], + [ + 12, + "flight_package_title" + ], + [ + 12, + "airline_service_title" + ], + [ + 13, + "seat_number" + ], + [ + 13, + "is_available" + ], + [ + 13, + "flight_number" + ], + [ + 14, + "meal_type" + ], + [ + 14, + "airline_service_title" + ], + [ + 15, + "duration_min" + ], + [ + 15, + "duration_from" + ], + [ + 15, + "duration_to" + ], + [ + 15, + "airport_iatacode" + ], + [ + 15, + "flight_number" + ], + [ + 16, + "flight_number" + ], + [ + 16, + "package_title" + ], + [ + 16, + "trip_id" + ], + [ + 16, + "requested_excessive_baggage_kg" + ], + [ + 16, + "seat_number" + ], + [ + 16, + "chosen_meal_service_price" + ], + [ + 16, + "chosen_wifi_service_price" + ], + [ + 16, + "price" + ], + [ + 17, + "id" + ], + [ + 17, + "tax" + ], + [ + 17, + "booking_date" + ], + [ + 17, + "user_email" + ], + [ + 17, + "type" + ], + [ + 18, + "email" + ], + [ + 18, + "first_name" + ], + [ + 18, + "last_name" + ], + [ + 18, + "birthdate" + ], + [ + 18, + "passport_number" + ], + [ + 18, + "address" + ], + [ + 18, + "password" + ], + [ + 18, + "phone_number" + ], + [ + 19, + "wifi_onboard_service_bandwidth_MB" + ], + [ + 19, + "airline_service_title" + ] + ], + "column_types": [ + "text", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "double", + "varchar", + "varchar", + "varchar", + "text", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "double", + "double", + "double", + "double", + "double", + "double", + "double", + "varchar", + "varchar", + "varchar", + "text", + "varchar", + "varchar", + "text", + "text", + "int", + "int", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "text", + "varchar", + "varchar", + "varchar", + "varchar", + "datetime", + "datetime", + "double", + "tinyint", + "double", + "varchar", + "varchar", + "varchar", + "text", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "tinyint", + "varchar", + "varchar", + "varchar", + "double", + "datetime", + "datetime", + "varchar", + "varchar", + "varchar", + "varchar", + "int", + "int", + "varchar", + "int", + "int", + "double", + "int", + "double", + "datetime", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "date", + "varchar", + "varchar", + "varchar", + "double", + "double", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "airline", + "airline_service", + "airport", + "baggage_policy", + "cabin_class", + "cancellation_policy", + "city", + "country", + "direct_flight", + "flight", + "flight_package", + "non_direct_flight", + "package_service", + "seat", + "special_meal_type", + "stopping", + "ticke", + "trip", + "user", + "wifi_onboard_service" + ], + "table_names_original": [ + "airline", + "airline_service", + "airport", + "baggage_policy", + "cabin_class", + "cancellation_policy", + "city", + "country", + "direct_flight", + "flight", + "flight_package", + "non_direct_flight", + "package_service", + "seat", + "special_meal_type", + "stopping", + "ticke", + "trip", + "user", + "wifi_onboard_service" + ] + }, + { + "db_id": "none", + "column_names": [ + [ + -1, + "*" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ] + ], + "column_types": [ + "text" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [], + "table_names_original": [] + }, + { + "db_id": "laptop_array", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 0, + "phone_numbers" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 0, + "phone_number" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_types": [ + "text", + "char", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "array", + "char", + "double", + "int", + "int", + "double", + "double", + "char", + "double", + "int", + "int", + "double", + "char", + "varchar", + "varchar", + "double", + "char", + "char", + "varchar", + "char", + "char", + "int", + "date", + "double", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ], + "table_names_original": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ] + }, + { + "db_id": "laptop_struct", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "person" + ], + [ + 1, + "customer_id" + ], + [ + 1, + "firstname" + ], + [ + 1, + "lastname" + ], + [ + 1, + "city" + ], + [ + 1, + "address" + ], + [ + 1, + "email" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "screen" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "speed" + ], + [ + 3, + "ram" + ], + [ + 3, + "hd" + ], + [ + 3, + "price" + ], + [ + 4, + "model" + ], + [ + 4, + "color" + ], + [ + 4, + "type" + ], + [ + 4, + "price" + ], + [ + 5, + "maker" + ], + [ + 5, + "model" + ], + [ + 5, + "type" + ], + [ + 6, + "customer_id" + ], + [ + 6, + "model" + ], + [ + 6, + "quantity" + ], + [ + 6, + "day" + ], + [ + 6, + "paid" + ], + [ + 6, + "type_of_payment" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "person" + ], + [ + 1, + "customer_id" + ], + [ + 1, + "firstname" + ], + [ + 1, + "lastname" + ], + [ + 1, + "city" + ], + [ + 1, + "address" + ], + [ + 1, + "email" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "screen" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "speed" + ], + [ + 3, + "ram" + ], + [ + 3, + "hd" + ], + [ + 3, + "price" + ], + [ + 4, + "model" + ], + [ + 4, + "color" + ], + [ + 4, + "type" + ], + [ + 4, + "price" + ], + [ + 5, + "maker" + ], + [ + 5, + "model" + ], + [ + 5, + "type" + ], + [ + 6, + "customer_id" + ], + [ + 6, + "model" + ], + [ + 6, + "quantity" + ], + [ + 6, + "day" + ], + [ + 6, + "paid" + ], + [ + 6, + "type_of_payment" + ] + ], + "column_types": [ + "text", + "struct(id int, name: text)", + "char", + "varchar", + "varchar", + "varchar", + "varchar", + "varchar", + "char", + "double", + "int", + "int", + "double", + "double", + "char", + "double", + "int", + "int", + "double", + "char", + "varchar", + "varchar", + "double", + "char", + "char", + "varchar", + "char", + "char", + "int", + "date", + "double", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "test", + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ], + "table_names_original": [ + "test", + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ] + }, + { + "db_id": "laptop_json", + "column_names": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_names_original": [ + [ + -1, + "*" + ], + [ + 0, + "customer_id" + ], + [ + 0, + "firstname" + ], + [ + 0, + "lastname" + ], + [ + 0, + "city" + ], + [ + 0, + "address" + ], + [ + 0, + "email" + ], + [ + 1, + "model" + ], + [ + 1, + "speed" + ], + [ + 1, + "ram" + ], + [ + 1, + "hd" + ], + [ + 1, + "screen" + ], + [ + 1, + "price" + ], + [ + 2, + "model" + ], + [ + 2, + "speed" + ], + [ + 2, + "ram" + ], + [ + 2, + "hd" + ], + [ + 2, + "price" + ], + [ + 3, + "model" + ], + [ + 3, + "color" + ], + [ + 3, + "type" + ], + [ + 3, + "price" + ], + [ + 4, + "maker" + ], + [ + 4, + "model" + ], + [ + 4, + "type" + ], + [ + 5, + "customer_id" + ], + [ + 5, + "model" + ], + [ + 5, + "quantity" + ], + [ + 5, + "day" + ], + [ + 5, + "paid" + ], + [ + 5, + "type_of_payment" + ] + ], + "column_types": [ + "text", + "char", + "varchar", + "varchar", + "varchar", + "varchar", + "json", + "char", + "double", + "int", + "int", + "double", + "double", + "char", + "double", + "int", + "int", + "double", + "char", + "varchar", + "varchar", + "double", + "char", + "char", + "varchar", + "char", + "char", + "int", + "date", + "double", + "varchar" + ], + "foreign_keys": {}, + "primary_keys": {}, + "table_names": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ], + "table_names_original": [ + "customers", + "laptops", + "pcs", + "printers", + "products", + "sales" + ] + } +] \ No newline at end of file diff --git a/duckdb-nsql/eval/data_utils.py b/duckdb-nsql/eval/data_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..49a34d243b5bccf32a33da7eecf03a4121781f08 --- /dev/null +++ b/duckdb-nsql/eval/data_utils.py @@ -0,0 +1,79 @@ +"""Training data prep utils.""" +import json +import re +from collections import defaultdict +from schema import ForeignKey, Table, TableColumn + + +def read_tables_json( + schema_file: str, + lowercase: bool = False, +) -> dict[str, dict[str, Table]]: + """Read tables json.""" + data = json.load(open(schema_file)) + db_to_tables = {} + for db in data: + db_name = db["db_id"] + table_names = db["table_names_original"] + db["column_names_original"] = [ + [x[0], x[1]] for x in db["column_names_original"] + ] + db["column_types"] = db["column_types"] + if lowercase: + table_names = [tn.lower() for tn in table_names] + pks = db["primary_keys"] + fks = db["foreign_keys"] + tables = defaultdict(list) + tables_pks = defaultdict(list) + tables_fks = defaultdict(list) + for idx, ((ti, col_name), col_type) in enumerate( + zip(db["column_names_original"], db["column_types"]) + ): + if ti == -1: + continue + if lowercase: + col_name = col_name.lower() + col_type = col_type.lower() + if idx in pks: + tables_pks[table_names[ti]].append( + TableColumn(name=col_name, dtype=col_type) + ) + for fk in fks: + if idx == fk[0]: + other_column = db["column_names_original"][fk[1]] + other_column_type = db["column_types"][fk[1]] + other_table = table_names[other_column[0]] + tables_fks[table_names[ti]].append( + ForeignKey( + column=TableColumn(name=col_name, dtype=col_type), + references_name=other_table, + references_column=TableColumn( + name=other_column[1], dtype=other_column_type + ), + ) + ) + tables[table_names[ti]].append(TableColumn(name=col_name, dtype=col_type)) + db_to_tables[db_name] = { + table_name: Table( + name=table_name, + columns=tables[table_name], + pks=tables_pks[table_name], + fks=tables_fks[table_name], + examples=None, + ) + for table_name in tables + } + return db_to_tables + + +def clean_str(target: str) -> str: + """Clean string for question.""" + if not target: + return target + + target = re.sub(r"[^\x00-\x7f]", r" ", target) + line = re.sub(r"''", r" ", target) + line = re.sub(r"``", r" ", line) + line = re.sub(r"\"", r"'", line) + line = re.sub(r"[\t ]+", " ", line) + return line.strip() diff --git a/duckdb-nsql/eval/doc_retriever.py b/duckdb-nsql/eval/doc_retriever.py new file mode 100644 index 0000000000000000000000000000000000000000..57dc69ff31d939770640322763c5551be0adb41c --- /dev/null +++ b/duckdb-nsql/eval/doc_retriever.py @@ -0,0 +1,92 @@ +"""Retrieve documentation for a given query.""" + +from pathlib import Path +from typing import Any +from rich.console import Console +from tqdm import tqdm +import numpy as np +from manifest import Manifest +from langchain.text_splitter import MarkdownHeaderTextSplitter +from langchain.text_splitter import RecursiveCharacterTextSplitter + +console = Console(soft_wrap=True) + +try: + EMBEDDING_MODEL = Manifest( + client_name="openaiembedding", + ) +except Exception as e: + console.print(e) + console.print( + "Failed to load embedding model. Likely OPENAI API key is not set. Please set to run document retrieval.", + style="bold red", + ) + + +def load_documentation(path: Path) -> dict[str, str]: + """Load documentation from path.""" + content = {} + for file in path.glob("**/*.md"): + with open(file, "r") as f: + data = f.read() + key = str(file).replace(str(path), "") + content[key] = data + return content + + +def split_documents(content: dict[str, str]) -> dict[str, Any]: + """Split documents into chunks.""" + md_splitted_docs = [] + markdown_splitter = MarkdownHeaderTextSplitter( + headers_to_split_on=[("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3")] + ) + text_splitter = RecursiveCharacterTextSplitter( + separators=["\n"], chunk_size=500, chunk_overlap=50, length_function=len + ) + + for file, raw_doc in content.items(): + splitted_text = markdown_splitter.split_text(raw_doc) + for t in splitted_text: + t.metadata["source"] = file + md_splitted_docs.extend(splitted_text) + + docs = text_splitter.split_documents(md_splitted_docs) + docs_as_dict = [doc.dict() for doc in docs] + return docs_as_dict + + +def get_embeddings(text: str) -> np.ndarray: + """Get embeddings.""" + return np.array(EMBEDDING_MODEL.run(text)) + + +def embed_documents( + chunked_docs: dict[str, Any], key: str = "page_content" +) -> tuple[dict[str, Any], np.ndarray]: + """Embed documents.""" + all_embeddings = [] + for doc in tqdm(chunked_docs): + emb = get_embeddings(doc[key]) + doc["embedding"] = emb + all_embeddings.append(doc["embedding"]) + full_embedding_mat = np.vstack(all_embeddings) + return chunked_docs, full_embedding_mat + + +def query_docs( + query: str, + docs: dict[str, Any], + embedding_mat: np.ndarray, + top_n: int = 10, + key: str = "page_content", +) -> tuple[list[int], list[str]]: + """Query documents.""" + query_embedding = get_embeddings(query) + scores = embedding_mat.dot(query_embedding) + sorted_indices = np.argsort(scores)[::-1] + top_n_indices = sorted_indices[:top_n] + top_n_indices_rev = top_n_indices[::-1] + returned_docs = [] + for i in top_n_indices_rev: + returned_docs.append(docs[i][key]) + return top_n_indices_rev.tolist(), returned_docs diff --git a/duckdb-nsql/eval/evaluate.py b/duckdb-nsql/eval/evaluate.py new file mode 100644 index 0000000000000000000000000000000000000000..139ff66358f4bbf8b14270efe66414bb67654075 --- /dev/null +++ b/duckdb-nsql/eval/evaluate.py @@ -0,0 +1,392 @@ +"""Evaluate text2sql spider model predictions.""" +import json +import os +import re +import signal +import sys +import traceback +from pathlib import Path +from typing import Any + +import click +import pandas as pd +from rich.console import Console +from tqdm.auto import tqdm + +sys.path.append(os.path.join(os.path.dirname(__file__), ".")) +# from metrics.spider import evaluation as spider_evaluation # type: ignore # noqa: E402 +from metrics.test_suite_sql_eval import ( # type: ignore # noqa: E402 + evaluation as test_suite_evaluation, +) +from data_utils import read_tables_json # type: ignore # noqa: E402 +from metric_utils import ( # type: ignore # noqa: E402 + correct_casing, + edit_distance, +) + +console = Console(soft_wrap=True) + +LEVELS = ["easy", "medium", "hard", "duckdb", "ddl", "all"] +PARTIAL_TYPES = [ + "select", + "select(no AGG)", + "where", + "where(no OP)", + "group(no Having)", + "group", + "order", + "and/or", + "IUEN", + "keywords", +] +TIMEOUT_SECONDS = 30 + + +def timeout_handler(signum: int, frame: Any) -> None: + raise TimeoutError("Function execution timed out.") + + +def print_scores(scores: dict, model_name: str, metric_type: str = "exec") -> None: + """Print scores.""" + + def print_formated_s( + row_name: str, l: list[str], element_format: str = "{}", sep: str = "\t" + ) -> None: + template = "{}" + sep + sep.join([element_format] * len(l)) + console.print(template.format(row_name, *l)) + + # Add empty scores for each level if not present + for level in LEVELS: + if level not in scores: + scores[level] = {} + scores[level]["count"] = 0 + scores[level]["exec"] = 0 + scores[level]["exact"] = 0 + + print_formated_s("", LEVELS) + counts = [scores[level]["count"] for level in LEVELS] + print_formated_s("count", counts) + console.print(f">====================== {model_name} =====================") + if metric_type == "exec": + console.print( + ">===================== EXECUTION ACCURACY =====================" + ) + exec_scores = [scores[level]["exec"] for level in LEVELS] + print_formated_s("execution", exec_scores, element_format="{:.3f}") + + elif metric_type == "exact": + console.print( + "\n>====================== EXACT MATCHING ACCURACY =====================" + ) + exact_scores = [scores[level]["exact"] for level in LEVELS] + print_formated_s("exact match", exact_scores, element_format="{:.3f}") + + +def compute_exact_match_metric( + predictions: list, + references: list, + gold_dbs: list, + kmaps: dict, + db_dir: str, + categories, +) -> dict: + """Compute exact match metric.""" + exact_match = {} + exact_match["all"] = {} + exact_match["all"]["count"] = 0 + exact_match["all"]["exact"] = 0 + for prediction, reference, gold_db, category in tqdm( + zip(predictions, references, gold_dbs, categories), total=len(predictions) + ): + if category not in exact_match: + exact_match[category] = {} + exact_match[category]["count"] = 0 + exact_match[category]["exact"] = 0 + exact_match["all"]["count"] += 1 + exact_match[category]["count"] += 1 + try: + match = int(prediction.trim() == reference.trim()) + exact_match[category]["exact"] += match + exact_match["all"]["exact"] += match + except Exception: + pass + return exact_match + + +def compute_test_suite_metric( + predictions: list, + references: list, + gold_dbs: list, + setup_sqls: list, + validate_sqls: list, + kmaps: dict, + db_dir: str, + categories: list[str] = None, +) -> tuple[Any, list[int | None]]: + """Compute test suite execution metric.""" + evaluator = test_suite_evaluation.Evaluator( + db_dir=db_dir, + kmaps=kmaps, + etype="exec", + plug_value=False, + keep_distinct=False, + progress_bar_for_each_datapoint=False, + ) + # Only used for Sparc/CoSQL + turn_scores: dict[str, list] = {"exec": [], "exact": []} + by_row_metrics: list[int | None] = [] + for prediction, reference, gold_db, setup_sql, validate_sql, category in tqdm( + zip(predictions, references, gold_dbs, setup_sqls, validate_sqls, categories), + total=len(predictions), + ): + turn_idx = 0 + # skip final utterance-query pairs + if turn_idx < 0: + continue + + # Register the timeout handler function + signal.signal(signal.SIGALRM, timeout_handler) + signal.alarm(TIMEOUT_SECONDS) + + try: + ex_metrics = evaluator.evaluate_one( + gold_db, + reference, + prediction, + setup_sql, + validate_sql, + turn_scores, + idx=turn_idx, + category=category, + ) + signal.alarm(0) + + by_row_metrics.append(int(ex_metrics["exec"])) + except Exception as e: + raise e + by_row_metrics.append(None) + pass + evaluator.finalize() + return evaluator.scores, by_row_metrics + + +def compute_metrics( + gold_sqls: list[str], + pred_sqls: list[str], + gold_dbs: list[str], + setup_sqls: list[str], + validate_sqls: list[str], + kmaps: dict, + db_schemas: dict, + database_dir: str, + lowercase_schema_match: bool, + model_name: str, + categories: list[str] = None, +) -> dict[str, str]: + """Compute all metrics for data slice.""" + if len(gold_sqls) != len(pred_sqls): + raise ValueError( + f"Gold {len(gold_sqls)} and pred {len(pred_sqls)} have different number of lines!" + ) + all_metrics: dict[str, Any] = {} + + # Execution Accuracy + metrics, by_row_metrics = compute_test_suite_metric( + pred_sqls, + gold_sqls, + gold_dbs, + setup_sqls, + validate_sqls, + kmaps, + database_dir, + categories, + ) + all_metrics["exec"] = metrics + all_metrics["by_row_exec"] = by_row_metrics + print_scores(metrics, model_name, "exec") + + # Exact Match Accuracy + metrics = compute_exact_match_metric( + pred_sqls, gold_sqls, gold_dbs, kmaps, database_dir, categories + ) + all_metrics["exact"] = metrics + print_scores(metrics, model_name, "exact") + + # Equality Accuracy + per_row_match = [ + int(gold.lower() == pred.lower()) for gold, pred in zip(gold_sqls, pred_sqls) + ] + all_metrics["equality"] = {"equality": sum(per_row_match) / len(gold_sqls)} + all_metrics["by_row_equality"] = per_row_match + + # Edit Distance + per_row_edit_dist = [ + edit_distance(gold, pred) for gold, pred in zip(gold_sqls, pred_sqls) + ] + edit_dist = sum(per_row_edit_dist) / len(gold_sqls) + all_metrics["edit_distance"] = {"edit_distance": edit_dist} + all_metrics["by_row_edit_distance"] = per_row_edit_dist + + return all_metrics + + +def get_to_print(metrics: dict, key: str, model_name: str, num_rows: int) -> dict: + """Get pretty print dictionary of metrics.""" + return { + "slice": key, + "model": model_name, + "support": num_rows, + "exec": f"{metrics[key]['exec']['all']['exec']:.3f}", + "exact": f"{metrics[key]['exact']['all']['exact']:.3f}", + "equality": f"{metrics[key]['equality']['equality']:.3f}", + "edit_distance": f"{metrics[key]['edit_distance']['edit_distance']:.3f}", + } + + +@click.group() +def cli() -> None: + """Entrypoint.""" + pass + + +@cli.command() +@click.option("--gold", type=str, required=True) +@click.option("--pred", type=str, required=True) +@click.option("--tables", type=str, required=True) +@click.option("--db", type=str, default="") +@click.option("--slice-attribute", type=str, default=None) +@click.option("--output-dir", type=str, default="") +@click.option("--output-filename", type=str, default="") +@click.option( + "--correct-sql-casing", type=bool, is_flag=True, default=False, required=False +) +@click.option( + "--lowercase-schema-match", type=bool, is_flag=True, default=False, required=False +) +def evaluate( + gold: str, + pred: str, + tables: str, + db: str, + slice_attribute: str, + output_dir: str, + output_filename: str, + correct_sql_casing: bool, + lowercase_schema_match: bool, +) -> None: + """Evaluate SQL. + + Args: + gold: path to gold sql file. + pred: path to predicted json lines file. + tables: the json path of the table metadata. + db: path to database dir. + slice_attribute: json attribute in gold data to slice on. + output_dir: the prediction output directory + output_filename: the prediction output filename + correct_sql_casing: whether to correct casing of SQL keywords + lowercase_schema_match: whether to lowercase schema match + """ + gold_path = Path(gold) + pred_path = Path(pred) + model_name = pred_path.stem + if not output_filename: + output_filename = pred_path.stem + "_eval.json" + console.print(f"Saving to {Path(output_dir) / output_filename}") + Path(output_dir).mkdir(parents=True, exist_ok=True) + + kmaps = test_suite_evaluation.build_foreign_key_map_from_json(tables) + db_schemas = read_tables_json(tables) + + gold_sqls_dict = json.load(gold_path.open("r", encoding="utf-8")) + pred_sqls_dict = [json.loads(l) for l in pred_path.open("r").readlines()] + + # Data validation + assert len(gold_sqls_dict) == len( + pred_sqls_dict + ), "Sample size doesn't match between pred and gold file" + + # Keep track of everything + full_results = [] + for gold_sql, pred_sql in zip(gold_sqls_dict, pred_sqls_dict): + merged_res = {**pred_sql, **gold_sql} + full_results.append(merged_res) + + gold_sqls = [ + re.sub(r"[\s\t\n]+", " ", p.get("gold", p.get("query", p.get("sql", "")))) + for p in gold_sqls_dict + ] + setup_sqls = [re.sub(r"[\s\t\n]+", " ", p["setup_sql"]) for p in gold_sqls_dict] + validate_sqls = [ + re.sub(r"[\s\t\n]+", " ", p["validation_sql"]) for p in gold_sqls_dict + ] + gold_dbs = [p.get("db_id", p.get("db", "")) for p in gold_sqls_dict] + pred_sqls = [re.sub(r"[\s\t\n]+", " ", p["pred"]) for p in pred_sqls_dict] + categories = [p.get("category", "") for p in gold_sqls_dict] + if correct_sql_casing: + # One line to correct casing of SQL keywords using correct_casing(sql) + gold_sqls = [correct_casing(sql) for sql in gold_sqls] + pred_sqls = [correct_casing(sql) for sql in pred_sqls] + + final_metrics: dict[str, dict[str, Any]] = {} + to_print = [] + final_metrics["all"] = compute_metrics( + gold_sqls=gold_sqls, + pred_sqls=pred_sqls, + gold_dbs=gold_dbs, + setup_sqls=setup_sqls, + validate_sqls=validate_sqls, + kmaps=kmaps, + db_schemas=db_schemas, + database_dir=db, + lowercase_schema_match=lowercase_schema_match, + model_name=model_name + "(all)", + categories=categories, + ) + + for k, v in final_metrics["all"].items(): + if k.startswith("by_row"): + assert len(v) == len(gold_sqls) + for dct, val in zip(full_results, v): + dct[k[len("by_row_") :]] = val + to_print.append(get_to_print(final_metrics, "all", model_name, len(gold_sqls))) + # TODO: could be way more efficient if we subsliced the results but...whatever + if slice_attribute: + for unq_value in sorted(set([g[slice_attribute] for g in gold_sqls_dict])): + idx_set = [ + i + for i, g in enumerate(gold_sqls_dict) + if g[slice_attribute] == unq_value + ] + print(f"Processing {unq_value} with {len(idx_set)} samples") + final_metrics[unq_value] = compute_metrics( + gold_sqls=[gold_sqls[i] for i in idx_set], + pred_sqls=[pred_sqls[i] for i in idx_set], + gold_dbs=[gold_dbs[i] for i in idx_set], + setup_sqls=[setup_sqls[i] for i in idx_set], + validate_sqls=[validate_sqls[i] for i in idx_set], + kmaps=kmaps, + db_schemas=db_schemas, + database_dir=db, + lowercase_schema_match=lowercase_schema_match, + model_name=model_name + f"({unq_value})", + categories=[categories[i] for i in idx_set], + ) + to_print.append( + get_to_print(final_metrics, unq_value, model_name, len(idx_set)) + ) + + df = pd.DataFrame(to_print) + console.print(df.to_csv(sep=",", index=False)) + console.print("******") + console.print(f"Saved metrics to {Path(output_dir) / output_filename}") + json.dump(final_metrics, open(Path(output_dir) / output_filename, "w"), indent=4) + output_filename = str(output_filename).replace("_eval.json", "_fd.jsonl") + console.print(f"Saved dump to {Path(output_dir) / output_filename}") + with open(Path(output_dir) / output_filename, "w") as f: + for dct in full_results: + f.write(json.dumps(dct) + "\n") + + +if __name__ == "__main__": + cli() diff --git a/duckdb-nsql/eval/get_manifest.py b/duckdb-nsql/eval/get_manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..068198a02df830ed12b6c41958310557532ddf49 --- /dev/null +++ b/duckdb-nsql/eval/get_manifest.py @@ -0,0 +1,29 @@ +"""Manifest utils.""" +from manifest import Manifest +from manifest.connections.client_pool import ClientConnection + + +def get_manifest( + manifest_client: str, + manifest_connection: str, + manifest_engine: str, +) -> Manifest: + """Get manifest engine.""" + if manifest_client in {"openai", "openaichat", "openai_mock", "openrouter", "azureendpoint"}: + manifest = Manifest( + client_name=manifest_client, + engine=manifest_engine, + ) + elif manifest_client in {"huggingface"}: + manifest = Manifest( + client_pool=[ + ClientConnection( + client_name=manifest_client, + client_connection=manifest_conn, + ) + for manifest_conn in manifest_connection.split(";") + ], + ) + else: + raise ValueError(f"Unknown manifest client {manifest_client}") + return manifest diff --git a/duckdb-nsql/eval/loaders.py b/duckdb-nsql/eval/loaders.py new file mode 100644 index 0000000000000000000000000000000000000000..a1c93f008a5c1a148ab42a5c6b561fd3c155c4ef --- /dev/null +++ b/duckdb-nsql/eval/loaders.py @@ -0,0 +1,78 @@ +"""Data loaders.""" +import json +import re +import string +from abc import ABC, abstractmethod + +from rich.console import Console +from data_utils import read_tables_json +from schema import Table + +RE_COLUMN = re.compile(r"^select (.+?) from") +RE_CONDS = re.compile(r"where (.+?)$") +RE_COND = re.compile(r"^(.+?)\s*([=><])\s*(.+?)$") + +translator = str.maketrans( + string.punctuation, " " * len(string.punctuation) +) # map punctuation to space + +console = Console(soft_wrap=True) + + +def standardize_column(col: str) -> str: + """Standardize the column name to SQL compatible.""" + col_name = col.replace("#", "num").replace("%", "perc") + col_name = col_name.strip().lower().translate(translator) + col_name = re.sub("[^0-9a-z ]", " ", col_name).strip() + col_name = re.sub(" +", "_", col_name) + if not col_name: + console.print(f"original {col}, new {col_name}") + return col_name + + +def clean_col(col: str) -> str: + """Remove table name and standardize column name.""" + if "." in col and not col.endswith("."): + col = col.split(".")[-1] + return standardize_column(col) + + +class Loader(ABC): + """Loader abstract class.""" + + @classmethod + @abstractmethod + def load_data(cls, path: str) -> list[dict]: + """Load data from path.""" + + @classmethod + @abstractmethod + def load_table_metadata(cls, path: str) -> dict[str, dict[str, Table]]: + """Extract table metadata from table-metadata-path.""" + + @classmethod + def format_output(cls, prediction: dict) -> dict: + """Parse for spider format.""" + return prediction + + +class DefaultLoader(Loader): + """Spider loader and writer.""" + + @classmethod + def load_data(cls, path: str) -> list[dict]: + """Load data from path.""" + try: + with open(path) as f: + data = json.loads(f.read()) + except json.decoder.JSONDecodeError: + # Try with jsonl + data = [json.loads(line) for line in open(path)] + return data + + @classmethod + def load_table_metadata(cls, path: str) -> dict[str, dict[str, Table]]: + """Extract table metadata from table-metadata-path.""" + # load the tables + db_to_tables = read_tables_json(path, lowercase=True) + return db_to_tables diff --git a/duckdb-nsql/eval/metric_utils.py b/duckdb-nsql/eval/metric_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..653696320cd811542184fa15cfade0b367290cc3 --- /dev/null +++ b/duckdb-nsql/eval/metric_utils.py @@ -0,0 +1,40 @@ +"""Utility metrics.""" +import sqlglot +from rich.console import Console +from sqlglot import parse_one + +console = Console(soft_wrap=True) + + +def correct_casing(sql: str) -> str: + """Correct casing of SQL.""" + parse: sqlglot.expressions.Expression = parse_one(sql, read="sqlite") + return parse.sql() + + +def prec_recall_f1(gold: set, pred: set) -> dict[str, float]: + """Compute precision, recall and F1 score.""" + prec = len(gold.intersection(pred)) / len(pred) if pred else 0.0 + recall = len(gold.intersection(pred)) / len(gold) if gold else 0.0 + f1 = 2 * prec * recall / (prec + recall) if prec + recall else 0.0 + return {"prec": prec, "recall": recall, "f1": f1} + + +def edit_distance(s1: str, s2: str) -> int: + """Compute edit distance between two strings.""" + # Make sure s1 is the shorter string + if len(s1) > len(s2): + s1, s2 = s2, s1 + + distances: list[int] = list(range(len(s1) + 1)) + for i2, c2 in enumerate(s2): + distances_ = [i2 + 1] + for i1, c1 in enumerate(s1): + if c1 == c2: + distances_.append(distances[i1]) + else: + distances_.append( + 1 + min((distances[i1], distances[i1 + 1], distances_[-1])) + ) + distances = distances_ + return distances[-1] diff --git a/duckdb-nsql/eval/metrics/test_suite_sql_eval b/duckdb-nsql/eval/metrics/test_suite_sql_eval new file mode 160000 index 0000000000000000000000000000000000000000..640a12975abf75a94e917caca149d56dbc6bcdd7 --- /dev/null +++ b/duckdb-nsql/eval/metrics/test_suite_sql_eval @@ -0,0 +1 @@ +Subproject commit 640a12975abf75a94e917caca149d56dbc6bcdd7 diff --git a/duckdb-nsql/eval/predict.py b/duckdb-nsql/eval/predict.py new file mode 100644 index 0000000000000000000000000000000000000000..3565c07d30556e720e570c4303be08425221b828 --- /dev/null +++ b/duckdb-nsql/eval/predict.py @@ -0,0 +1,345 @@ +"""Run dataset on text2sql zazu experiment. + +See README.md for more details. +""" +import datetime +import json +import multiprocessing +import random +import re +from pathlib import Path + +import click +import numpy as np +from constants import PROMPT_FORMATTERS +from loaders import DefaultLoader +from get_manifest import get_manifest +from manifest import Manifest +from prompt_formatters import RajkumarFormatter +from rich.console import Console +from schema import Table, TextToSQLModelResponse, TextToSQLParams +from text_to_sql import instruction_to_sql, instruction_to_sql_list +from doc_retriever import ( + load_documentation, + split_documents, + embed_documents, + query_docs, +) +from tqdm import tqdm +from transformers import AutoTokenizer + +console = Console(soft_wrap=True) + + +def generate_sql( + manifest: Manifest, + text_to_sql_in: list[TextToSQLParams], + retrieved_docs: list[list[str]], + prompt_formatter: RajkumarFormatter, + stop_tokens: list[str] | None = None, + overwrite_manifest: bool = False, + max_tokens: int = 300, + temperature: float = 0.1, + num_beams: int = 2, + parallel: bool = False, +) -> list[tuple[str, TextToSQLModelResponse]]: + """Call our text2sql function with manifest of our choice.""" + if parallel: + instruction_to_sql_resps: list[ + TextToSQLModelResponse + ] = instruction_to_sql_list( + params=text_to_sql_in, + extra_context=retrieved_docs, + manifest=manifest, + prompt_formatter=prompt_formatter, + overwrite_manifest=overwrite_manifest, + max_tokens=max_tokens, + temperature=0.1, + stop_sequences=stop_tokens, + num_beams=num_beams, + ) + else: + instruction_to_sql_resps = [ + instruction_to_sql( + params=_text_to_sql_in, + extra_context=_retrieved_docs, + manifest=manifest, + prompt_formatter=prompt_formatter, + overwrite_manifest=overwrite_manifest, + max_tokens=max_tokens, + temperature=temperature, + stop_sequences=stop_tokens, + num_beams=num_beams, + ) + for _retrieved_docs, _text_to_sql_in in tqdm( + zip(retrieved_docs, text_to_sql_in), + desc="Generating SQL", + total=len(text_to_sql_in), + disable=(len(text_to_sql_in) <= 1), + ) + ] + assert len(instruction_to_sql_resps) == len(text_to_sql_in) + + sql_statements = [] + for i in range(len(instruction_to_sql_resps)): + sql_statement = instruction_to_sql_resps[i].output.strip() + if "<>" in sql_statement: + sql_statement.replace("<>", "!=") + # Models sometime train to predict | + sql_statement = sql_statement.split("|")[-1].strip() + sql_statements.append(sql_statement) + return list(zip(sql_statements, instruction_to_sql_resps)) + + +def get_text_to_sql_in( + input_question: dict, db_to_tables: dict[str, dict[str, Table]] +) -> TextToSQLParams: + """Format input question for text2sql function.""" + question = input_question["question"] + db_id = input_question.get("db_id", None) + if db_id != "none": + table_params = list(db_to_tables.get(db_id, {}).values()) + else: + table_params = [] + if len(table_params) == 0: + console.print(f"[red] WARNING: No tables found for {db_id} [/red]") + text_to_sql_in = TextToSQLParams( + instruction=question, + database=db_id, + tables=table_params, + ) + return text_to_sql_in + + +@click.group() +def cli() -> None: + """Entrypoint.""" + pass + + +@cli.command() +@click.argument("dataset-path") +@click.argument("table-meta-path") +@click.option("--output-dir", type=str, default="") +@click.option("--run-name", type=str, default="") +@click.option("--num-run", type=int, default=-1) +@click.option("--num-print", type=int, default=20) +# Format options +@click.option("--prompt-format", type=str, default="spider") +# Prompt options +@click.option("--stop-tokens", type=str, default=[], multiple=True) +@click.option("--max-tokens", type=int, default=200) +@click.option("--temperature", type=float, default=0) +@click.option("--num-beams", type=int, default=-1) # use whatever is in manifest +@click.option("--max-context-length", type=int, default=-1) +# Docs options +@click.option( + "--markdown-docs-path", + #type=click.Path( + # exists=True, file_okay=True, dir_okay=True, readable=True, path_type=Path + #), + default="eval/docs/duckdb-web/docs/archive/0.9.2/sql", +) +@click.option("--num-retrieved-docs", type=int, default=0) +# Manifest options +@click.option("--manifest-client", type=str, default="openai") +@click.option("--manifest-engine", type=str, default="gpt-4o") +@click.option("--manifest-connection", type=str, default="http://localhost:5005") +@click.option("--overwrite-manifest", is_flag=True, default=False) +@click.option("--parallel", is_flag=True, default=False) +def predict( + dataset_path: str, + table_meta_path: str, + output_dir: str, + run_name: str, + num_run: int, + num_print: int, + prompt_format: str, + stop_tokens: list[str], + max_tokens: int, + temperature: float, + num_beams: int, + max_context_length: int, + markdown_docs_path: Path, + num_retrieved_docs: int, + manifest_client: str, + manifest_engine: str, + manifest_connection: str, + overwrite_manifest: bool, + parallel: bool, +) -> None: + """Predict SQL. + + Args: + dataset_path: the dataset path. + table_meta_path: the json path of the table metadata. + database_path: the database path for sqlite. + output_dir: the prediction output directory + run_name: special prefix to add to filename + num_run: the number of examples to run + num_print: the number of examples to print + prompt_format: the format of the prompt. E.g., "rajkumar" + stop_tokens: the stop tokens to try + max_tokens: the max tokens + temperature: the temperature + num_beams: the number of beams + max_context_length: max context length for demonstration truncation (-1 means None) + markdown_docs_path: path to duckdb sql docs + num_retrieved_docs: number of docs to retrieve + manifest_client: the manifest client + manifest_engine: the manifest engine + manifest_connection: the manifest connection + """ + multiprocessing.set_start_method("spawn", force=True) + random.seed(0) + np.random.seed(0) + locals_dict = locals() + locals_dict["markdown_docs_path"] = str(markdown_docs_path) + console.print(json.dumps(locals_dict, indent=2)) + + data_formatter = DefaultLoader() + + if prompt_format not in PROMPT_FORMATTERS: + raise ValueError(f"Unknown prompt format {prompt_format}") + prompt_formatter = PROMPT_FORMATTERS[prompt_format]() + + # load manifest + manifest = get_manifest( + manifest_client=manifest_client, + manifest_connection=manifest_connection, + manifest_engine=manifest_engine, + ) + manifest_params = manifest.client_pool.get_current_client().get_model_params() + console.print(f"Running with {manifest_params} manifest.") + model_name = manifest_params.get("engine", manifest_params["model_name"]) + + if manifest_client in {"openai", "openaichat", "openrouter", "azureendpoint"}: + tokenizer = AutoTokenizer.from_pretrained("gpt2", trust_remote_code=True) + else: + tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) + + if stop_tokens: + stop_tokens = [st.strip("'") for st in stop_tokens] + console.print(f"Stop tokens: {stop_tokens}") + + # Get output filename + full_dataset_path = Path(dataset_path) + # Get todays date + date_today = datetime.datetime.now().strftime("%y-%m-%d") + if run_name: + run_name = f"{run_name}_" + suffix = f"{run_name}{full_dataset_path.stem}_{date_today}.json" # noqa: E501 + prefix = f"{prompt_format}_{num_retrieved_docs}docs" + if manifest_client in {"openai", "openaiazure"}: + middleix = manifest_engine + elif manifest_client in {"huggingface", "ray"}: + middleix = Path(manifest_params.get("model_path", "")).name.replace("/", "-") + elif manifest_client in {"toma", "openrouter", "openaichat", "azureendpoint"}: + middleix = manifest_engine.split("/")[-1] + else: + raise ValueError(f"Unknown manifest client {manifest_client}") + output_filename = f"{prefix}_{middleix}_{suffix}" + console.print(f"Saving to {Path(output_dir) / output_filename}") + Path(output_dir).mkdir(parents=True, exist_ok=True) + + console.print("Loading metadata...") + db_to_tables = data_formatter.load_table_metadata(table_meta_path) + + console.print("Loading data...") + data = data_formatter.load_data(dataset_path) + if num_run > 0: + console.print(f"Running on {min(len(data), num_run)} examples") + data = data[:num_run] + original_data = data + + # load the examples + console.print("Formatting data...") + num_print = min(num_print, len(data)) + token_lengths = [] + text_to_sql_in = [ + get_text_to_sql_in(input_question, db_to_tables) for input_question in data + ] + + if num_retrieved_docs > 0: + console.print("Loading documenration and indexing...") + retrieved_docs = [] + doc_contents = load_documentation(markdown_docs_path) + chunked_docs = split_documents(doc_contents) + embedded_docs, full_embedding_mat = embed_documents(chunked_docs) + for i in tqdm(range(len(text_to_sql_in)), desc="Retrieving docs"): + _, retrieved_docs_strings = query_docs( + text_to_sql_in[i].instruction, + embedded_docs, + full_embedding_mat, + top_n=num_retrieved_docs, + ) + retrieved_docs.append(retrieved_docs_strings) + else: + retrieved_docs = [[] for _ in range(len(text_to_sql_in))] + + for i in range(num_print): + # Run a few to get some examples to print + generated_responses = generate_sql( + manifest=manifest, + text_to_sql_in=[text_to_sql_in[i]], + retrieved_docs=[retrieved_docs[i]], + stop_tokens=stop_tokens, + max_tokens=max_tokens, + temperature=temperature, + num_beams=num_beams, + prompt_formatter=prompt_formatter, + overwrite_manifest=overwrite_manifest, + parallel=parallel, + ) + for prediction, model_response in generated_responses: + prediction = re.sub(r"[\s\t\n]+", " ", prediction) + token_lengths.append(len(tokenizer(prediction).input_ids)) + console.print(f"[blue]Prompt:[/blue] {model_response.final_prompt}") + console.print(f"[red]Prediction:[/red] {prediction}") + if data[i].get("query") or data[i].get("sql"): + console.print( + "[purple]Gold:[/purple] " + f"{data[i].get('query') or data[i].get('sql')}" + ) + console.print("\n****\n") + + # Run the entire thing now - the to_print results will be in cache and fast + generated_sqls = generate_sql( + manifest=manifest, + text_to_sql_in=text_to_sql_in, + retrieved_docs=retrieved_docs, + stop_tokens=stop_tokens, + max_tokens=max_tokens, + temperature=temperature, + num_beams=num_beams, + prompt_formatter=prompt_formatter, + overwrite_manifest=overwrite_manifest, + parallel=parallel, + ) + + with open(Path(output_dir) / output_filename, "w") as fout: + for i, (prediction, model_response) in enumerate(generated_sqls): + if isinstance(model_response.final_prompt, str): + token_lengths.append( + len(tokenizer(model_response.final_prompt).input_ids) + ) + else: + for prompt in model_response.final_prompt: + token_lengths.append(len(tokenizer(prompt["content"]).input_ids)) + entry = { + **original_data[i], + "pred": prediction, + "raw_pred": model_response.output, + "raw_output": model_response.raw_output, + "prompt": model_response.final_prompt, + "tables": [tbl.dict() for tbl in text_to_sql_in[i].tables or []], + } + formatted_entry = data_formatter.format_output(entry) + print(json.dumps(formatted_entry), file=fout) + overflow = len([tl for tl in token_lengths if tl > 2048]) / len(token_lengths) + console.print(f"Overflow 2048 prompt {100*overflow:.2f}%") + console.print(f"Saved to {Path(output_dir) / output_filename}") + + +if __name__ == "__main__": + cli() diff --git a/duckdb-nsql/eval/prompt_formatters.py b/duckdb-nsql/eval/prompt_formatters.py new file mode 100644 index 0000000000000000000000000000000000000000..580928913969c1201319327c5b8eb99c0184ac3c --- /dev/null +++ b/duckdb-nsql/eval/prompt_formatters.py @@ -0,0 +1,1056 @@ +"""Rajkumar prompt formatter.""" + +from random import shuffle +from manifest import Manifest +from schema import Table +import re + + +class RajkumarFormatter: + """RajkumarFormatter class. + + From https://arxiv.org/pdf/2204.00498.pdf. + """ + + table_sep: str = "\n\n" + shuffle_table_order: bool = True + _cache: dict[tuple[str, str, str], list[str]] = {} + clean_whitespace = False + + @classmethod + def format_table(cls, table: Table) -> str: + """Get table format.""" + table_fmt = [] + for col in table.columns or []: + # This is technically an incorrect type, but it should be a catchall word + table_fmt.append(f" {col.name} {col.dtype or 'any'}") + if table_fmt: + all_cols = ",\n".join(table_fmt) + create_tbl = f"CREATE TABLE {table.name} (\n{all_cols}\n)" + else: + create_tbl = f"CREATE TABLE {table.name}" + return create_tbl + + @classmethod + def format_all_tables(cls, tables: list[Table], instruction: str) -> list[str]: + """Get all tables format.""" + table_texts = [cls.format_table(table) for table in tables] + key = ("tables", instruction, str(tables)) + if key not in cls._cache: + shuffle(table_texts) + cls._cache[key] = table_texts + else: + table_texts = cls._cache[key] + return table_texts + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n\n/*\nHere is additional documentation about DuckDB that could be useful.\n--------\n{context_str}\n--------\n*/" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + return f"""{table_text}\n\n\n-- Using valid DuckDB SQL, answer the following question for the tables provided above.{context_text}\n\n-- {instruction}\n""" # noqa: E501 + + @classmethod + def format_model_output(cls, output_sql: str, prompt: str) -> str: + """Format model output.""" + clean_sql = (output_sql + .replace('```sql\n', '') + .replace('```duckdb\n', '') + .replace('```\n', '') + .replace('```', '')).strip() + + if clean_sql.find(';') != -1: + clean_sql[:clean_sql.find(';')].strip() + + if not clean_sql.endswith(";"): + clean_sql += ";" + + return clean_sql + + @classmethod + def format_gold_output(cls, output_sql: str) -> str: + """Format gold output for demonstration.""" + return output_sql + +class MotherDuckFormatter(RajkumarFormatter): + """MotherDuck class.""" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + return f"""{table_text}\n\n\n-- Using valid DuckDB SQL, answer the following question for the tables provided above.{context_text}\n\n-- {instruction}\n```sql\n""" # noqa: E501 + + +class DuckDBFormatter(RajkumarFormatter): + """DuckDB class.""" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + return f"""{table_text}\n\n\n-- Using valid DuckDB SQL, answer the following question for the tables provided above.{context_text}\n\n-- {instruction}\n```sql\n""" # noqa: E501 + + +class DuckDBInstFormatter(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """### Instruction:\n{instruction}\n\n### Input:\n{input}{context}\n### Question:\n{question}\n\n### Response (use duckdb shorthand if possible):\n""" + INSTRUCTION_TEMPLATE = """Your task is to generate valid duckdb SQL to answer the following question{has_schema}""" # noqa: E501 + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + input = "" + if table_text: + input = """Here is the database schema that the SQL query will run on:\n{schema}\n""".format( # noqa: E501 + schema=table_text + ) + instruction = cls.PROMPT_TEMPLATE.format( + instruction=cls.INSTRUCTION_TEMPLATE.format( + has_schema="." + if table_text == "" + else ", given a duckdb database schema." + ), + context=context_text, + input=input, + question=instruction, + ) + return instruction + +class DuckDBInstFormatterLlamaShort(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """<|begin_of_text|><|start_header_id|>system<|end_header_id|> + +Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query. + +Here are some DuckDB SQL syntax specifics you should be aware of: + +- DuckDB uses double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity and single quotes (') to define string literals +- DuckDB can query CSV, Parquet, and JSON directly without loading them first, e.g. `SELECT * FROM 'data.csv';` +- DuckDB supports CREATE TABLE AS (CTAS): `CREATE TABLE new_table AS SELECT * FROM old_table;` +- DuckDB queries can start with FROM, and optionally omit SELECT *, e.g. `FROM my_table WHERE condition;` is equivalent to `SELECT * FROM my_table WHERE condition;` +- DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly, e.g. `SELECT 1 + 1 AS result;` +- DuckDB supports attaching multiple databases, unsing the ATTACH statement: `ATTACH 'my_database.duckdb' AS mydb;`. Tables within attached databases can be accessed using the dot notation (.), e.g. `SELECT * FROM mydb.table_name syntax`. The default databases doesn't require the do notation to access tables. The default database can be changed with the USE statement, e.g. `USE my_db;`. +- DuckDB is generally more lenient with implicit type conversions (e.g. `SELECT '42' + 1;` - Implicit cast, result is 43), but you can always be explicit using `::`, e.g. `SELECT '42'::INTEGER + 1;` +- DuckDB can extract parts of strings and lists using [start:end] or [start:end:step] syntax. Indexes start at 1. String slicing: `SELECT 'DuckDB'[1:4];`. Array/List slicing: `SELECT [1, 2, 3, 4][1:3];` +- DuckDB has a powerful way to select or transform multiple columns using patterns or functions. You can select columns matching a pattern: `SELECT COLUMNS('sales_.*') FROM sales_data;` or transform multiple columns with a function: `SELECT AVG(COLUMNS('sales_.*')) FROM sales_data;` +- DuckDB has an easy way to include/exclude or modify columns when selecting all: e.g. Exclude: `SELECT * EXCLUDE (sensitive_data) FROM users;` Replace: `SELECT * REPLACE (UPPER(name) AS name) FROM users;` +- DuckDB has a shorthand for grouping/ordering by all non-aggregated/all columns. e.g `SELECT category, SUM(sales) FROM sales_data GROUP BY ALL;` and `SELECT * FROM my_table ORDER BY ALL;` +- DuckDB can combine tables by matching column names, not just their positions using UNION BY NAME. E.g. `SELECT * FROM table1 UNION BY NAME SELECT * FROM table2;` +- DuckDB has an inutitive syntax to create List/Struct/Map and Array types. Create complex types using intuitive syntax. List: `SELECT [1, 2, 3] AS my_list;`, Struct: `{{'a': 1, 'b': 'text'}} AS my_struct;`, Map: `MAP([1,2],['one','two']) as my_map;`. All types can also be nested into each other. Array types are fixed size, while list types have variable size. Compared to Structs, MAPs do not need to have the same keys present for each row, but keys can only be of type Integer or Varchar. Example: `CREATE TABLE example (my_list INTEGER[], my_struct STRUCT(a INTEGER, b TEXT), my_map MAP(INTEGER, VARCHAR), my_array INTEGER[3], my_nested_struct STRUCT(a INTEGER, b Integer[3]));` +- DuckDB has an inutive syntax to access struct fields using dot notation (.) or brackets ([]) with the field name. Maps fields can be accessed by brackets ([]). +- DuckDB's way of converting between text and timestamps, and extract date parts. Current date as 'YYYY-MM-DD': `SELECT strftime(NOW(), '%Y-%m-%d');` String to timestamp: `SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP;`, Extract Year from date: `SELECT EXTRACT(YEAR FROM DATE '2023-07-23');` +- Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. E.g.: `SELECT a + b AS total FROM my_table WHERE total > 10 GROUP BY total HAVING total < 20;` +- DuckDB allows generating lists using expressions similar to Python list comprehensions. E.g. `SELECT [x*2 FOR x IN [1, 2, 3]];` Returns [2, 4, 6]. +- DuckDB allows chaining multiple function calls together using the dot (.) operator. E.g.: `SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB';` +- DuckDB has a JSON data type. It supports selecting fields from the JSON with a JSON-Path expression using the arrow operator, -> (returns JSON) or ->> (returns text) with JSONPath expressions. For example: `SELECT data->'$.user.id' AS user_id, data->>'$.event_type' AS event_type FROM events;` +- DuckDB has built-in functions for regex regexp_matches(column, regex), regexp_replace(column, regex), and regexp_extract(column, regex). +- DuckDB has a way to quickly get a subset of your data with `SELECT * FROM large_table USING SAMPLE 10%;`<|eot_id|> +<|start_header_id|>user<|end_header_id|> + +Database Schema: +Here is the schema of the DuckDB database that the SQL query will run on: +```sql +{schema} +``` + +Question: +Here is the question or an instruction the user provided: +{question} + +Task: +Write a DuckDB SQL query for the given question!<|eot_id|> +<|start_header_id|>assistant<|end_header_id|> + +```sql +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterLlamaBasic(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """<|begin_of_text|><|start_header_id|>system<|end_header_id|> + +Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query.<|eot_id|> +<|start_header_id|>user<|end_header_id|> + +Database Schema: +Here is the schema of the DuckDB database that the SQL query will run on: +```sql +{schema} +``` + +Question: +Here is the question or an instruction the user provided: +{question} + +Task: +Write a DuckDB SQL query for the given question!<|eot_id|> +<|start_header_id|>assistant<|end_header_id|> + +```sql +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterLlamaSyntax(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """<|begin_of_text|><|start_header_id|>system<|end_header_id|> + +Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query. + +Here are some DuckDB SQL syntax specifics you should be aware of: + +- DuckDB uses double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity and single quotes (') to define string literals +- DuckDB can query CSV, Parquet, and JSON directly without loading them first, e.g. `SELECT * FROM 'data.csv';` +- DuckDB supports CREATE TABLE AS (CTAS): `CREATE TABLE new_table AS SELECT * FROM old_table;` +- DuckDB queries can start with FROM, and optionally omit SELECT *, e.g. `FROM my_table WHERE condition;` is equivalent to `SELECT * FROM my_table WHERE condition;` +- DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly, e.g. `SELECT 1 + 1 AS result;` +- DuckDB supports attaching multiple databases, unsing the ATTACH statement: `ATTACH 'my_database.duckdb' AS mydb;`. Tables within attached databases can be accessed using the dot notation (.), e.g. `SELECT * FROM mydb.table_name syntax`. The default databases doesn't require the do notation to access tables. The default database can be changed with the USE statement, e.g. `USE my_db;`. +- DuckDB is generally more lenient with implicit type conversions (e.g. `SELECT '42' + 1;` - Implicit cast, result is 43), but you can always be explicit using `::`, e.g. `SELECT '42'::INTEGER + 1;` +- DuckDB can extract parts of strings and lists using [start:end] or [start:end:step] syntax. Indexes start at 1. String slicing: `SELECT 'DuckDB'[1:4];`. Array/List slicing: `SELECT [1, 2, 3, 4][1:3];` +- DuckDB has a powerful way to select or transform multiple columns using patterns or functions. You can select columns matching a pattern: `SELECT COLUMNS('sales_.*') FROM sales_data;` or transform multiple columns with a function: `SELECT AVG(COLUMNS('sales_.*')) FROM sales_data;` +- DuckDB has an easy way to include/exclude or modify columns when selecting all: e.g. Exclude: `SELECT * EXCLUDE (sensitive_data) FROM users;` Replace: `SELECT * REPLACE (UPPER(name) AS name) FROM users;` +- DuckDB has a shorthand for grouping/ordering by all non-aggregated/all columns. e.g `SELECT category, SUM(sales) FROM sales_data GROUP BY ALL;` and `SELECT * FROM my_table ORDER BY ALL;` +- DuckDB can combine tables by matching column names, not just their positions using UNION BY NAME. E.g. `SELECT * FROM table1 UNION BY NAME SELECT * FROM table2;` +- DuckDB has an inutitive syntax to create List/Struct/Map and Array types. Create complex types using intuitive syntax. List: `SELECT [1, 2, 3] AS my_list;`, Struct: `{{'a': 1, 'b': 'text'}} AS my_struct;`, Map: `MAP([1,2],['one','two']) as my_map;`. All types can also be nested into each other. Array types are fixed size, while list types have variable size. Compared to Structs, MAPs do not need to have the same keys present for each row, but keys can only be of type Integer or Varchar. Example: `CREATE TABLE example (my_list INTEGER[], my_struct STRUCT(a INTEGER, b TEXT), my_map MAP(INTEGER, VARCHAR), my_array INTEGER[3], my_nested_struct STRUCT(a INTEGER, b Integer[3]));` +- DuckDB has an inutive syntax to access struct fields using dot notation (.) or brackets ([]) with the field name. Maps fields can be accessed by brackets ([]). +- DuckDB's way of converting between text and timestamps, and extract date parts. Current date as 'YYYY-MM-DD': `SELECT strftime(NOW(), '%Y-%m-%d');` String to timestamp: `SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP;`, Extract Year from date: `SELECT EXTRACT(YEAR FROM DATE '2023-07-23');` +- Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. E.g.: `SELECT a + b AS total FROM my_table WHERE total > 10 GROUP BY total HAVING total < 20;` +- DuckDB allows generating lists using expressions similar to Python list comprehensions. E.g. `SELECT [x*2 FOR x IN [1, 2, 3]];` Returns [2, 4, 6]. +- DuckDB allows chaining multiple function calls together using the dot (.) operator. E.g.: `SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB';` +- DuckDB has a JSON data type. It supports selecting fields from the JSON with a JSON-Path expression using the arrow operator, -> (returns JSON) or ->> (returns text) with JSONPath expressions. For example: `SELECT data->'$.user.id' AS user_id, data->>'$.event_type' AS event_type FROM events;` +- DuckDB has built-in functions for regex regexp_matches(column, regex), regexp_replace(column, regex), and regexp_extract(column, regex). +- DuckDB has a way to quickly get a subset of your data with `SELECT * FROM large_table USING SAMPLE 10%;`<|eot_id|> +<|start_header_id|>user<|end_header_id|> + +Database Schema: +Here is the schema of the DuckDB database that the SQL query will run on: +```sql +{schema} +``` + +Question: +Here is the question or an instruction the user provided: +{question} + +Task: +Write a DuckDB SQL query for the given question!<|eot_id|> +<|start_header_id|>assistant<|end_header_id|> + +```sql +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterGraniteShort(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """System: +Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query. + +Here are some DuckDB SQL syntax specifics you should be aware of: + + +- DuckDB use double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity and single quotes (') to define string literals +- DuckDB can query CSV, Parquet, and JSON directly without loading them first, e.g. `SELECT * FROM 'data.csv';` +- DuckDB supports CREATE TABLE AS (CTAS): `CREATE TABLE new_table AS SELECT * FROM old_table;` +- DuckDB queries can start with FROM, and optionally omit SELECT *, e.g. `FROM my_table WHERE condition;` is equivalent to `SELECT * FROM my_table WHERE condition;` +- DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly, e.g. `SELECT 1 + 1 AS result;` +- DuckDB supports attaching multiple databases, unsing the ATTACH statement: `ATTACH 'my_database.duckdb' AS mydb;`. Tables within attached databases can be accessed using the dot notation (.), e.g. `SELECT * FROM mydb.table_name syntax`. The default databases doesn't require the do notation to access tables. The default database can be changed with the USE statement, e.g. `USE my_db;`. +- DuckDB is generally more lenient with implicit type conversions (e.g. `SELECT '42' + 1;` - Implicit cast, result is 43), but you can always be explicit using `::`, e.g. `SELECT '42'::INTEGER + 1;` +- DuckDB can extract parts of strings and lists using [start:end] or [start:end:step] syntax. Indexes start at 1. String slicing: `SELECT 'DuckDB'[1:4];`. Array/List slicing: `SELECT [1, 2, 3, 4][1:3];` +- DuckDB has a powerful way to select or transform multiple columns using patterns or functions. You can select columns matching a pattern: `SELECT COLUMNS('sales_.*') FROM sales_data;` or transform multiple columns with a function: `SELECT AVG(COLUMNS('sales_.*')) FROM sales_data;` +- DuckDB an easy way to include/exclude or modify columns when selecting all: e.g. Exclude: `SELECT * EXCLUDE (sensitive_data) FROM users;` Replace: `SELECT * REPLACE (UPPER(name) AS name) FROM users;` +- DuckDB has a shorthand for grouping/ordering by all non-aggregated/all columns. e.g `SELECT category, SUM(sales) FROM sales_data GROUP BY ALL;` and `SELECT * FROM my_table ORDER BY ALL;` +- DuckDB can combine tables by matching column names, not just their positions using UNION BY NAME. E.g. `SELECT * FROM table1 UNION BY NAME SELECT * FROM table2;` +- DuckDB has an inutitive syntax to create List/Struct/Map and Array types. Create complex types using intuitive syntax. List: `SELECT [1, 2, 3] AS my_list;`, Struct: `{{'a': 1, 'b': 'text'}} AS my_struct;`, Map: `MAP([1,2],['one','two']) as my_map;`. All types can also be nested into each other. Array types are fixed size, while list types have variable size. Compared to Structs, MAPs do not need to have the same keys present for each row, but keys can only be of type Integer or Varchar. Example: `CREATE TABLE example (my_list INTEGER[], my_struct STRUCT(a INTEGER, b TEXT), my_map MAP(INTEGER, VARCHAR), my_array INTEGER[3], my_nested_struct STRUCT(a INTEGER, b Integer[3]));` +- DuckDB has an inutive syntax to access struct fields using dot notation (.) or brackets ([]) with the field name. Maps fields can be accessed by brackets ([]). +- DuckDB's way of converting between text and timestamps, and extract date parts. Current date as 'YYYY-MM-DD': `SELECT strftime(NOW(), '%Y-%m-%d');` String to timestamp: `SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP;`, Extract Year from date: `SELECT EXTRACT(YEAR FROM DATE '2023-07-23');` +- Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. E.g.: `SELECT a + b AS total FROM my_table WHERE total > 10 GROUP BY total HAVING total < 20;` +- DuckDB allows generating lists using expressions similar to Python list comprehensions. E.g. `SELECT [x*2 FOR x IN [1, 2, 3]];` Returns [2, 4, 6]. +- DuckDB allows chaining multiple function calls together using the dot (.) operator. E.g.: `SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB';` +- DuckDB has a JSON data type. It supports selecting fields from the JSON with a JSON-Path expression using the arrow operator, -> (returns JSON) or ->> (returns text) with JSONPath expressions. For example: `SELECT data->'$.user.id' AS user_id, data->>'$.event_type' AS event_type FROM events;` +- DuckDB has built-in functions for regex regexp_matches(column, regex), regexp_replace(column, regex), and regexp_extract(column, regex). +- DuckDB has a way to quickly get a subset of your data with `SELECT * FROM large_table USING SAMPLE 10%;` + +Here is the schema of the DuckDB database that the SQL query will run on: +{schema} + +Question: +Here is the question or an instruction the user provided: +{question} + +Write a DuckDB SQL query for the given question! + +Answer: +``` +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterLlama(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """<|begin_of_text|> + +Your task is to generate valid DuckDB SQL to answer the following question, given a DuckDB database schema. + +## DuckDB SQL syntax specifics you should be aware of: + +### Case Insensitivity and Quoting: + +Identifiers (tables, columns): Case-insensitive, but DuckDB remembers the case you use. Use double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity. +``` +CREATE TABLE "My Table" ("column_name" VARCHAR); -- Spaces and mixed case +SELECT "column_name" FROM "My Table"; +``` + +### String Literals: Always use single quotes (') to define string literals. +``` +SELECT 'This is a string' AS text; +``` + +### Direct File Querying: Query CSV, Parquet, and JSON files directly without loading them first. + +``` +SELECT * FROM 'data.csv'; +SELECT * FROM 'data.parquet'; +SELECT * FROM 'data.json'; +``` + +### CREATE TABLE AS (CTAS): Create tables from query results. + +``` +CREATE TABLE squares AS SELECT i, i * i AS square FROM generate_series(1, 10) t(i); +``` + +### FROM-First Syntax (Optional SELECT): Start queries with FROM, and optionally omit SELECT *. + +``` +FROM my_table WHERE condition; -- Equivalent to SELECT * FROM my_table WHERE condition +``` + +### SELECT without FROM: DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly. + +``` +SELECT 1 + 1 AS result; +``` + +### GROUP BY ALL/ORDER BY ALL: Shorthand for grouping/ordering by all non-aggregated/all columns. + +``` +SELECT category, SUM(sales) FROM sales_data GROUP BY ALL; +SELECT * FROM my_table ORDER BY ALL; +``` + +### SELECT COLUMNS(): Powerful way to select or transform multiple columns using patterns or functions. + +``` +-- Select columns matching a pattern +SELECT COLUMNS('sales_.*') FROM sales_data; + +-- Transform multiple columns with a function +SELECT AVG(COLUMNS(*)) FROM sales_data; +``` + +### UNION BY NAME: Combine tables by matching column names, not just their positions. + +``` +SELECT * FROM table1 UNION BY NAME SELECT * FROM table2; +``` + +### Implicit/Explicit Casting: DuckDB is generally more lenient with implicit type conversions, but you can always be explicit using :: + +``` +SELECT '42' + 1; -- Implicit cast, result is 43 +SELECT '42'::INTEGER + 1; -- Explicit cast, result is 43 +``` + +### String/List Slicing: Extract parts of strings and lists using [start:end] or [start:end:step] syntax. + +``` +SELECT 'DuckDB'[1:4]; -- Returns 'Duck' +SELECT [1, 2, 3, 4][1:3]; -- Returns [1, 2, 3] +``` + +### Simple List/Struct/Map/Array Creation: Create complex types using intuitive syntax. + +In a SELECT statement: +``` +SELECT [1, 2, 3] AS my_list, {{'a': 1, 'b': 'text'}} AS my_struct, MAP([1,2],['one','two']) as my_map; +``` + +When creating a table: +``` +CREATE TABLE data ( + my_list INTEGER[], + my_struct STRUCT(a INTEGER, b TEXT), + my_map MAP(INTEGER, VARCHAR), + my_array INTEGER[3] +); +``` + +### Timestamp Conversions and Extraction: Convert between text and timestamps, and extract date parts. + +``` +SELECT strftime(NOW(), '%Y-%m-%d'); -- Current date as 'YYYY-MM-DD' +SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP; -- String to timestamp +SELECT EXTRACT(YEAR FROM DATE '2023-07-23'); -- Extract year +``` + +### Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. + +``` +SELECT a + b AS total +FROM my_table +WHERE total > 10 +GROUP BY total +HAVING total < 20; +``` + +### List Comprehensions: Generate lists using expressions similar to Python list comprehensions. + +``` +SELECT [x*2 FOR x IN [1, 2, 3]]; -- Returns [2, 4, 6] +``` + +### Function Chaining: Chain multiple function calls together using the dot (.) operator. + +``` +SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB' +``` + +### Regular Expressions: DuckDB has built-in functions for regex matching, replacement, and extraction. + +``` +SELECT regexp_matches('DuckDB', 'Duck'); -- Returns true +SELECT regexp_replace('DuckDB', 'Duck', 'Goose'); -- Returns 'GooseDB' +SELECT regexp_extract('DuckDB', '(\w+)(DB)', 1); -- Returns 'Duck' +``` + +### Sampling: Quickly get a subset of your data with SAMPLE or TABLESAMPLE. + +``` +SELECT * FROM large_table USING SAMPLE 10%; -- Random 10% sample +SELECT * FROM large_table TABLESAMPLE BERNOULLI(10); -- Bernoulli sampling +``` + +### ATTACH and Access: Attach external databases and reference their objects using databasename.table_name syntax. + +``` +ATTACH 'my_database.duckdb' AS mydb; +SELECT * FROM mydb.my_table; +``` + +### SUMMARIZE: Get summary statistics (min, max, unique count, average, standard deviation, quartiles, and count) of a table. + +``` +SUMMARIZE table_name; +``` + +### DESCRIBE: Get schema of a table (column_name, column_type, null, key, default, extra). + +``` +DESCRIBE table_name; +``` + +Database Schema: +Here is the schema of the DuckDB database that the SQL query will run on: +{schema} + +Question: +Here is the question or an instruction the user provided: +{question} + +Task: +Write a DuckDB SQL query for the given question! + +Here is the valid DuckDB SQL query: +``` +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterGranite(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """System: + +Your task is to generate valid DuckDB SQL to answer the following question, given a DuckDB database schema. + +## DuckDB SQL syntax specifics you should be aware of: + +### Case Insensitivity and Quoting: + +Identifiers (tables, columns): Case-insensitive, but DuckDB remembers the case you use. Use double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity. +``` +CREATE TABLE "My Table" ("column_name" VARCHAR); -- Spaces and mixed case +SELECT "column_name" FROM "My Table"; +``` + +### String Literals: Always use single quotes (') to define string literals. +``` +SELECT 'This is a string' AS text; +``` + +### Direct File Querying: Query CSV, Parquet, and JSON files directly without loading them first. + +``` +SELECT * FROM 'data.csv'; +SELECT * FROM 'data.parquet'; +SELECT * FROM 'data.json'; +``` + +### CREATE TABLE AS (CTAS): Create tables from query results. + +``` +CREATE TABLE squares AS SELECT i, i * i AS square FROM generate_series(1, 10) t(i); +``` + +### FROM-First Syntax (Optional SELECT): Start queries with FROM, and optionally omit SELECT *. + +``` +FROM my_table WHERE condition; -- Equivalent to SELECT * FROM my_table WHERE condition +``` + +### SELECT without FROM: DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly. + +``` +SELECT 1 + 1 AS result; +``` + +### GROUP BY ALL/ORDER BY ALL: Shorthand for grouping/ordering by all non-aggregated/all columns. + +``` +SELECT category, SUM(sales) FROM sales_data GROUP BY ALL; +SELECT * FROM my_table ORDER BY ALL; +``` + +### SELECT COLUMNS(): Powerful way to select or transform multiple columns using patterns or functions. + +``` +-- Select columns matching a pattern +SELECT COLUMNS('sales_.*') FROM sales_data; + +-- Transform multiple columns with a function +SELECT AVG(COLUMNS(*)) FROM sales_data; +``` + +### UNION BY NAME: Combine tables by matching column names, not just their positions. + +``` +SELECT * FROM table1 UNION BY NAME SELECT * FROM table2; +``` + +### Implicit/Explicit Casting: DuckDB is generally more lenient with implicit type conversions, but you can always be explicit using :: + +``` +SELECT '42' + 1; -- Implicit cast, result is 43 +SELECT '42'::INTEGER + 1; -- Explicit cast, result is 43 +``` + +### String/List Slicing: Extract parts of strings and lists using [start:end] or [start:end:step] syntax. + +``` +SELECT 'DuckDB'[1:4]; -- Returns 'Duck' +SELECT [1, 2, 3, 4][1:3]; -- Returns [1, 2, 3] +``` + +### Simple List/Struct/Map/Array Creation: Create complex types using intuitive syntax. + +In a SELECT statement: +``` +SELECT [1, 2, 3] AS my_list, {{'a': 1, 'b': 'text'}} AS my_struct, MAP([1,2],['one','two']) as my_map; +``` + +When creating a table: +``` +CREATE TABLE data ( + my_list INTEGER[], + my_struct STRUCT(a INTEGER, b TEXT), + my_map MAP(INTEGER, VARCHAR), + my_array INTEGER[3] +); +``` + +### Timestamp Conversions and Extraction: Convert between text and timestamps, and extract date parts. + +``` +SELECT strftime(NOW(), '%Y-%m-%d'); -- Current date as 'YYYY-MM-DD' +SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP; -- String to timestamp +SELECT EXTRACT(YEAR FROM DATE '2023-07-23'); -- Extract year +``` + +### Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. + +``` +SELECT a + b AS total +FROM my_table +WHERE total > 10 +GROUP BY total +HAVING total < 20; +``` + +### List Comprehensions: Generate lists using expressions similar to Python list comprehensions. + +``` +SELECT [x*2 FOR x IN [1, 2, 3]]; -- Returns [2, 4, 6] +``` + +### Function Chaining: Chain multiple function calls together using the dot (.) operator. + +``` +SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB' +``` + +### Regular Expressions: DuckDB has built-in functions for regex matching, replacement, and extraction. + +``` +SELECT regexp_matches('DuckDB', 'Duck'); -- Returns true +SELECT regexp_replace('DuckDB', 'Duck', 'Goose'); -- Returns 'GooseDB' +SELECT regexp_extract('DuckDB', '(\w+)(DB)', 1); -- Returns 'Duck' +``` + +### Sampling: Quickly get a subset of your data with SAMPLE or TABLESAMPLE. + +``` +SELECT * FROM large_table USING SAMPLE 10%; -- Random 10% sample +SELECT * FROM large_table TABLESAMPLE BERNOULLI(10); -- Bernoulli sampling +``` + +### ATTACH and Access: Attach external databases and reference their objects using databasename.table_name syntax. + +``` +ATTACH 'my_database.duckdb' AS mydb; +SELECT * FROM mydb.my_table; +``` + +### SUMMARIZE: Get summary statistics (min, max, unique count, average, standard deviation, quartiles, and count) of a table. + +``` +SUMMARIZE table_name; +``` + +### DESCRIBE: Get schema of a table (column_name, column_type, null, key, default, extra). + +``` +DESCRIBE table_name; +``` + +Here is the schema of the DuckDB database that the SQL query will run on: +{schema} + +Question: +Here is the question or an instruction the user provided: +{question} + +Please write a DuckDB SQL query that answers the user's question or instruction. Use DuckDB-specific syntax if possible. + +Answer: +``` +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterPhi(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """<|endoftext|><|user|> +Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query. + +Here are some DuckDB SQL syntax specifics you should be aware of: + + +- DuckDB use double quotes (") for identifiers that contain spaces or special characters, or to force case-sensitivity and single quotes (') to define string literals +- DuckDB can query CSV, Parquet, and JSON directly without loading them first, e.g. `SELECT * FROM 'data.csv';` +- DuckDB supports CREATE TABLE AS (CTAS): `CREATE TABLE new_table AS SELECT * FROM old_table;` +- DuckDB queries can start with FROM, and optionally omit SELECT *, e.g. `FROM my_table WHERE condition;` is equivalent to `SELECT * FROM my_table WHERE condition;` +- DuckDB allows you to use SELECT without a FROM clause to generate a single row of results or to work with expressions directly, e.g. `SELECT 1 + 1 AS result;` +- DuckDB supports attaching multiple databases, unsing the ATTACH statement: `ATTACH 'my_database.duckdb' AS mydb;`. Tables within attached databases can be accessed using the dot notation (.), e.g. `SELECT * FROM mydb.table_name syntax`. The default databases doesn't require the do notation to access tables. The default database can be changed with the USE statement, e.g. `USE my_db;`. +- DuckDB is generally more lenient with implicit type conversions (e.g. `SELECT '42' + 1;` - Implicit cast, result is 43), but you can always be explicit using `::`, e.g. `SELECT '42'::INTEGER + 1;` +- DuckDB can extract parts of strings and lists using [start:end] or [start:end:step] syntax. Indexes start at 1. String slicing: `SELECT 'DuckDB'[1:4];`. Array/List slicing: `SELECT [1, 2, 3, 4][1:3];` +- DuckDB has a powerful way to select or transform multiple columns using patterns or functions. You can select columns matching a pattern: `SELECT COLUMNS('sales_.*') FROM sales_data;` or transform multiple columns with a function: `SELECT AVG(COLUMNS('sales_.*')) FROM sales_data;` +- DuckDB an easy way to include/exclude or modify columns when selecting all: e.g. Exclude: `SELECT * EXCLUDE (sensitive_data) FROM users;` Replace: `SELECT * REPLACE (UPPER(name) AS name) FROM users;` +- DuckDB has a shorthand for grouping/ordering by all non-aggregated/all columns. e.g `SELECT category, SUM(sales) FROM sales_data GROUP BY ALL;` and `SELECT * FROM my_table ORDER BY ALL;` +- DuckDB can combine tables by matching column names, not just their positions using UNION BY NAME. E.g. `SELECT * FROM table1 UNION BY NAME SELECT * FROM table2;` +- DuckDB has an inutitive syntax to create List/Struct/Map and Array types. Create complex types using intuitive syntax. List: `SELECT [1, 2, 3] AS my_list;`, Struct: `{{'a': 1, 'b': 'text'}} AS my_struct;`, Map: `MAP([1,2],['one','two']) as my_map;`. All types can also be nested into each other. Array types are fixed size, while list types have variable size. Compared to Structs, MAPs do not need to have the same keys present for each row, but keys can only be of type Integer or Varchar. Example: `CREATE TABLE example (my_list INTEGER[], my_struct STRUCT(a INTEGER, b TEXT), my_map MAP(INTEGER, VARCHAR), my_array INTEGER[3], my_nested_struct STRUCT(a INTEGER, b Integer[3]));` +- DuckDB has an inutive syntax to access struct fields using dot notation (.) or brackets ([]) with the field name. Maps fields can be accessed by brackets ([]). +- DuckDB's way of converting between text and timestamps, and extract date parts. Current date as 'YYYY-MM-DD': `SELECT strftime(NOW(), '%Y-%m-%d');` String to timestamp: `SELECT strptime('2023-07-23', '%Y-%m-%d')::TIMESTAMP;`, Extract Year from date: `SELECT EXTRACT(YEAR FROM DATE '2023-07-23');` +- Column Aliases in WHERE/GROUP BY/HAVING: You can use column aliases defined in the SELECT clause within the WHERE, GROUP BY, and HAVING clauses. E.g.: `SELECT a + b AS total FROM my_table WHERE total > 10 GROUP BY total HAVING total < 20;` +- DuckDB allows generating lists using expressions similar to Python list comprehensions. E.g. `SELECT [x*2 FOR x IN [1, 2, 3]];` Returns [2, 4, 6]. +- DuckDB allows chaining multiple function calls together using the dot (.) operator. E.g.: `SELECT 'DuckDB'.replace('Duck', 'Goose').upper(); -- Returns 'GOOSEDB';` +- DuckDB has a JSON data type. It supports selecting fields from the JSON with a JSON-Path expression using the arrow operator, -> (returns JSON) or ->> (returns text) with JSONPath expressions. For example: `SELECT data->'$.user.id' AS user_id, data->>'$.event_type' AS event_type FROM events;` +- DuckDB has built-in functions for regex regexp_matches(column, regex), regexp_replace(column, regex), and regexp_extract(column, regex). +- DuckDB has a way to quickly get a subset of your data with `SELECT * FROM large_table USING SAMPLE 10%;` + +Here is the schema of the DuckDB database that the SQL query will run on: +{schema} + +Question: +Here is the question or an instruction the user provided: +{question} + +Write a DuckDB SQL query for the given question!<|end|> +<|assistant|> +```sql +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterGPTmini(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """Schema: +```sql +{schema} +``` + +Question: +{question} + +Write a valid DuckDB SQL query to answer the question! +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstFormatterPhiAzure(RajkumarFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """Your task is to generate valid DuckDB SQL to answer the question that the user asks. You should only respond with a valid DuckDB SQL query. + +Here is the schema of the DuckDB database that the SQL query will run on: +{schema} + +Question: +Here is the question or an instruction the user provided: +{question} + +Write a DuckDB SQL query for the given question! +""" + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n### Documentation:\n{context_str}\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: str, + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + instruction = cls.PROMPT_TEMPLATE.format( + schema=table_text, + question=instruction + ) + return instruction + +class DuckDBInstNoShorthandFormatter(DuckDBInstFormatter): + """DuckDB Inst class.""" + + PROMPT_TEMPLATE = """### Instruction:\n{instruction}\n\n### Input:\n{input}{context}\n### Question:\n{question}\n\n### Response:\n""" + INSTRUCTION_TEMPLATE = """Your task is to generate valid duckdb SQL to answer the following question{has_schema}""" # noqa: E501 + + +class DuckDBChat: + """DuckDB Inst class.""" + + table_sep: str = "\n\n" + shuffle_table_order: bool = True + _cache: dict[tuple[str, str, str], list[str]] = {} + clean_whitespace = False + model = None + + @classmethod + def format_table(cls, table: Table) -> str: + """Get table format.""" + table_fmt = [] + for col in table.columns or []: + # This is technically an incorrect type, but it should be a catchall word + table_fmt.append(f" {col.name} {col.dtype or 'any'}") + if table_fmt: + all_cols = ",\n".join(table_fmt) + create_tbl = f"CREATE TABLE {table.name} (\n{all_cols}\n)" + else: + create_tbl = f"CREATE TABLE {table.name}" + return create_tbl + + @classmethod + def format_all_tables(cls, tables: list[Table], instruction: str) -> list[dict]: + """Get all tables format.""" + if not cls.model: + cls.model = Manifest( + engine="gpt-3.5-turbo", + client_name="openaichat", + cache_name="sqlite", + cache_connection=".manifest.sqlite", + ) + table_texts = [cls.format_table(table) for table in tables] + full_schema = cls.table_sep.join(table_texts) + prompt = f"""SQL schema of my database: +{full_schema} +Explain in a few sentences what the data is about: + """ + messages = [ + { + "role": "system", + "content": "You are a helpful assistant that can generate an human redable summary of database content based on the schema.", + }, + {"role": "user", "content": prompt}, + ] + explanation = cls.model.run(messages, temperature=0) + messages.append({"role": "assistant", "content": explanation}) + return messages[1:] + + @classmethod + def format_retrieved_context( + cls, + context: list[str], + ) -> str: + """Format retrieved context.""" + context_str = "\n--------\n".join(context) + return f"\n\nHere is additional documentation about DuckDB that could be useful.\n--------\n{context_str}\n--------\n" + + @classmethod + def format_prompt( + cls, + instruction: str, + table_text: list[dict], + context_text: str, + ) -> str | list[str]: + """Get prompt format.""" + prompt = f"""Now output a single SQL query without any explanation and do not add anything +to the query that was not part of the question, also do not use markdown. Make sure to only +use information provided in the prompt, or tables and columns from the schema above and write a query to answer the question.{context_text}\n\nMy quesiton is \n`{instruction}`\n\nGenerate the DuckDB specific SQL query:""" # noqa: E501 + messages = [ + { + "role": "system", + "content": "You are a helpful assistant that can generate DuckDB sql queries, which is a superset of Postgresql, based on the user input. You do not respond with any human readable text, only SQL code.", + }, + *table_text, + {"role": "user", "content": prompt}, + ] + return messages + + @classmethod + def format_model_output(cls, output_sql: str, prompt: str) -> str: + """Format model output.""" + return output_sql + + @classmethod + def format_gold_output(cls, output_sql: str) -> str: + """Format gold output for demonstration.""" + return output_sql diff --git a/duckdb-nsql/eval/schema.py b/duckdb-nsql/eval/schema.py new file mode 100644 index 0000000000000000000000000000000000000000..2b7205ae014065e27d696500565fce18bb58496f --- /dev/null +++ b/duckdb-nsql/eval/schema.py @@ -0,0 +1,115 @@ +"""Text2SQL schemas.""" +import enum + +from manifest.response import Usage +from pydantic import BaseModel + +DEFAULT_TABLE_NAME: str = "db_table" + + +class Dialect(str, enum.Enum): + """SQGFluff and SQLGlot dialects. + + Lucky for us, the dialects match both parsers. + + Ref: https://github.com/sqlfluff/sqlfluff/blob/main/src/sqlfluff/core/dialects/__init__.py # noqa: E501 + Ref: https://github.com/tobymao/sqlglot/blob/main/sqlglot/dialects/__init__.py # noqa: E501 + """ + + SNOWFLAKE = "snowflake" + BIGQUERY = "bigquery" + REDSHIFT = "redshift" + POSTGRES = "postgres" + UNKNOWN = "unknown" + + @property + def dialect_str(self) -> str | None: + """Get the dialect string for validation. + + We need to pass in dialect = None for UNKNOWN dialects. + """ + if self != Dialect.UNKNOWN: + return self.value + else: + return None + + @property + def quote_str(self) -> str: + """Get the quote string for the dialect.""" + if self == Dialect.SNOWFLAKE: + return '"' + elif self == Dialect.BIGQUERY: + return "`" + elif self == Dialect.REDSHIFT: + return '"' + elif self == Dialect.POSTGRES: + return '"' + elif self == Dialect.UNKNOWN: + return '"' + raise NotImplementedError(f"Quote string not implemented for dialect {self}") + + def quote(self, string: str) -> str: + """Quote a string.""" + return f"{self.quote_str}{string}{self.quote_str}" + + +class ColumnOrLiteral(BaseModel): + """Column that may or may not be a literal.""" + + name: str | None = None + literal: bool = False + + def __hash__(self) -> int: + """Hash.""" + return hash((self.name, self.literal)) + + +class TableColumn(BaseModel): + """Table column.""" + + name: str + dtype: str | None + + +class ForeignKey(BaseModel): + """Foreign key.""" + + # Referenced column + column: TableColumn + # References table name + references_name: str + # References column + references_column: TableColumn + + +class Table(BaseModel): + """Table.""" + + name: str | None + columns: list[TableColumn] | None + pks: list[TableColumn] | None + # FK from this table to another column in another table + fks: list[ForeignKey] | None + examples: list[dict] | None + # Is the table a source or intermediate reference table + is_reference_table: bool = False + + +class TextToSQLParams(BaseModel): + """A text to sql request.""" + + instruction: str + database: str | None + # Default to unknown + dialect: Dialect = Dialect.UNKNOWN + tables: list[Table] | None + + +class TextToSQLModelResponse(BaseModel): + """Model for Autocomplete Responses.""" + + output: str + final_prompt: str | list[dict] + raw_output: str + usage: Usage + metadata: str | None = None diff --git a/duckdb-nsql/eval/text_to_sql.py b/duckdb-nsql/eval/text_to_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..5a4f2bba611e1899a25882a15acf5cf9f47207a9 --- /dev/null +++ b/duckdb-nsql/eval/text_to_sql.py @@ -0,0 +1,260 @@ +"""Text-to-SQL running.""" +import asyncio +import json +import re +import time +from typing import cast +import duckdb + +import structlog +from manifest import Manifest +from manifest.response import Response, Usage +from prompt_formatters import RajkumarFormatter, MotherDuckFormatter +from schema import DEFAULT_TABLE_NAME, TextToSQLModelResponse, TextToSQLParams +from tqdm.auto import tqdm + +logger = structlog.get_logger() + + +def clean_whitespace(sql: str) -> str: + """Clean whitespace.""" + return re.sub(r"[\t\n\s]+", " ", sql) + + +def instruction_to_sql( + params: TextToSQLParams, + extra_context: list[str], + manifest: Manifest, + prompt_formatter: RajkumarFormatter = None, + overwrite_manifest: bool = False, + max_tokens: int = 300, + temperature: float = 0.1, + stop_sequences: list[str] | None = None, + num_beams: int = 1, +) -> TextToSQLModelResponse: + """Parse the instruction to a sql command.""" + return instruction_to_sql_list( + params=[params], + extra_context=[extra_context], + manifest=manifest, + prompt_formatter=prompt_formatter, + overwrite_manifest=overwrite_manifest, + max_tokens=max_tokens, + temperature=0.1, + stop_sequences=stop_sequences, + num_beams=num_beams, + )[0] + +def run_motherduck_prompt_sql(params: list[TextToSQLParams]) -> list[TextToSQLModelResponse]: + results = [] + for param in params: + con = duckdb.connect('md:') + try: + sql_query = con.execute("CALL prompt_sql(?);", [param.instruction]).fetchall()[0][0] + except Exception as e: + print(e) + sql_query = "SELECT * FROM hn.hacker_news LIMIT 1"; + usage = Usage( + completion_tokens = 0, + prompt_tokens = 0, + total_tokens = 0 + ) + model_response = TextToSQLModelResponse( + output=sql_query, + raw_output=sql_query, + final_prompt=param.instruction, + usage=usage, + ) + results.append(model_response) + return results + + + +def instruction_to_sql_list( + params: list[TextToSQLParams], + extra_context: list[list[str]], + manifest: Manifest, + prompt_formatter: RajkumarFormatter = None, + overwrite_manifest: bool = False, + max_tokens: int = 300, + temperature: float = 0.1, + stop_sequences: list[str] | None = None, + num_beams: int = 1, + verbose: bool = False, +) -> list[TextToSQLModelResponse]: + """Parse the list of instructions to sql commands. + + Connector is used for default retry handlers only. + """ + if type(prompt_formatter) is MotherDuckFormatter: + return run_motherduck_prompt_sql(params) + + if prompt_formatter is None: + raise ValueError("Prompt formatter is required.") + + def construct_params( + params: TextToSQLParams, + context: list[str], + ) -> str | list[dict]: + """Turn params into prompt.""" + if prompt_formatter.clean_whitespace: + instruction = clean_whitespace(params.instruction) + else: + instruction = params.instruction + + table_texts = prompt_formatter.format_all_tables( + params.tables, instruction=instruction + ) + # table_texts can be list of chat messages. Only join list of str. + if table_texts: + if isinstance(table_texts[0], str): + table_text = prompt_formatter.table_sep.join(table_texts) + else: + table_text = table_texts + else: + table_text = "" + + if context: + context_text = prompt_formatter.format_retrieved_context(context) + else: + context_text = "" if isinstance(table_text, str) else [] + prompt = prompt_formatter.format_prompt( + instruction, + table_text, + context_text, + ) + return prompt + + # If no inputs, return nothing + if not params: + return [] + + # Stitch together demonstrations and params + prompts: list[str | list[dict]] = [] + for i, param in tqdm( + enumerate(params), + total=len(params), + desc="Constructing prompts", + disable=not verbose, + ): + predict_str = construct_params(param, extra_context[i] if extra_context else []) + if isinstance(predict_str, str): + prompt = predict_str.lstrip() + else: + prompt = predict_str + prompts.append(prompt) + + manifest_params = dict( + max_tokens=max_tokens, + overwrite_cache=overwrite_manifest, + num_beams=num_beams, + logprobs=5, + temperature=0.1, + do_sample=False if 0.1 <= 0 else True, + stop_sequences=stop_sequences or prompt_formatter.stop_sequences, + ) + + ret: list[TextToSQLModelResponse] = [] + if len(params) == 1: + prompt = prompts[0] + success = False + retries = 0 + while not success and retries < 5: + try: + model_response = _run_manifest( + prompt, + manifest_params, + prompt_formatter, + manifest, + stop_sequences=stop_sequences, + ) + success = True + except: + retries +=1 + + usage = model_response.usage + model_response.usage = usage + ret.append(model_response) + else: + # We do not handle retry logic on parallel requests right now + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + response = cast( + Response, + loop.run_until_complete( + manifest.arun_batch( + prompts, + **manifest_params, # type: ignore + ), + ), + ) + loop.close() + + response_usage = response.get_usage() + response_text = response.get_parsed_response() + for prompt, resp in zip(prompts, response_text): + # This will restitch the query in the case we force it to start with SELECT + sql_query = prompt_formatter.format_model_output(cast(str, resp), prompt) + for token in stop_sequences: + sql_query = sql_query.split(token)[0] + logger.info(f"FINAL OUTPUT: {sql_query}") + ret.append( + TextToSQLModelResponse( + output=sql_query, + raw_output=cast(str, resp), + final_prompt=prompt, + usage=response_usage, + ) + ) + + return ret + + +def _run_manifest( + prompt: str | list[str], + manifest_params: dict, + prompt_formatter: RajkumarFormatter, + manifest: Manifest, + stop_sequences: list[str] | None = None, +) -> TextToSQLModelResponse: + """Run manifest for prompt format.""" + logger.info(f"PARAMS: {manifest_params}") + if isinstance(prompt, list): + for p in prompt: + logger.info(f"PROMPT: {p['role']}: {p['content']}") + else: + logger.info(f"PROMPT: {prompt}") + start_time = time.time() + # Run result + response = cast( + Response, + manifest.run( + prompt, + return_response=True, + client_timeout=1800, + **manifest_params, # type: ignore + ), + ) + logger.info(f"TIME: {time.time() - start_time: .2f}") + + response_usage = response.get_usage_obj() + summed_usage = Usage() + for usage in response_usage.usages: + summed_usage.completion_tokens += usage.completion_tokens + summed_usage.prompt_tokens += usage.prompt_tokens + summed_usage.total_tokens += usage.total_tokens + # This will restitch the query in the case we force it to start with SELECT + sql_query = prompt_formatter.format_model_output( + cast(str, response.get_response()), prompt + ) + + for token in stop_sequences: + sql_query = sql_query.split(token)[0] + logger.info(f"OUTPUT: {sql_query}") + model_response = TextToSQLModelResponse( + output=sql_query, + raw_output=cast(str, response.get_response()), + final_prompt=prompt, + usage=summed_usage, + ) + return model_response diff --git a/duckdb-nsql/manifest/.flake8 b/duckdb-nsql/manifest/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..9d8d9eb86cff004cab58df74f1f45eb5cee7ae3c --- /dev/null +++ b/duckdb-nsql/manifest/.flake8 @@ -0,0 +1,11 @@ +# This is our code-style check. We currently allow the following exceptions: +# - E731: do not assign a lambda expression, use a def +# - E402: module level import not at top of file +# - W503: line break before binary operator +# - E203: whitespace before : + +[flake8] +exclude = .git +max-line-length = 88 +ignore = E731, E402, W503, E203, PAI100, PAI101, PAI201, PAI202, PAI203 +per-file-ignores = __init__.py:F401, version.py:D100 diff --git a/duckdb-nsql/manifest/.pre-commit-config.yaml b/duckdb-nsql/manifest/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8648b07f99d277575033105a5230d44e4916cc1d --- /dev/null +++ b/duckdb-nsql/manifest/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: check-added-large-files + - repo: https://github.com/timothycrosley/isort + rev: 5.13.2 + hooks: + - id: isort + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + language_version: python3 + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 diff --git a/duckdb-nsql/manifest/CHANGELOG.rst b/duckdb-nsql/manifest/CHANGELOG.rst new file mode 100644 index 0000000000000000000000000000000000000000..9011c7ac20c1b2e073ae0ba5659e04900fee451b --- /dev/null +++ b/duckdb-nsql/manifest/CHANGELOG.rst @@ -0,0 +1,93 @@ +0.1.10 - Unreleased +--------------------- + +0.1.9 - 2024-01-22 +--------------------- +Fixed +^^^^^ +* Added trust code params HF models +* Added LRU cache to HF model param calls to avoid extra calls +* Fixed pydantic type issue HF model return +* Support for Python 3.10-3.11 + +0.1.8 - 2023-05-22 +--------------------- +Added +^^^^^ +* Azure model support (completion and chat) +* Google Vertex API model support (completion and chat) +* Streaming responses for LM Completions (set stream=True) + +Fixed +^^^^^ +* `run` with batches now acts the same as async run except not async. We will batch requests into appropriate batchs sizes. +* Refactored client so unified preprocess and postprocess of requests and responses to better support model variants in request/response format. + +0.1.7 - 2023-05-17 +--------------------- +Fixed +^^^^^ +* `_run_chat` fixed bug where not passing in kwargs + +0.1.6 - 2023-05-16 +--------------------- +Fixed +^^^^^ +* Unified `run` and `run_chat` methods so it's just `run` now. +* LLama HF models for eval + +0.1.5 - 2023-05-03 +--------------------- +Added +^^^^^ +* Added chat input for chat models. + +0.1.4 - 2023-04-24 +--------------------- +Added +^^^^^ +* Connection pools to swap between clients +* Chunksize param for async runs + +Fixed +^^^^^ +* Determine cache and response by request type, not client name +* Refactor Response to use Pydantic types for Request and Response + +0.1.1 +--------------------- +Added +^^^^^ +* Async support in arun_batch + +Fixed +^^^^^ +* Batched runs now caches individual items +* Score prompt does not truncate outside token + +Removed +^^^^^ +* Deprecated chatGPT in favor of openaichat which uses OpenAI completions +* Deprecated Sessions + +0.1.0 - 2022-01-31 +--------------------- +Added +^^^^^ +* Batched inference support in `manifest.run`. No more separate `manifest.run_batch` method. +* Standard request base model for all language inputs. +* ChatGPT client. Requires CHATGPT_SESSION_KEY to be passed in. +* Diffusion model support +* Together model support + +Removed +^^^^^^^ +* `Prompt` class +* `OPT` client - OPT is now available in HuggingFace + +0.0.1 - 2022-11-08 +------------------- +First major pip release of Manifest. Install via `pip install manifest-ml`. + + +.. _@lorr1: https://github.com/lorr1 diff --git a/duckdb-nsql/manifest/LICENSE b/duckdb-nsql/manifest/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/duckdb-nsql/manifest/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/duckdb-nsql/manifest/Makefile b/duckdb-nsql/manifest/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6aaf992cc8322efd088cf27bca469c3c8d1ec331 --- /dev/null +++ b/duckdb-nsql/manifest/Makefile @@ -0,0 +1,27 @@ +dev: + pip install -e .[all] + pre-commit install + +test: dev check + pytest tests + +format: + isort --atomic manifest/ tests/ web_app/ + black manifest/ tests/ web_app/ + +check: + isort -c manifest/ tests/ web_app/ + black manifest/ tests/ web_app/ --check + flake8 manifest/ tests/ web_app/ + mypy manifest/ tests/ web_app/ + +clean: + pip uninstall -y manifest + rm -rf src/manifest.egg-info + rm -rf build/ dist/ + +prune: + @bash -c "git fetch -p"; + @bash -c "for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -d $branch; done"; + +.PHONY: dev test clean check prune diff --git a/duckdb-nsql/manifest/README.md b/duckdb-nsql/manifest/README.md new file mode 100644 index 0000000000000000000000000000000000000000..175d38fd59882c87304a27147e65032e4e0f63be --- /dev/null +++ b/duckdb-nsql/manifest/README.md @@ -0,0 +1,304 @@ +# Manifest +How to make prompt programming with Foundation Models a little easier. + + +# Table of Contents +- [Install](#install) +- [Getting Started](#getting-started) +- [Manifest](#manifest-components) +- [Other Models Types](#other-models) + - [Local HuggingFace Models](#local-huggingface-models) + - [Chat Models](#chat-models) + - [Embedding Models](#embedding-models) +- [Road Map](#road-map) +- [Development](#development) +- [Cite](#cite) + + +# Install +Install: +```bash +pip install manifest-ml +``` + +Install with diffusion support: +```bash +pip install manifest-ml[diffusers] +``` + +Install with HuggingFace local model support: +```bash +pip install manifest-ml[api] +``` + +Dev Install: +```bash +git clone git@github.com:HazyResearch/manifest.git +cd manifest +make dev +``` + +# Getting Started +Running is simple to get started. If using OpenAI, set `export OPENAI_API_KEY=` (or pass key in through variable `client_connection`) then run + +```python +from manifest import Manifest + +# Start a manifest session to OpenAI - default `engine=text-davinci-003` +manifest = Manifest( + client_name = "openai", +) +manifest.run("Why is the grass green?") +``` + +## Examples +We have example notebook and python scripts located at [examples](examples). These show how to use different models, model types (i.e. text, diffusers, or embedding models), and async running. + +# Manifest Components +Manifest is meant to be a very light weight package to help with prompt design and iteration. Three key design decisions of Manifest are + +* All models are behind APIs +* Supports caching of model inputs/outputs for iteration, reproducibility, and cost saving +* Unified API to support generate, score, and embed + +## Models +Manifest provides model clients for [OpenAI](https://openai.com/), [AI21](https://studio.ai21.com/), [Cohere](https://cohere.ai/), [Together](https://together.xyz/), and HuggingFace (see [below](#huggingface-models) for how to use locally hosted HuggingFace models). You can toggle between the models by changing `client_name` and `client_connection`. For example, if a HuggingFace model is loaded locally, run +```python +manifest = Manifest( + client_name = "huggingface", + client_connection = "http://127.0.0.1:5000", +) +``` +If you want to use Cohere, run +```python +manifest = Manifest( + client_name = "cohere", + client_connection = , +) +``` +You can also just set `export COHERE_API_KEY=` and not use `client_connection`. + +If you want to use AI21 Labs, run +```python +manifest = Manifest( + client_name = "ai21", + client_connection = , +) +``` + +You can see the model details and possible model inputs to `run()` via +```python +print(manifest.client_pool.get_current_client().get_model_params()) +print(manifest.client_pool.get_current_client().get_model_inputs()) +``` + +## Global Cache +We support having queries and results stored in a global cache that can be shared across users. We treat inputs and outputs as key value pairs and support SQLite or Redis backends. To start with global caching using SQLite, run + +```python +manifest = Manifest( + client_name = "openai", + cache_name = "sqlite", + cache_connection = "mycache.sqlite", +) +``` +The cache will be saved in `mycache.sqlite`. + +We also support Redis backend. +```python +manifest = Manifest( + client_name = "openai", + cache_name = "redis", + cache_connection = "localhost:6379" +) +``` +As a hint, if you want to get Redis running, see the `docker run` command below under development. + +## Running Queries +Once you have a session open, you can write and develop prompts. + +```python +result = manifest.run("Hello, my name is Laurel") +``` + +You can also run over multiple examples if supported by the client. +```python +results = manifest.run(["Where are the cats?", "Where are the dogs?"]) +``` + +We support async queries as well via +```python +import asyncio +results = asyncio.run(manifest.arun_batch(["Where are the cats?", "Where are the dogs?"])) +``` + +If something doesn't go right, you can also ask to get a raw manifest Response. +```python +result_object = manifest.run(["Where are the cats?", "Where are the dogs?"], return_response=True) +print(result_object.get_request_obj()) +print(result_object.is_cached()) +print(result_object.get_response_obj()) +``` + +By default, we do not truncate results based on a stop token. You can change this by either passing a new stop token to a Manifest session or to a `run`. +```python +result = manifest.run(prompt, "Laurel", stop_token="and") +``` + +If you want to change default parameters to a model, we pass those as `kwargs` to the client. +```python +result = manifest.run(prompt, "Laurel", max_tokens=50) +``` + +## Streaming Queries +Manifest also supports streaming the model response back, assuming it's supported by the underlying client. When calling `run`, pass `stream=True` to get a streaming iterator in response. + +```python +result_iterator = manifest.run("Tell me a story. Once upon a time", max_tokens=100, stream=True) +for res_text in result_iterator: + print(res_text) +``` +Streaming responses are only supported for single string queries (not batch mode) for text completion models. + +## Model Pools +Manifest supports querying multiple models with different schedulers. This is very much a work in progress effort, but Manifest will round robin select (or randomly select) the clients you want. You can use the same client multiple times with different connection strings (e.g. different API keys), or you can mix and match. The only requirement is that all clients are the same request type. I.e. you can't have a pool of generation models and embedding models. + +To query between a local model and OpenAI, +```python +from manifest.connections.client_pool import ClientConnection +from manifest import Manifest + +client_connection1 = ClientConnection( + client_name="huggingface", + client_connection="http://127.0.0.1:5000", +) +client_connection2 = ClientConnection(client_name="openai", engine="text-ada-001") +manifest = Manifest( + client_pool=[client_connection1, client_connection2], + cache_name="sqlite", + client_connection=sqlite_cache, +) +manifest.run(...) +``` + +The speed benefit comes in with async batched runs. When calling `arun_batch` with a list of prompts, Manifest supports a `chunk_size` param. This will break the prompts into `chunk_size` chunks to spread across the client pool. By default `chunk_size` is `-1` which means only one client will get all the prompts to run asynchronously. You must set `chunk_size > 1` to distribute across the pool. There is a further `batch_size` param which control the individual client `batch_size` to send to the model. + +```python +responses = asyncio.run(manifest.arun_batch(prompts, max_tokens=30, chunk_size=20)) +``` + +# Other Models + +## Local Huggingface Models +To use a HuggingFace generative model, in `manifest/api` we have a Flask application that hosts the models for you. + +In a separate terminal or Tmux/Screen session, to load 6B parameters models, run +```bash +python3 -m manifest.api.app \ + --model_type huggingface \ + --model_name_or_path EleutherAI/gpt-j-6B \ + --device 0 +``` +You will see the Flask session start and output a URL `http://127.0.0.1:5000`. Pass this in to Manifest. If you want to use a different port, set the `FLASK_PORT` environment variable. + +```python +manifest = Manifest( + client_name = "huggingface", + client_connection = "http://127.0.0.1:5000", +) +``` + +If you have a custom model you trained, pass the model path to `--model_name_or_path`. + +To help load larger models, we also support using `parallelize()` from HF, [accelerate](https://huggingface.co/docs/accelerate/index), [bitsandbytes](https://github.com/TimDettmers/bitsandbytes), and [deepspeed](https://github.com/microsoft/DeepSpeed). You will need to install these packages first via `pip install manifest-ml[api]`. We list the commands to load larger models below. + +* T0pp +```bash +python3 -m manifest.api.app \ + --model_type huggingface \ + --model_name_or_path bigscience/T0pp \ + --use_hf_parallelize +``` + +* NeoX 20B (requires at least 60GB of GPU memory) +```bash +python3 -m manifest.api.app \ + --model_type huggingface \ + --model_name_or_path EleutherAI/gpt-neox-20b \ + --use_accelerate_multigpu \ + --percent_max_gpu_mem_reduction 0.75 +``` +* Bloom 175B (requires at least 240GB of GPU memory) +```bash +python3 -m manifest.api.app \ + --model_type huggingface \ + --model_name_or_path bigscience/bloom \ + --use_bitsandbytes \ + --percent_max_gpu_mem_reduction 0.85 +``` + +## Chat Models +Manifest has specific support for executing against chat models in the more standard "system" / "user" dialogue. To pass in a dialogue history to Manifest, use the `run` command with a list of dictionary inputs with `role` and `content` keys using an associated chat model such as `openaichat`. + +```python +manifest = Manifest(client_name="openaichat") +dialogue = [ + {"role": "system", "content": "You are a helpful assistant who also responds in rhymes"}, + {"role": "user", "content": "What is the date?"}, +] +res = manifest.run(dialogue, max_tokens=100) +``` + +## Embedding Models +Manifest also supports getting embeddings from models and available APIs. We do this all through changing the `client_name` argument. You still use `run` and `abatch_run`. + +To use OpenAI's embedding models, simply run +```python +manifest = Manifest(client_name="openaiembedding") +embedding_as_np = manifest.run("Get me an embedding for a bunny") +``` + +As explained above, you can load local HuggingFace models that give you embeddings, too. If you want to use a standard generative model, load the model as above use use `client_name="huggingfaceembedding"`. If you want to use a standard embedding model, like those from SentenceTransformers, load your local model via +```bash +python3 -m manifest.api.app \ + --model_type sentence_transformers \ + --model_name_or_path all-mpnet-base-v2 \ + --device 0 +``` + +# Road Map +Here's what's coming up next +- [ ] Clients + - [ ] HuggingFace Hub + - [x] Azure OpenAI + - [x] Google Vertex + - [ ] Anthropic + - [x] Streaming Support Completions + - [ ] Streaming Support Chat Models +- [ ] Data Types + - [ ] Diffusion Models +- [x] Orchestration + - [x] Connection pools +- [ ] Local Inference + - [ ] FlexGen + +# Development +Before submitting a PR, run +```bash +export REDIS_PORT="6379" # or whatever PORT local redis is running for those tests +cd +docker run -d -p 127.0.0.1:${REDIS_PORT}:6379 -v `pwd`:`pwd` -w `pwd` --name manifest_redis_test redis +make test +``` + +# Cite +Please cite Manifest if you used it for any publications. Thanks!! +``` +@misc{orr2022manifest, + author = {Orr, Laurel}, + title = {Manifest}, + year = {2022}, + publisher = {GitHub}, + howpublished = {\url{https://github.com/HazyResearch/manifest}}, +} +``` diff --git a/duckdb-nsql/manifest/examples/langchain_chatgpt.ipynb b/duckdb-nsql/manifest/examples/langchain_chatgpt.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3b4b0c6042405213bf473d0936e394a73b40063d --- /dev/null +++ b/duckdb-nsql/manifest/examples/langchain_chatgpt.ipynb @@ -0,0 +1,455 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "b253f4d5", + "metadata": {}, + "source": [ + "# ChatGPT Clone using TOMA GPT-JT-6B\n", + "(adopted from ChatGPT Clone [notebook](https://github.com/hwchase17/langchain/blob/master/docs/examples/chains/chatgpt_clone.ipynb))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b0302886", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: TOMA_URL=https://staging.together.xyz/api\n" + ] + } + ], + "source": [ + "%env TOMA_URL=https://staging.together.xyz/api" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "93a18ea6", + "metadata": {}, + "source": [ + "Make sure you have langchain installed and manifest. For the most recent versions, run\n", + "```\n", + "pip install git+https://github.com/hwchase17/langchain.git\n", + "pip install git+https://github.com/HazyResearch/manifest.git\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "a99acd89", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "\n", + "Input: Classes are \"positive\" and \"negative\". For example given\n", + "Input: I love this product!\n", + "Output: positive.\n", + "I think this movie was one of the worst of the year. Script was boring!\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "negative.\n" + ] + } + ], + "source": [ + "from manifest import Manifest\n", + "from langchain.llms.manifest import ManifestWrapper\n", + "from langchain import ConversationChain, LLMChain, PromptTemplate\n", + "from langchain.chains.conversation.memory import ConversationalBufferWindowMemory\n", + "\n", + "\n", + "template = \"\"\"I am a classification model. It will try to classify your input.\n", + "\n", + "{history}\n", + "Input: {human_input}\n", + "Output:\"\"\"\n", + "\n", + "prompt = PromptTemplate(\n", + " input_variables=[\"history\", \"human_input\"], \n", + " template=template\n", + ")\n", + "\n", + "manifest = Manifest(\n", + " client_name=\"toma\",\n", + " engine=\"Together-gpt-JT-6B-v1\",\n", + " max_tokens=150,\n", + " top_p=0.9,\n", + " top_k=40,\n", + " stop_sequences=[\"\\n\"],\n", + ")\n", + "\n", + "chatgpt_chain = LLMChain(\n", + " llm=ManifestWrapper(client=manifest), \n", + " prompt=prompt, \n", + " verbose=True, \n", + " memory=ConversationalBufferWindowMemory(k=8),\n", + ")\n", + "\n", + "output = chatgpt_chain.predict(human_input=\"Classes are \\\"positive\\\" and \\\"negative\\\". For example given\\nInput: I love this product!\\nOutput: positive.\\nI think this movie was one of the worst of the year. Script was boring!\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "4ef711d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are \"positive\" and \"negative\". For example given\n", + "Input: I love this product!\n", + "Output: positive.\n", + "I think this movie was one of the worst of the year. Script was boring!\n", + "AI: negative.\n", + "Input: So awesome! I wish I could have gone\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "positive.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"So awesome! I wish I could have gone\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "a5d6dac2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are \"positive\" and \"negative\". For example given\n", + "Input: I love this product!\n", + "Output: positive.\n", + "I think this movie was one of the worst of the year. Script was boring!\n", + "AI: negative.\n", + "Human: So awesome! I wish I could have gone\n", + "AI: positive.\n", + "Input: Hate it.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "negative.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"Hate it.\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "b9283077", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "\n", + "Input: Classes are fruits \"apple\", \"banana\", \"orange\", \"pear\". For example given\n", + "Input: This fruit rippens off of the tree.\n", + "Output: banana.\n", + "Often comes in bosc and bartlett varieties.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "apple.\n" + ] + } + ], + "source": [ + "chatgpt_chain.memory.clear()\n", + "output = chatgpt_chain.predict(human_input=\"Classes are fruits \\\"apple\\\", \\\"banana\\\", \\\"orange\\\", \\\"pear\\\". For example given\\nInput: This fruit rippens off of the tree.\\nOutput: banana.\\nOften comes in bosc and bartlett varieties.\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "cd0a23d9", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are fruits \"apple\", \"banana\", \"orange\", \"pear\". For example given\n", + "Input: This fruit rippens off of the tree.\n", + "Output: banana.\n", + "Often comes in bosc and bartlett varieties.\n", + "AI: apple.\n", + "Input: Often associated with monkeys\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "banana.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"Often associated with monkeys\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "90db6eb2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are fruits \"apple\", \"banana\", \"orange\", \"pear\". For example given\n", + "Input: This fruit rippens off of the tree.\n", + "Output: banana.\n", + "Often comes in bosc and bartlett varieties.\n", + "AI: apple.\n", + "Human: Often associated with monkeys\n", + "AI: banana.\n", + "Input: Is the color red and often delicious.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "apple.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"Is the color red and often delicious.\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "c3806f89", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "\n", + "Input: Classes are colors \"red\", \"green\", \"blue\", \"yellow\". For example given\n", + "Input: The color of a school bus.\n", + "Output: yellow.\n", + "Is the color of the sky\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "blue.\n" + ] + } + ], + "source": [ + "chatgpt_chain.memory.clear()\n", + "output = chatgpt_chain.predict(human_input=\"Classes are colors \\\"red\\\", \\\"green\\\", \\\"blue\\\", \\\"yellow\\\". For example given\\nInput: The color of a school bus.\\nOutput: yellow.\\nIs the color of the sky\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "f508f597", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are colors \"red\", \"green\", \"blue\", \"yellow\". For example given\n", + "Input: The color of a school bus.\n", + "Output: yellow.\n", + "Is the color of the sky\n", + "AI: blue.\n", + "Input: Color of a banana.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "yellow.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"Color of a banana.\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "cbd607f4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are colors \"red\", \"green\", \"blue\", \"yellow\". For example given\n", + "Input: The color of a school bus.\n", + "Output: yellow.\n", + "Is the color of the sky\n", + "AI: blue.\n", + "Human: Color of a banana.\n", + "AI: yellow.\n", + "Input: When someone is sick they are this color.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "green.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"When someone is sick they are this color.\")\n", + "print(output)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "d33e0e28", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n", + "Prompt after formatting:\n", + "\u001b[32;1m\u001b[1;3mI am a classification model. It will try to classify your input.\n", + "\n", + "Human: Classes are colors \"red\", \"green\", \"blue\", \"yellow\". For example given\n", + "Input: The color of a school bus.\n", + "Output: yellow.\n", + "Is the color of the sky\n", + "AI: blue.\n", + "Human: Color of a banana.\n", + "AI: yellow.\n", + "Human: When someone is sick they are this color.\n", + "AI: green.\n", + "Input: Color of anger.\n", + "Output:\u001b[0m\n", + "\n", + "\u001b[1m> Finished LLMChain chain.\u001b[0m\n", + "red.\n" + ] + } + ], + "source": [ + "output = chatgpt_chain.predict(human_input=\"Color of anger.\")\n", + "print(output)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "bootleg", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12 | packaged by conda-forge | (default, Jan 30 2022, 23:36:06) \n[Clang 11.1.0 ]" + }, + "vscode": { + "interpreter": { + "hash": "7a3f97ab0465937066e9b79893b779dfc8a12d73c41f9d98a7bf05133c798250" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/duckdb-nsql/manifest/examples/manifest_async.py b/duckdb-nsql/manifest/examples/manifest_async.py new file mode 100644 index 0000000000000000000000000000000000000000..e252c7357e73f39d2424ed552adb2d2c3a5687f8 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_async.py @@ -0,0 +1,27 @@ +import asyncio +import time + +from manifest import Manifest + + +def main(): + + manifest = Manifest( + client_name="openaichat", + ) + + print("Running in serial") + prompts = [f"Tell me something interesting about {i}" for i in range(50)] + st = time.time() + for pmt in prompts: + _ = manifest.run(pmt) + print(f"For loop: {time.time() - st :.2f}") + + print("Running with async") + st = time.time() + _ = asyncio.run(manifest.arun_batch(prompts, max_tokens=30)) + print(f"Async loop: {time.time() - st :.2f}") + + +if __name__ == "__main__": + main() diff --git a/duckdb-nsql/manifest/examples/manifest_azure.ipynb b/duckdb-nsql/manifest/examples/manifest_azure.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..e20b698fe4f844dee2648573eee80b096237ceed --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_azure.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "AZURE_KEY = \"API_KEY::URL\"\n", + "OPENAI_KEY = \"sk-XXX\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use Azure and OpenAI models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "from pathlib import Path\n", + "\n", + "cache_path = Path(\"manifest.db\")\n", + "if cache_path.exists():\n", + " cache_path.unlink()\n", + "\n", + "\n", + "azure = ClientConnection(\n", + " client_name=\"azureopenai\",\n", + " client_connection=AZURE_KEY,\n", + " engine=\"text-davinci-003\",\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[azure], \n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"manifest.db\"\n", + ")\n", + "\n", + "\n", + "openai = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY,\n", + " engine=\"text-davinci-003\",\n", + ")\n", + "\n", + "manifest_openai_nocache = Manifest(client_pool=[openai])\n", + "\n", + "manifest_openai = Manifest(client_pool=[openai], \n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"manifest.db\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Show caches are the same\n", + "text = \"What is the meaning of life?\"\n", + "res = manifest.run(text, max_tokens=100, temperature=0.7, return_response=True)\n", + "print(res.get_response())\n", + "print(res.is_cached())\n", + "res2 = manifest_openai.run(text, max_tokens=100, temperature=0.7, return_response=True)\n", + "print(res2.is_cached())\n", + "\n", + "assert res2.get_response() == res.get_response()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "azure_chat = ClientConnection(\n", + " client_name=\"azureopenaichat\",\n", + " client_connection=AZURE_KEY,\n", + " engine=\"gpt-3.5-turbo\",\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[azure_chat])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(manifest.run(\"What do you think is the best food?\", max_tokens=100))\n", + "\n", + "chat_dict = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n", + " {\"role\": \"assistant\", \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"},\n", + " {\"role\": \"user\", \"content\": \"Where was it played?\"}\n", + "]\n", + "print(manifest.run(chat_dict, max_tokens=100))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_chatgpt.ipynb b/duckdb-nsql/manifest/examples/manifest_chatgpt.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..79b101f3a55289c259dcc1bc057acc36c7b0d3a3 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_chatgpt.ipynb @@ -0,0 +1,101 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "OPENAI_KEY = \"sk-XXX\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use ChatOpenAI\n", + "\n", + "Set you `OPENAI_API_KEY` environment variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "openai_chat = ClientConnection(\n", + " client_name=\"openaichat\",\n", + " client_connection=OPENAI_KEY,\n", + " engine=\"gpt-3.5-turbo\"\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[openai_chat])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Simple question\n", + "chat_dict = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n", + " {\"role\": \"assistant\", \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"},\n", + " {\"role\": \"user\", \"content\": \"Where was it played?\"}\n", + "]\n", + "print(manifest.run(chat_dict, max_tokens=100))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_connection_pool.ipynb b/duckdb-nsql/manifest/examples/manifest_connection_pool.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5b2b861fc79170d586aa3e71d472de964b39806e --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_connection_pool.ipynb @@ -0,0 +1,208 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "OPENAI_KEY1 = \"sk-XXX\"\n", + "OPENAI_KEY2 = \"sk-XX\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use OpenAI\n", + "\n", + "Set you `OPENAI_API_KEY` environment variable." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "openai_ada = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY1,\n", + " engine=\"text-ada-001\"\n", + ")\n", + "\n", + "openai_curie = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY2,\n", + " engine=\"text-curie-001\"\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[openai_ada, openai_curie], client_pool_schedule=\"round_robin\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "I am a model.\n", + "1\n", + "I am a MacBook Pro with a retina\n" + ] + } + ], + "source": [ + "res = manifest.run(\"What model are you?\", temperature=0.0)\n", + "print(manifest.client_pool.current_client_id)\n", + "print(res)\n", + "res = manifest.run(\"What model are you?\", temperature=0.0)\n", + "print(manifest.client_pool.current_client_id)\n", + "print(res)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## With Async" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import nest_asyncio\n", + "# This is required for asyncio.run(...) to work in Jupyter notebooks.\n", + "nest_asyncio.apply()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "openai_ada = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY1,\n", + " engine=\"text-ada-001\"\n", + ")\n", + "\n", + "openai_babbage = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY2,\n", + " engine=\"text-babbage-001\"\n", + ")\n", + "\n", + "openai_curie = ClientConnection(\n", + " client_name=\"openai\",\n", + " client_connection=OPENAI_KEY2,\n", + " engine=\"text-curie-001\"\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[openai_ada, openai_babbage, openai_curie], client_pool_schedule=\"round_robin\")\n", + "manifest_single_client = Manifest(client_pool=[openai_babbage])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For loop: 128.68\n", + "Running with async single client\n", + "Running 1 tasks across all clients.\n", + "Async loop: 4.02\n", + "Running with async two clients but not chunking\n", + "Running 1 tasks across all clients.\n", + "Async loop: 3.92\n", + "Running with async two clients and chunk size\n", + "Running 20 tasks across all clients.\n", + "Async loop: 1.44\n" + ] + } + ], + "source": [ + "import time\n", + "import asyncio\n", + "\n", + "prompts = [f\"Tell me something interesting about {i}\" for i in range(400)]\n", + "st = time.time()\n", + "for pmt in prompts:\n", + " _ = manifest_single_client.run(pmt, max_tokens=30)\n", + "print(f\"For loop: {time.time() - st :.2f}\")\n", + "\n", + "print(\"Running with async single client\")\n", + "st = time.time()\n", + "_ = asyncio.run(manifest_single_client.arun_batch(prompts, max_tokens=30, chunk_size=-1))\n", + "print(f\"Async loop: {time.time() - st :.2f}\")\n", + "\n", + "print(\"Running with async two clients but not chunking\")\n", + "st = time.time()\n", + "_ = asyncio.run(manifest.arun_batch(prompts, max_tokens=30, chunk_size=-1))\n", + "print(f\"Async loop: {time.time() - st :.2f}\")\n", + "\n", + "print(\"Running with async two clients and chunk size\")\n", + "st = time.time()\n", + "_ = asyncio.run(manifest.arun_batch(prompts, max_tokens=30, chunk_size=20))\n", + "print(f\"Async loop: {time.time() - st :.2f}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_diffusers.ipynb b/duckdb-nsql/manifest/examples/manifest_diffusers.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..56911b6e17980a23b06449947246e8688f4264e4 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_diffusers.ipynb @@ -0,0 +1,198 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Locally Hosted Huggingface LM\n", + "\n", + "Run\n", + "```\n", + "python3 manifest/api/app.py --model_type huggingface --model_name_or_path EleutherAI/gpt-neo-125M --device 0\n", + "```\n", + "in a separate `screen` or `tmux`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'model_name': 'EleutherAI/gpt-neo-125M', 'model_path': 'EleutherAI/gpt-neo-125M'}\n" + ] + } + ], + "source": [ + "from manifest import Manifest\n", + "\n", + "# Local hosted GPT Neo 125M\n", + "manifest = Manifest(\n", + " client_name=\"huggingface\",\n", + " client_connection=\"http://127.0.0.1:6001\",\n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"my_sqlite_manifest.sqlite\"\n", + ")\n", + "print(manifest.client_pool.get_current_client().get_model_params())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Locally Hosted Huggingface Diffusers\n", + "\n", + "Run\n", + "```\n", + "python3 manifest/api/app.py --model_type diffuser --model_name_or_path runwayml/stable-diffusion-v1-5 --device 0\n", + "```\n", + "in a separate `screen` or `tmux`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'model_name': 'runwayml/stable-diffusion-v1-5', 'model_path': 'runwayml/stable-diffusion-v1-5'}\n" + ] + } + ], + "source": [ + "from manifest import Manifest\n", + "\n", + "manifest_diff = Manifest(\n", + " client_name=\"diffuser\",\n", + " client_connection=\"http://127.0.0.1:6000\",\n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"my_sqlite_manifest.sqlite\"\n", + ")\n", + "print(manifest_diff.client_pool.get_current_client().get_model_params())" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "carrots, onions, radishes, and spinach.\n" + ] + } + ], + "source": [ + "ingredients = manifest.run(\"What are best vegetables for a sandwhich? The ingrediates are lettuce,\", stop_token=\"\\n\")\n", + "print(ingredients)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from PIL import Image\n", + "\n", + "res = manifest_diff.run(f\"Sandwich with {ingredients}\", client_timeout=300)\n", + "im = Image.fromarray(res)\n", + "display(im)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparing with and without a cache" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import numpy as np\n", + "\n", + "manifest = Manifest(\n", + " client_name=\"tomadiffuser\",\n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"my_sqlite_manifest.sqlite\"\n", + ")\n", + "\n", + "st = time.time()\n", + "res = manifest.run(\"Coloring book image of a horse\", overwrite_cache=True)\n", + "im = Image.fromarray(res)\n", + "display(im)\n", + "print(f\"Took {time.time() - st:.2f} seconds\")\n", + "\n", + "st = time.time()\n", + "res = manifest.run(\"Coloring book image of a horse\")\n", + "im = Image.fromarray(np.array(res))\n", + "display(im)\n", + "print(f\"Now took {time.time() - st:.2f} seconds\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_embedding.ipynb b/duckdb-nsql/manifest/examples/manifest_embedding.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6ef281123e93d5a02d4c6fbeed2b3293ce44dbe8 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_embedding.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use OpenAI\n", + "\n", + "Set you `OPENAI_API_KEY` environment variable." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'model_name': 'openaiembedding', 'engine': 'text-embedding-ada-002'}\n" + ] + } + ], + "source": [ + "from manifest import Manifest\n", + "\n", + "manifest = Manifest(client_name=\"openaiembedding\")\n", + "print(manifest.client_pool.get_next_client().get_model_params())" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1536,)\n" + ] + } + ], + "source": [ + "emb = manifest.run(\"Is this an embedding?\")\n", + "print(emb.shape)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Locally Hosted Huggingface LM\n", + "\n", + "Run\n", + "```\n", + "python3 manifest/api/app.py --model_type huggingface --model_name_or_path EleutherAI/gpt-neo-125M --device 0\n", + "```\n", + "or\n", + "```\n", + "python3 manifest/api/app.py --model_type sentence_transformers --model_name_or_path all-mpnet-base-v2 --device 0\n", + "```\n", + "\n", + "in a separate `screen` or `tmux`. Make sure to note the port. You can change this with `export FLASK_PORT=`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'model_name': 'all-mpnet-base-v2', 'model_path': 'all-mpnet-base-v2', 'client_name': 'huggingfaceembedding'}\n" + ] + } + ], + "source": [ + "from manifest import Manifest\n", + "\n", + "# Local hosted GPT Neo 125M\n", + "manifest = Manifest(\n", + " client_name=\"huggingfaceembedding\",\n", + " client_connection=\"http://127.0.0.1:6000\",\n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"my_sqlite_manifest.sqlite\"\n", + ")\n", + "print(manifest.client_pool.get_next_client().get_model_params())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(768,)\n", + "(768,) (768,)\n" + ] + } + ], + "source": [ + "emb = manifest.run(\"Is this an embedding?\")\n", + "print(emb.shape)\n", + "\n", + "emb = manifest.run([\"Is this an embedding?\", \"Bananas!!!\"])\n", + "print(emb[0].shape, emb[1].shape)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_google.ipynb b/duckdb-nsql/manifest/examples/manifest_google.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..2c66be099d455f4db5a0acb94c475c53a2a25566 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_google.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "GOOGLE_KEY = \"KEY::PROJECT_ID\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use GoogleVertexAPI" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "google_bison = ClientConnection(\n", + " client_name=\"google\",\n", + " client_connection=GOOGLE_KEY\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[google_bison])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Simple question\n", + "print(manifest.run(\"What is your name\", max_tokens=40))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "google_bison = ClientConnection(\n", + " client_name=\"googlechat\",\n", + " client_connection=GOOGLE_KEY\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[google_bison])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "chat_dict = [\n", + " # {\"author\": \"bot\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"author\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n", + " {\"author\": \"bot\", \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"},\n", + " {\"author\": \"user\", \"content\": \"Where was it played?\"}\n", + "]\n", + "print(manifest.run(chat_dict, max_tokens=8))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_openrouter.ipynb b/duckdb-nsql/manifest/examples/manifest_openrouter.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b28e658826bad78068e8f87e95a215d4bbabe062 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_openrouter.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "OPENROUTER_API_KEY = \"sk-...\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use ChatOpenAI\n", + "\n", + "Set you `OPENROUTER_API_KEY` environment variable." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "openai_chat = ClientConnection(\n", + " client_name=\"openrouter\",\n", + " client_connection=OPENROUTER_API_KEY,\n", + " engine=\"meta-llama/codellama-70b-instruct\"\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[openai_chat])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2020 World Series was played at the Globe Life Field in Arlington, Texas.\n" + ] + } + ], + "source": [ + "# Simple question\n", + "chat_dict = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n", + " {\"role\": \"assistant\", \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"},\n", + " {\"role\": \"user\", \"content\": \"Where was it played?\"}\n", + "]\n", + "print(manifest.run(chat_dict, max_tokens=100))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + }, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/duckdb-nsql/manifest/examples/manifest_streaming.ipynb b/duckdb-nsql/manifest/examples/manifest_streaming.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5f31048abfdcf1dabe0d65b4306e71a7c27e84c3 --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_streaming.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "OPENAI_KEY = \"sk-XXX\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use ChatOpenAI\n", + "\n", + "Set you `OPENAI_API_KEY` environment variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "from manifest.connections.client_pool import ClientConnection\n", + "\n", + "openai_chat = ClientConnection(\n", + " client_name=\"openaichat\",\n", + " client_connection=OPENAI_KEY,\n", + " engine=\"gpt-3.5-turbo\"\n", + ")\n", + "\n", + "manifest = Manifest(client_pool=[openai_chat])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "manifest_iterator = manifest.run(\"Tell me a story about a fat cat.\\n\\nOnce upon a time\", max_tokens=200, stream=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "cur_line_length = 0\n", + "# Iterate over stream\n", + "for res in manifest_iterator:\n", + " sys.stdout.write(res)\n", + " cur_line_length += len(res)\n", + " if cur_line_length > 80:\n", + " sys.stdout.write(\"\\n\")\n", + " cur_line_length = 0" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/examples/manifest_together.ipynb b/duckdb-nsql/manifest/examples/manifest_together.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..47f67fcba69a48542d33394445049860aa2ee95d --- /dev/null +++ b/duckdb-nsql/manifest/examples/manifest_together.ipynb @@ -0,0 +1,106 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: TOMA_URL=\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "%env TOMA_URL=" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "\n", + "# The responses are not fast\n", + "manifest = Manifest(\n", + " client_name=\"toma\",\n", + ")\n", + "\n", + "print(manifest.run(\"What is the color of an apple?\"))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With a cache" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from manifest import Manifest\n", + "\n", + "# The responses are not fast\n", + "manifest = Manifest(\n", + " client_name=\"toma\",\n", + " cache_name=\"sqlite\",\n", + " cache_connection=\"my_manifest_cache.sqlite\",\n", + ")\n", + "\n", + "res = manifest.run(\"What is the color of an apple?\", return_response=True)\n", + "print(res.get_response())\n", + "print(\"Is Cached?\", res.is_cached())\n", + "\n", + "res = manifest.run(\"What is the color of an apple?\", return_response=True)\n", + "print(res.get_response())\n", + "print(\"Is Cached?\", res.is_cached())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "manifest", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "fddffe4ac3b9f00470127629076101c1b5f38ecb1e7358b567d19305425e9491" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/duckdb-nsql/manifest/manifest/__init__.py b/duckdb-nsql/manifest/manifest/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..00484be445ed14f7fb912786195d431edaa13dac --- /dev/null +++ b/duckdb-nsql/manifest/manifest/__init__.py @@ -0,0 +1,6 @@ +"""Manifest init.""" +from manifest.manifest import Manifest +from manifest.request import Request +from manifest.response import Response + +__all__ = ["Manifest", "Response", "Request"] diff --git a/duckdb-nsql/manifest/manifest/api/__init__.py b/duckdb-nsql/manifest/manifest/api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..30dc19022e0cb628538a3e645a4e2e1fb6314277 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/__init__.py @@ -0,0 +1 @@ +"""Api init.""" diff --git a/duckdb-nsql/manifest/manifest/api/app.py b/duckdb-nsql/manifest/manifest/api/app.py new file mode 100644 index 0000000000000000000000000000000000000000..a8f9ebca491c82e79b85a40e0b02bd08210cedaa --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/app.py @@ -0,0 +1,301 @@ +"""Flask app.""" +import argparse +import io +import json +import logging +import os +import socket +from typing import Dict + +import pkg_resources +from flask import Flask, Response, request + +from manifest.api.models.diffuser import DiffuserModel +from manifest.api.models.huggingface import ( + MODEL_GENTYPE_REGISTRY, + CrossModalEncoderModel, + TextGenerationModel, +) +from manifest.api.models.sentence_transformer import SentenceTransformerModel +from manifest.api.response import ModelResponse + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + +logger = logging.getLogger(__name__) +app = Flask(__name__) # define app using Flask +# Will be global +model = None +model_type = None +PORT = int(os.environ.get("FLASK_PORT", 5000)) +MODEL_CONSTRUCTORS = { + "huggingface": TextGenerationModel, + "sentence_transformers": SentenceTransformerModel, + "huggingface_crossmodal": CrossModalEncoderModel, + "diffuser": DiffuserModel, +} + + +def parse_args() -> argparse.Namespace: + """Generate args.""" + parser = argparse.ArgumentParser(description="Model args") + parser.add_argument( + "--model_type", + default=None, + type=str, + required=True, + help="Model type used for finding constructor.", + choices=MODEL_CONSTRUCTORS.keys(), + ) + parser.add_argument( + "--model_generation_type", + default=None, + type=str, + help="Model generation type.", + choices=MODEL_GENTYPE_REGISTRY.keys(), + ) + parser.add_argument( + "--model_name_or_path", + default=None, + type=str, + help="Name of model or path to model. Used in initialize of model class.", + ) + parser.add_argument( + "--cache_dir", default=None, type=str, help="Cache directory for models." + ) + parser.add_argument( + "--device", type=int, default=0, help="Model device. -1 for CPU." + ) + parser.add_argument( + "--fp16", action="store_true", help="Force use fp16 for model params." + ) + parser.add_argument( + "--percent_max_gpu_mem_reduction", + type=float, + default=0.85, + help="Used with accelerate multigpu. Scales down max memory.", + ) + parser.add_argument( + "--use_bitsandbytes", + action="store_true", + help=("Use bits and bytes. " "This will override --device parameter."), + ) + parser.add_argument( + "--use_accelerate_multigpu", + action="store_true", + help=( + "Use accelerate for multi gpu inference. " + "This will override --device parameter." + ), + ) + parser.add_argument( + "--use_hf_parallelize", + action="store_true", + help=( + "Use HF parallelize for multi gpu inference. " + "This will override --device parameter." + ), + ) + parser.add_argument( + "--use_deepspeed", + action="store_true", + help=("Use deepspeed. This will override --device parameter."), + ) + args = parser.parse_args() + return args + + +def is_port_in_use(port: int) -> bool: + """Check if port is in use.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex(("localhost", port)) == 0 + + +def main() -> None: + """Run main.""" + kwargs = parse_args() + if is_port_in_use(PORT): + raise ValueError(f"Port {PORT} is already in use.") + global model_type + model_type = kwargs.model_type + model_gen_type = kwargs.model_generation_type + model_name_or_path = kwargs.model_name_or_path + if not model_name_or_path: + raise ValueError("Must provide model_name_or_path.") + if kwargs.use_accelerate_multigpu: + logger.info("Using accelerate. Overridding --device argument.") + if ( + kwargs.percent_max_gpu_mem_reduction <= 0 + or kwargs.percent_max_gpu_mem_reduction > 1 + ): + raise ValueError("percent_max_gpu_mem_reduction must be in (0, 1].") + if ( + sum( + [ + kwargs.use_accelerate_multigpu, + kwargs.use_hf_parallelize, + kwargs.use_bitsandbytes, + kwargs.use_deepspeed, + ] + ) + > 1 + ): + raise ValueError( + "Only one of use_accelerate_multigpu, use_hf_parallelize, " + "use_bitsandbytes, and use_deepspeed can be set." + ) + # Global model + global model + model = MODEL_CONSTRUCTORS[model_type]( + model_name_or_path, + model_type=model_gen_type, + cache_dir=kwargs.cache_dir, + device=kwargs.device, + use_accelerate=kwargs.use_accelerate_multigpu, + use_parallelize=kwargs.use_hf_parallelize, + use_bitsandbytes=kwargs.use_bitsandbytes, + use_deepspeed=kwargs.use_deepspeed, + perc_max_gpu_mem_red=kwargs.percent_max_gpu_mem_reduction, + use_fp16=kwargs.fp16, + ) + app.run(host="0.0.0.0", port=PORT) + + +@app.route("/completions", methods=["POST"]) +def completions() -> Response: + """Get completions for generation.""" + prompt = request.json["prompt"] + del request.json["prompt"] + generation_args = request.json + + if not isinstance(prompt, (str, list)): + raise ValueError("Prompt must be a str or list of str") + try: + result_gens = [] + for generations in model.generate(prompt, **generation_args): + result_gens.append(generations) + if model_type == "diffuser": + # Assign None logprob as it's not supported in diffusers + results = [ + {"array": r[0], "logprob": None, "tokens": None, "token_logprobs": None} + for r in result_gens + ] + res_type = "image_generation" + else: + results = [ + {"text": r[0], "logprob": r[1], "tokens": r[2], "token_logprobs": r[3]} + for r in result_gens + ] + res_type = "text_completion" + # transform the result into the openai format + return Response( + json.dumps(ModelResponse(results, response_type=res_type).__dict__()), + status=200, + ) + except Exception as e: + logger.error(e) + return Response( + json.dumps({"message": str(e)}), + status=400, + ) + + +@app.route("/embed", methods=["POST"]) +def embed() -> Response: + """Get embed for generation.""" + if "modality" in request.json: + modality = request.json["modality"] + else: + modality = "text" + if modality == "text": + prompts = request.json["prompt"] + elif modality == "image": + import base64 + + from PIL import Image + + prompts = [ + Image.open(io.BytesIO(base64.b64decode(data))) + for data in request.json["prompt"] + ] + else: + raise ValueError("modality must be text or image") + + try: + results = [] + embeddings = model.embed(prompts) + for embedding in embeddings: + results.append( + { + "array": embedding, + "logprob": None, + "tokens": None, + "token_logprobs": None, + } + ) + + return Response( + json.dumps( + ModelResponse(results, response_type="embedding_generation").__dict__() + ), + status=200, + ) + except Exception as e: + logger.error(e) + return Response( + json.dumps({"message": str(e)}), + status=400, + ) + + +@app.route("/score_sequence", methods=["POST"]) +def score_sequence() -> Response: + """Get logprob of prompt.""" + prompt = request.json["prompt"] + del request.json["prompt"] + generation_args = request.json + + if not isinstance(prompt, (str, list)): + raise ValueError("Prompt must be a str or list of str") + + try: + score_list = model.score_sequence(prompt, **generation_args) + results = [ + { + "text": prompt if isinstance(prompt, str) else prompt[i], + "logprob": r[0], + "tokens": r[1], + "token_logprobs": r[2], + } + for i, r in enumerate(score_list) + ] + # transform the result into the openai format + return Response( + json.dumps( + ModelResponse(results, response_type="prompt_logit_score").__dict__() + ), + status=200, + ) + except Exception as e: + logger.error(e) + return Response( + json.dumps({"message": str(e)}), + status=400, + ) + + +@app.route("/params", methods=["POST"]) +def params() -> Dict: + """Get model params.""" + return model.get_init_params() + + +@app.route("/") +def index() -> str: + """Get index completion.""" + fn = pkg_resources.resource_filename("metaseq", "service/index.html") + with open(fn) as f: + return f.read() + + +if __name__ == "__main__": + main() diff --git a/duckdb-nsql/manifest/manifest/api/models/__init__.py b/duckdb-nsql/manifest/manifest/api/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d80b6cf5d6bf664c117998224dd9289eaf8cab3f --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/models/__init__.py @@ -0,0 +1 @@ +"""Models init.""" diff --git a/duckdb-nsql/manifest/manifest/api/models/diffuser.py b/duckdb-nsql/manifest/manifest/api/models/diffuser.py new file mode 100644 index 0000000000000000000000000000000000000000..e04db4f9dfd2e70da5a252ea5201366d8f7c46c6 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/models/diffuser.py @@ -0,0 +1,123 @@ +"""Diffuser model.""" +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple, Union + +import numpy as np +import torch +from diffusers import StableDiffusionPipeline + +from manifest.api.models.model import Model + + +class DiffuserModel(Model): + """Diffuser model.""" + + def __init__( + self, + model_name_or_path: str, + model_type: Optional[str] = None, + model_config: Optional[str] = None, + cache_dir: Optional[str] = None, + device: int = 0, + use_accelerate: bool = False, + use_parallelize: bool = False, + use_bitsandbytes: bool = False, + use_deepspeed: bool = False, + perc_max_gpu_mem_red: float = 1.0, + use_fp16: bool = False, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_config: model config string. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + if use_accelerate or use_parallelize or use_bitsandbytes or use_deepspeed: + raise ValueError( + "Cannot use accelerate or parallelize or " + "bitsandbytes or deepspeeed with diffusers" + ) + # Check if providing path + self.model_path = model_name_or_path + if Path(self.model_path).exists() and Path(self.model_path).is_dir(): + model_name_or_path = Path(self.model_path).name + self.model_name = model_name_or_path + print("Model Name:", self.model_name, "Model Path:", self.model_path) + dtype = torch.float16 if use_fp16 else None + torch_device = ( + torch.device("cpu") + if (device == -1 or not torch.cuda.is_available()) + else torch.device(f"cuda:{device}") + ) + self.pipeline = StableDiffusionPipeline.from_pretrained( + self.model_path, + torch_dtype=dtype, + revision="fp16" if str(dtype) == "float16" else None, + ) + self.pipeline.safety_checker = None + self.pipeline.to(torch_device) + + def get_init_params(self) -> Dict: + """Return init params to determine what model is being used.""" + return {"model_name": self.model_name, "model_path": self.model_path} + + @torch.no_grad() + def generate( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[Any, float, List[str], List[float]]]: + """ + Generate the prompt from model. + + Outputs must be generated text and score, not including prompt. + + Args: + prompt: promt to generate from. + + Returns: + list of generated text (list of length 1 for 1 generation). + """ + # TODO: Is this correct for getting arguments in? + if isinstance(prompt, str): + prompt = [prompt] + result = self.pipeline(prompt, output_type="np.array", **kwargs) + # Return None for logprobs and token logprobs + return [(im, None, None, None) for im in result["images"]] + + @torch.no_grad() + def embed(self, prompt: Union[str, List[str]], **kwargs: Any) -> np.ndarray: + """ + Embed the prompt from model. + + Args: + prompt: promt to embed from. + + Returns: + list of embeddings (list of length 1 for 1 embedding). + """ + raise NotImplementedError("Embed not supported for diffusers") + + @torch.no_grad() + def score_sequence( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[float, List[int], List[float]]]: + """ + Score a sequence of choices. + + Args: + prompt (:obj:`str` or :obj:`List[str]`): + The prompt to score the choices against. + **kwargs: + Additional keyword arguments passed along to the :obj:`__call__` method. + """ + raise NotImplementedError("Score sequence not supported for diffusers") diff --git a/duckdb-nsql/manifest/manifest/api/models/huggingface.py b/duckdb-nsql/manifest/manifest/api/models/huggingface.py new file mode 100644 index 0000000000000000000000000000000000000000..912832bb1b847c9584d5eb5bd44dbbf9b779676a --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/models/huggingface.py @@ -0,0 +1,671 @@ +"""Huggingface model.""" +import json +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple, Union, cast + +import deepspeed +import numpy as np +import PIL +import torch +from accelerate import dispatch_model, infer_auto_device_map +from accelerate.utils.modeling import get_max_memory as acc_get_max_memory +from transformers import ( + AutoModelForCausalLM, + AutoModelForSeq2SeqLM, + AutoTokenizer, + BloomForCausalLM, + CLIPModel, + CLIPProcessor, + GPT2LMHeadModel, + GPTJForCausalLM, + GPTNeoForCausalLM, + GPTNeoXForCausalLM, + LlamaForCausalLM, + LlamaTokenizer, + OPTForCausalLM, + PreTrainedModel, + PreTrainedTokenizer, +) + +from manifest.api.models.model import Model + +MODEL_REGISTRY = { + "EleutherAI/gpt-neo-125M": GPTNeoForCausalLM, + "EleutherAI/gpt-neo-1.3B": GPTNeoForCausalLM, + "EleutherAI/gpt-neo-2.7B": GPTNeoForCausalLM, + "EleutherAI/gpt-j-6B": GPTJForCausalLM, + "EleutherAI/gpt-neox-20b": GPTNeoXForCausalLM, + "facebook/opt-125m": OPTForCausalLM, + "facebook/opt-350m": OPTForCausalLM, + "Salesforce/codegen-2B-mono": AutoModelForCausalLM, + "Salesforce/codegen-6B-mono": AutoModelForCausalLM, + "facebook/opt-1.3b": OPTForCausalLM, + "facebook/opt-2.7b": OPTForCausalLM, + "facebook/opt-6.7b": OPTForCausalLM, + "facebook/opt-13b": OPTForCausalLM, + "facebook/opt-30b": OPTForCausalLM, + "gpt2": GPT2LMHeadModel, + "openai/clip-vit-base-patch32": CLIPModel, + "bigscience/bloom-560m": BloomForCausalLM, + "bigscience/bloom-1b7": BloomForCausalLM, + "bigscience/bloom-3b": BloomForCausalLM, + "bigscience/bloom-7b1": BloomForCausalLM, + "chainyo/alpaca-lora-7b": LlamaForCausalLM, + "bigscience/bloom": AutoModelForCausalLM, + "bigscience/T0pp": AutoModelForSeq2SeqLM, + "bigscience/T0_3B": AutoModelForSeq2SeqLM, + "google/t5-small-lm-adapt": AutoModelForSeq2SeqLM, # 220M + "google/t5-l-lm-adapt": AutoModelForSeq2SeqLM, # 800M + "google/t5-xl-lm-adapt": AutoModelForSeq2SeqLM, # 3B + "google/t5-xxl-lm-adapt": AutoModelForSeq2SeqLM, # 11B + "google/t5-v1_1-l": AutoModelForSeq2SeqLM, # 800M + "google/t5-v1_1-xl": AutoModelForSeq2SeqLM, # 3B + "google/t5-v1_1-xxl": AutoModelForSeq2SeqLM, # 11B + "google/flan-t5-l": AutoModelForSeq2SeqLM, # 800M + "google/flan-t5-xl": AutoModelForSeq2SeqLM, # 3B + "google/flan-t5-xxl": AutoModelForSeq2SeqLM, # 11B +} + +MODEL_GENTYPE_REGISTRY = { + "text-generation": AutoModelForCausalLM, + "llama-text-generation": LlamaForCausalLM, + "text2text-generation": AutoModelForSeq2SeqLM, +} + + +def get_max_memory(gpu_reduction: float) -> Dict[int, str]: + """Get max memory in GB times reduction.""" + free_in_gb = int(torch.cuda.mem_get_info()[0] / 1024**3) # type: ignore + max_mem = f"{int(gpu_reduction*free_in_gb)}GB" + + n_gpus = torch.cuda.device_count() + max_mem_dict = {i: max_mem for i in range(n_gpus)} + return max_mem_dict + + +class GenerationPipeline: + """ + Custom Pipeline. + + HF pipelines do not handle devices well in multi-gpu setting. + Create our own generation pipeline. + """ + + def __init__( + self, + model: Union[PreTrainedModel, deepspeed.InferenceEngine], + tokenizer: PreTrainedTokenizer, + device: int = None, + bitsandbytes: bool = False, + is_encdec: bool = False, + ): + """Initialize.""" + # Use to turn off sampling + # https://github.com/TimDettmers/bitsandbytes/issues/42 + self.bitsandbytes = bitsandbytes + self.model = model + self.is_encdec = is_encdec + config = model.config # type: ignore + # Used for GPT + self.max_length = getattr(config, "max_position_embeddings", None) + if self.max_length is None: + # Used for Bloom + self.max_length = getattr(config, "seq_length", None) + if self.max_length is None: + # Used for T0 + self.max_length = getattr(config, "d_model", None) + if self.max_length is None: + # Default + self.max_length = 2048 + + print(f"Usings max_length: {self.max_length}") + + self.tokenizer = tokenizer + # self.device = device + # With bits and bytes, do not want to place inputs on any device + # if self.device: + self.device = ( + torch.device("cpu") + if (device == -1 or not torch.cuda.is_available()) + else torch.device(f"cuda:{device}") + ) + + def __call__( + self, text: Union[str, List[str]], **kwargs: Any + ) -> List[Dict[str, Union[str, List[float], List[str]]]]: + """Generate from text. + + Args: + text: text to generate. + + Returns: + generated text. + """ + # If text is longer than max model length, we reduce max input length to ensure + # the user indicated generation tokens is preserved. + max_input_len = ( + self.max_length - kwargs.get("max_new_tokens") + if not self.is_encdec + else self.max_length + ) + encoded_prompt = self.tokenizer( + text, + max_length=max_input_len, + truncation=True, + padding=True, + return_tensors="pt", + ) + encoded_prompt = encoded_prompt.to(self.device) + kwargs_to_pass = dict( + temperature=kwargs.get("temperature"), + top_k=kwargs.get("top_k"), + top_p=kwargs.get("top_p"), + repetition_penalty=kwargs.get("repetition_penalty"), + num_return_sequences=kwargs.get("num_return_sequences"), + do_sample=kwargs.get("do_sample"), + ) + kwargs_to_pass = {k: v for k, v in kwargs_to_pass.items() if v is not None} + output_dict = self.model.generate( # type: ignore + **encoded_prompt, + **kwargs_to_pass, + max_new_tokens=kwargs.get("max_new_tokens"), + eos_token_id=self.tokenizer.eos_token_id, + pad_token_id=self.tokenizer.pad_token_id, + output_scores=True, + return_dict_in_generate=True, + ) + # logits/scores from the output always correspond to the generated tokens. + # shape (num_tokens, num_return_sequences, vocab_size) + logits = torch.stack(output_dict.scores) + logits = torch.nn.functional.log_softmax(logits, dim=-1) + num_generated_tokens = logits.shape[0] + generated_sequences = [ + { + "generated_text": self.tokenizer.decode( + output_seq[-num_generated_tokens:], skip_special_tokens=True + ), + "logprobs": logits[ + range(num_generated_tokens), i, output_seq[-num_generated_tokens:] + ].tolist(), + "tokens": self.tokenizer.convert_ids_to_tokens( + output_seq[-num_generated_tokens:].tolist() + ), + } + for i, output_seq in enumerate(output_dict.sequences) + ] + return generated_sequences + + +class HuggingFaceModel(Model): + """HuggingFace Model.""" + + def __init__( + self, + model_name_or_path: str, + model_type: Optional[str] = None, + model_config: Optional[str] = None, + cache_dir: Optional[str] = None, + device: int = 0, + use_accelerate: bool = False, + use_parallelize: bool = False, + use_bitsandbytes: bool = False, + use_deepspeed: bool = False, + perc_max_gpu_mem_red: float = 1.0, + use_fp16: bool = False, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_config: model config string. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + if sum([use_accelerate, use_parallelize, use_bitsandbytes, use_deepspeed]) > 1: + raise ValueError( + "Only one of use_accelerate, use_parallelize, " + "use_bitsandbytes, use_deepspeed can be set to True" + ) + # Check if providing path + self.model_path = model_name_or_path + if Path(self.model_path).exists() and Path(self.model_path).is_dir(): + # Try to find config + if (Path(self.model_path) / "config.json").exists(): + config = json.load(open(Path(self.model_path) / "config.json")) + model_name_or_path = config["_name_or_path"] + self.model_name = model_name_or_path + self.model_type = model_type + if self.model_name not in MODEL_REGISTRY and self.model_type is None: + raise ValueError( + f"{self.model_name} is not in our registry. Please specify " + "--model_generation_type as either text-generation (for Causal)" + " or text2text-generation (for Seq2Seq)" + ) + print("Model Name:", self.model_name, "Model Path:", self.model_path) + + def get_init_params(self) -> Dict: + """Return init params to determine what model is being used.""" + return {"model_name": self.model_name, "model_path": self.model_path} + + def _dispatch_deepspeed_model( + self, model: PreTrainedModel + ) -> deepspeed.InferenceEngine: + """ + Load model with deepspeed. + + Adapted from https://www.deepspeed.ai/tutorials/inference-tutorial/ + + Args: + model: loaded hugging face model + """ + model = deepspeed.init_inference( + model=model, + mp_size=1, + dtype=model.dtype, + replace_method="auto", + replace_with_kernel_inject=True, + ) + return model + + def _dispatch_accelerate_model( + self, model: PreTrainedModel, perc_max_gpu_mem_red: float + ) -> None: + """ + Load model with accelerate. + + Adapted from https://colab.research.google.com/drive/14wnxMvD9zsiBQo2FtT + pxn6w2cpXCcb-7#scrollTo=y8Ne7jJdaF9F&uniqifier=1 + + Args: + model: loaded hugging face model + perc_max_gpu_mem_red: percent memory reduction + """ + model.tie_weights() # type: ignore + # Get the model where we can infer devices from + if hasattr(model, "model"): + # OPT + main_model = model.model # type: ignore + model_getter = "model." + else: + # Eleuther Neo and J + main_model = model + model_getter = "" + # Decrease max mem + max_memory = { + k: int(perc_max_gpu_mem_red * v) for k, v in acc_get_max_memory().items() + } + raw_device_map = infer_auto_device_map( + main_model, + max_memory=max_memory, + no_split_module_classes=[ + "OPTDecoderLayer", + "GPTNeoBlock", + "GPTJBlock", + "GPTNeoXLayer", + "T5Block", + ], + dtype=model.dtype, # type: ignore + ) + # Hacky fix for Eleuther getting the "weight" of embeddings + device_map = {} + for k, v in raw_device_map.items(): + if k in {"wte", "wpe"}: + device_map[f"{model_getter}{k}.weight"] = v + else: + device_map[f"{model_getter}{k}"] = v + # For OPT models + if "lm_head" not in device_map: + try: + device_map["lm_head"] = max(device_map.values()) + except TypeError: + device_map["lm_head"] = "cpu" + print("Device Map", device_map) + dispatch_model(model, device_map=device_map) + return + + +class CrossModalEncoderModel(HuggingFaceModel): + """CrossModalEncoderModel.""" + + def __init__( + self, + model_name_or_path: str, + model_type: Optional[str] = None, + model_config: Optional[str] = None, + cache_dir: Optional[str] = None, + device: int = 0, + use_accelerate: bool = False, + use_parallelize: bool = False, + use_bitsandbytes: bool = False, + use_deepspeed: bool = False, + perc_max_gpu_mem_red: float = 1.0, + use_fp16: bool = False, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_config: model config string. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + super().__init__( + model_name_or_path, + model_type, + model_config, + cache_dir, + device, + use_accelerate, + use_parallelize, + use_bitsandbytes, + use_deepspeed, + perc_max_gpu_mem_red, + use_fp16, + ) + + # TODO: make this generalizable + self.processor = CLIPProcessor.from_pretrained(self.model_path) + + model = MODEL_REGISTRY.get( + self.model_name, MODEL_GENTYPE_REGISTRY.get(self.model_type, None) + ).from_pretrained( + self.model_path, + cache_dir=cache_dir, + trust_remote_code=True, + ) + model.eval() + + torch_device = ( + torch.device("cpu") + if (device == -1 or not torch.cuda.is_available()) + else torch.device(f"cuda:{device}") + ) + self.model = model.to(torch_device) # type: ignore + + @torch.no_grad() + def embed(self, prompt: Union[str, List[str]], **kwargs: Any) -> np.ndarray: + """ + Compute embedding for prompts. + + Args: + prompt: promt to generate from. + + Returns: + embedding + """ + if isinstance(prompt, str): + inputs = self.processor(text=prompt, return_tensors="pt", padding=True) + elif isinstance(prompt, PIL.Image.Image): + inputs = self.processor(images=prompt, return_tensors="pt", padding=True) + else: + raise ValueError("Prompt must be a string or an image") + + outputs = self.model(**inputs) + return outputs + + +class TextGenerationModel(HuggingFaceModel): + """Huggingface model.""" + + def __init__( + self, + model_name_or_path: str, + model_type: Optional[str] = None, + model_config: Optional[str] = None, + cache_dir: Optional[str] = None, + device: int = 0, + use_accelerate: bool = False, + use_parallelize: bool = False, + use_bitsandbytes: bool = False, + use_deepspeed: bool = False, + perc_max_gpu_mem_red: float = 1.0, + use_fp16: bool = False, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_config: model config string. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + super().__init__( + model_name_or_path, + model_type, + model_config, + cache_dir, + device, + use_accelerate, + use_parallelize, + use_bitsandbytes, + use_deepspeed, + perc_max_gpu_mem_red, + use_fp16, + ) + if ( + MODEL_REGISTRY.get( + self.model_name, MODEL_GENTYPE_REGISTRY.get(self.model_type, None) + ) + == LlamaForCausalLM + ): + tokenizer = LlamaTokenizer.from_pretrained(self.model_name) + else: + try: + tokenizer = AutoTokenizer.from_pretrained( + self.model_name, truncation_side="left", padding_side="left" + ) + except ValueError: + tokenizer = AutoTokenizer.from_pretrained( + self.model_name, + truncation_side="left", + padding_side="left", + use_fast=False, + ) + dtype = torch.float16 if use_fp16 else "auto" + if use_bitsandbytes: + print("WARNING!!! Cannot use sampling with bitsandbytes.") + max_memory = get_max_memory(perc_max_gpu_mem_red) + model = MODEL_REGISTRY.get( + self.model_name, MODEL_GENTYPE_REGISTRY.get(self.model_type, None) + ).from_pretrained( # type: ignore + self.model_path, + cache_dir=cache_dir, + load_in_8bit=True, + device_map="auto", + max_memory=max_memory, + trust_remote_code=True, + ) + else: + try: + # Try to explicitely find a fp16 copy (gpt-j-6B for example) + model = MODEL_REGISTRY.get( + self.model_name, MODEL_GENTYPE_REGISTRY.get(self.model_type, None) + ).from_pretrained( # type: ignore + self.model_path, + cache_dir=cache_dir, + revision="float16", + torch_dtype=torch.float16, + trust_remote_code=True, + ) + except Exception: + model = MODEL_REGISTRY.get( + self.model_name, MODEL_GENTYPE_REGISTRY.get(self.model_type, None) + ).from_pretrained( # type: ignore + self.model_path, + cache_dir=cache_dir, + torch_dtype=dtype, + trust_remote_code=True, + ) + model.eval() + print(f"Loaded Model DType {model.dtype}") + self.is_encdec = model.config.is_encoder_decoder + if not self.is_encdec: + tokenizer.pad_token = tokenizer.eos_token + tokenizer.pad_token_id = tokenizer.eos_token_id + if not use_bitsandbytes: + if use_accelerate: + self._dispatch_accelerate_model(model, perc_max_gpu_mem_red) + device = 0 + elif use_parallelize: + model.parallelize() + device = 0 + elif use_deepspeed: + self._dispatch_deepspeed_model(model) + device = 0 + else: + if device > -1: + torch_device = ( + torch.device("cpu") + if (device == -1 or not torch.cuda.is_available()) + else torch.device(f"cuda:{device}") + ) + model = model.to(torch_device) # type: ignore + self.pipeline = GenerationPipeline( # type: ignore + model=model, + tokenizer=tokenizer, + device=device, + bitsandbytes=use_bitsandbytes, + is_encdec=self.is_encdec, + ) + + @torch.no_grad() + def embed(self, prompt: Union[str, List[str]], **kwargs: Any) -> np.ndarray: + """ + Embed the prompt from model. + + Args: + prompt: promt to embed from. + + Returns: + list of embeddings (list of length 1 for 1 embedding). + """ + if isinstance(prompt, str): + prompt = [prompt] + encoded_prompt = self.pipeline.tokenizer( + prompt, + max_length=self.pipeline.max_length, + truncation=True, + padding=True, + return_tensors="pt", + ) + encoded_prompt = encoded_prompt.to(self.pipeline.device) + # Get last hidden state + output = self.pipeline.model( # type: ignore + **encoded_prompt, + output_hidden_states=True, + return_dict=True, + ) + last_hidden_state = output["hidden_states"][-1][:, -1, :] + return last_hidden_state.cpu().numpy() + + @torch.no_grad() + def generate( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[Any, float, List[str], List[float]]]: + """ + Generate the prompt from model. + + Outputs must be generated text and score, not including prompt. + + Args: + prompt: promt to generate from. + + Returns: + list of generated text (list of length 1 for 1 generation). + """ + num_return = kwargs.get("n", 1) + if isinstance(prompt, list) and num_return > 1: + raise ValueError("In batch generate, n must be 1.") + result = self.pipeline( + prompt, + max_new_tokens=kwargs.get("max_tokens"), + temperature=kwargs.get("temperature"), + repetition_penalty=kwargs.get("repetition_penalty"), + top_k=kwargs.get("top_k"), + top_p=kwargs.get("top_p"), + do_sample=kwargs.get("do_sample"), + num_return_sequences=num_return, + ) + final_results = [ + ( + cast(str, r["generated_text"]), + sum(cast(List[float], r["logprobs"])), + cast(List[str], r["tokens"]), + cast(List[float], r["logprobs"]), + ) + for r in result + ] + return final_results + + @torch.no_grad() + def score_sequence( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[float, List[int], List[float]]]: + """ + Score a sequence of choices. + + Args: + prompt (:obj:`str` or :obj:`List[str]`): + The prompt to score the choices against. + **kwargs: + Additional keyword arguments passed along to the :obj:`__call__` method. + """ + if isinstance(prompt, str): + prompt = [prompt] + encoded_prompt = self.pipeline.tokenizer( + prompt, + max_length=self.pipeline.max_length, + truncation=True, + padding=True, + return_tensors="pt", + ) + encoded_prompt["labels"] = encoded_prompt["input_ids"].clone() + encoded_prompt = encoded_prompt.to(self.pipeline.device) + logits = self.pipeline.model( # type: ignore + **encoded_prompt, + ).logits + # For causal decoders, shift logts and labels + labels_attention_mask = encoded_prompt["attention_mask"].unsqueeze(-1) + masked_log_probs = labels_attention_mask.float() * torch.log_softmax( + logits.float(), dim=-1 + ) + seq_token_log_probs = torch.gather( + masked_log_probs, -1, encoded_prompt["labels"].unsqueeze(-1) + ) + seq_token_log_probs = seq_token_log_probs.squeeze(dim=-1) + seq_log_prob = seq_token_log_probs.sum(dim=-1) + return [ + (seq, tokens, seq_token) + for seq, tokens, seq_token in zip( + seq_log_prob.tolist(), + encoded_prompt["input_ids"].tolist(), + seq_token_log_probs.tolist(), + ) + ] diff --git a/duckdb-nsql/manifest/manifest/api/models/model.py b/duckdb-nsql/manifest/manifest/api/models/model.py new file mode 100644 index 0000000000000000000000000000000000000000..dcb04b9618cad8772a524f46a54e495b31ef410e --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/models/model.py @@ -0,0 +1,91 @@ +"""Model class.""" +from typing import Any, Dict, List, Tuple, Union + +import numpy as np + + +class Model: + """Model class.""" + + def __init__( + self, + model_name_or_path: str, + model_type: str, + cache_dir: str, + device: int, + use_accelerate: bool, + use_parallelize: bool, + use_bitsandbytes: bool, + use_deepspeed: bool, + perc_max_gpu_mem_red: float, + use_fp16: bool, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_type: model type string for when model_name not in registry. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + raise NotImplementedError() + + def get_init_params(self) -> Dict: + """Return init params to determine what model is being used.""" + raise NotImplementedError() + + def generate( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[Any, float, List[str], List[float]]]: + """ + Generate the prompt from model. + + Outputs must be generated text and score, not including prompt. + + Args: + prompt: promt to generate from. + + Returns: + list of generated text (list of length 1 for 1 generation). + Each item is the response, answer logprob, list of tokens, + and list of logprobs for each token. + """ + raise NotImplementedError() + + def embed(self, prompt: Union[str, List[str]], **kwargs: Any) -> np.ndarray: + """ + Embed the prompt from model. + + Args: + prompt: promt to embed from. + + Returns: + list of embeddings (list of length 1 for 1 embedding). + """ + raise NotImplementedError() + + def score_sequence( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[float, List[int], List[float]]]: + """ + Score a sequence of choices. + + Args: + prompt (:obj:`str` or :obj:`List[str]`): + The prompt to score the choices against. + **kwargs: + Additional keyword arguments passed along to the :obj:`__call__` method. + + Returns: + Tuple of total score, tokens, and probs per token. + """ + raise NotImplementedError() diff --git a/duckdb-nsql/manifest/manifest/api/models/sentence_transformer.py b/duckdb-nsql/manifest/manifest/api/models/sentence_transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..5f6c2fb428c4dd32a0cb2de2971c67f5d8b8477e --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/models/sentence_transformer.py @@ -0,0 +1,113 @@ +"""Sentence transformer model.""" +from typing import Any, Dict, List, Optional, Tuple, Union + +import numpy as np +import torch +from sentence_transformers import SentenceTransformer + +from manifest.api.models.model import Model + + +class SentenceTransformerModel(Model): + """SentenceTransformer model.""" + + def __init__( + self, + model_name_or_path: str, + model_type: Optional[str] = None, + model_config: Optional[str] = None, + cache_dir: Optional[str] = None, + device: int = 0, + use_accelerate: bool = False, + use_parallelize: bool = False, + use_bitsandbytes: bool = False, + use_deepspeed: bool = False, + perc_max_gpu_mem_red: float = 1.0, + use_fp16: bool = False, + ): + """ + Initialize model. + + All arguments will be passed in the request from Manifest. + + Args: + model_name_or_path: model name string. + model_config: model config string. + cache_dir: cache directory for model. + device: device to use for model. + use_accelerate: whether to use accelerate for multi-gpu inference. + use_parallelize: use HF default parallelize + use_bitsandbytes: use HF bits and bytes + use_deepspeed: use deepspeed + perc_max_gpu_mem_red: percent max memory reduction in accelerate + use_fp16: use fp16 for model weights. + """ + if use_accelerate or use_parallelize or use_bitsandbytes or use_deepspeed: + raise ValueError( + "Cannot use accelerate or parallelize or " + "bitsandbytes or deepspeeed with sentence transformers" + ) + # Check if providing path + self.model_name = model_name_or_path + print("Model Name:", self.model_name) + torch_device = ( + torch.device("cpu") + if (device == -1 or not torch.cuda.is_available()) + else torch.device(f"cuda:{device}") + ) + self.embedding_model = SentenceTransformer(self.model_name, device=torch_device) + self.embedding_model.to(torch_device) + self.embedding_model.eval() + + def get_init_params(self) -> Dict: + """Return init params to determine what model is being used.""" + return {"model_name": self.model_name, "model_path": self.model_name} + + @torch.no_grad() + def generate( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[Any, float, List[str], List[float]]]: + """ + Generate the prompt from model. + + Outputs must be generated text and score, not including prompt. + + Args: + prompt: promt to generate from. + + Returns: + list of generated text (list of length 1 for 1 generation). + """ + raise NotImplementedError("Generate not supported for sentence transformers") + + @torch.no_grad() + def embed(self, prompt: Union[str, List[str]], **kwargs: Any) -> np.ndarray: + """ + Embed the prompt from model. + + Args: + prompt: promt to embed from. + + Returns: + list of embeddings (list of length 1 for 1 embedding). + """ + if isinstance(prompt, str): + prompt = [prompt] + return self.embedding_model.encode(prompt) + + @torch.no_grad() + def score_sequence( + self, prompt: Union[str, List[str]], **kwargs: Any + ) -> List[Tuple[float, List[int], List[float]]]: + """ + Score a sequence of choices. + + Args: + prompt (:obj:`str` or :obj:`List[str]`): + The prompt to score the choices against. + **kwargs: + Additional keyword arguments passed along to the :obj:`__call__` method. + """ + raise NotImplementedError( + "Score sequence not supported for sentence transformers" + ) diff --git a/duckdb-nsql/manifest/manifest/api/response.py b/duckdb-nsql/manifest/manifest/api/response.py new file mode 100644 index 0000000000000000000000000000000000000000..123d3a93c6f1872d1dff91dffa1a57637ae2a588 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/api/response.py @@ -0,0 +1,55 @@ +"""Response.""" + +import time +import uuid +from typing import Any, Dict, List + + +class ModelResponse: + """ModelResponse.""" + + def __init__(self, results: List[Dict[str, Any]], response_type: str) -> None: + """Initialize response.""" + self.results = results + self.response_type = response_type + if self.response_type not in { + "text_completion", + "prompt_logit_score", + "image_generation", + "embedding_generation", + }: + raise ValueError( + f"Invalid response type: {self.response_type}. " + "Must be one of: text_completion, prompt_logit_score, " + "image_generation, embedding_generation." + ) + self.response_id = str(uuid.uuid4()) + self.created = int(time.time()) + + def __dict__(self) -> Dict[str, Any]: # type: ignore + """Return dictionary representation of response.""" + key = ( + "text" + if self.response_type not in {"image_generation", "embedding_generation"} + else "array" + ) + return { + "id": self.response_id, + "object": self.response_type, + "created": self.created, + "model": "flask_model", + "choices": [ + { + key: result[key], + "logprob": result["logprob"], + "tokens": result["tokens"], + "token_logprobs": result["token_logprobs"], + } + if key == "text" + else { + key: result[key].tolist(), + "logprob": result["logprob"], + } + for result in self.results + ], + } diff --git a/duckdb-nsql/manifest/manifest/caches/__init__.py b/duckdb-nsql/manifest/manifest/caches/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..50b7463233ea16813668c3203132cdb5c5acda33 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/__init__.py @@ -0,0 +1 @@ +"""Cache init.""" diff --git a/duckdb-nsql/manifest/manifest/caches/array_cache.py b/duckdb-nsql/manifest/manifest/caches/array_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..9934fafd6f708dfb2c919a5ac79b9fd6e7189233 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/array_cache.py @@ -0,0 +1,116 @@ +"""Array cache.""" +from pathlib import Path +from typing import Union + +import numpy as np +from sqlitedict import SqliteDict + + +def open_mmap_arr(file: Union[Path, str], size: float) -> np.memmap: + """Open memmap.""" + if not Path(file).exists(): + mode = "w+" + else: + mode = "r+" + arr = np.memmap( # type: ignore + str(file), + dtype=np.float32, # This means we only support float 32 + mode=mode, + shape=size, + ) + return arr + + +class ArrayCache: + """Array cache.""" + + def __init__(self, folder: Union[str, Path]) -> None: + """ + Initialize the array writer. + + Args: + folder: folder to write to. + """ + self.folder = Path(folder) + self.folder.mkdir(exist_ok=True, parents=True) + self.hash2arrloc = SqliteDict( + self.folder / "hash2arrloc.sqlite", autocommit=True + ) + # Approx 1GB (I think) + self.max_memmap_size = 20480000 + self.cur_file_idx = 0 + # Get the last file idx used + for key in self.hash2arrloc: + file_data = self.hash2arrloc[key] + if file_data["file_idx"] > self.cur_file_idx: + self.cur_file_idx = file_data["file_idx"] + self.cur_memmap = open_mmap_arr( + self.folder / f"{self.cur_file_idx}.npy", + self.max_memmap_size, + ) + # Make sure there is space left in the memmap + non_zero = np.nonzero(self.cur_memmap)[0] + if len(non_zero) > 0: + self.cur_offset = int(np.max(non_zero) + 1) + else: + self.cur_offset = 0 + # If no space, make a new memmap + if self.cur_offset == self.max_memmap_size: + self.cur_file_idx += 1 + self.cur_memmap = open_mmap_arr( + self.folder / f"{self.cur_file_idx}.npy", + self.max_memmap_size, + ) + self.cur_offset = 0 + + def contains_key(self, key: str) -> bool: + """ + Check if the key is in the cache. + + Args: + key: key to check. + + Returns: + True if the key is in the cache. + """ + return key in self.hash2arrloc + + def put(self, key: str, arr: np.ndarray) -> None: + """Save array in store and associate location with key.""" + # Check if there is space in the memmap + arr_shape = arr.shape + arr = arr.flatten() + if len(arr) > self.max_memmap_size: + raise ValueError( + f"Array is too large to be cached. Max is {self.max_memmap_size}" + ) + if self.cur_offset + len(arr) > self.max_memmap_size: + self.cur_file_idx += 1 + self.cur_memmap = open_mmap_arr( + self.folder / f"{self.cur_file_idx}.npy", + self.max_memmap_size, + ) + self.cur_offset = 0 + self.cur_memmap[self.cur_offset : self.cur_offset + len(arr)] = arr + self.cur_memmap.flush() + self.hash2arrloc[key] = { + "file_idx": self.cur_file_idx, + "offset": self.cur_offset, + "flatten_size": len(arr), + "shape": arr_shape, + "dtype": arr.dtype, + } + self.cur_offset += len(arr) + return + + def get(self, key: str) -> np.ndarray: + """Get array associated with location from key.""" + file_data = self.hash2arrloc[key] + memmap = open_mmap_arr( + self.folder / f"{file_data['file_idx']}.npy", + self.max_memmap_size, + ) + arr = memmap[ + file_data["offset"] : file_data["offset"] + file_data["flatten_size"] + ] + return arr.reshape(file_data["shape"]).astype(file_data["dtype"]) diff --git a/duckdb-nsql/manifest/manifest/caches/cache.py b/duckdb-nsql/manifest/manifest/caches/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..e4cbe6d35f59473899b72cd3412e60aac3fb7631 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/cache.py @@ -0,0 +1,135 @@ +"""Cache for queries and responses.""" +from abc import ABC, abstractmethod +from typing import Any, Dict, Type, Union + +from manifest.caches.serializers import ArraySerializer, NumpyByteSerializer, Serializer +from manifest.request import DiffusionRequest, EmbeddingRequest, LMRequest, Request +from manifest.response import Response + +# Non-text return type caches +ARRAY_CACHE_TYPES = {EmbeddingRequest, DiffusionRequest} + + +class Cache(ABC): + """A cache for request/response pairs.""" + + def __init__( + self, + connection_str: str, + request_type: Type[Request] = LMRequest, + cache_args: Dict[str, Any] = {}, + ): + """ + Initialize cache. + + Args: + connection_str: connection string. + request_type: request type. + cache_args: arguments for cache. + + cache_args are any arguments needed to initialize the cache. + + Further, cache_args can contain `array_serializer` as a string + for embedding or image return types (e.g. diffusers) with values + as `local_file` or `byte_string`. `local_file` will save the + array in a local file and cache a pointer to the file. + `byte_string` will convert the array to a byte string and cache + the entire byte string. `byte_string` is default. + + Args: + connection_str: connection string for cache. + cache_args: cache arguments. + """ + self.request_type = request_type + self.connect(connection_str, cache_args) + if self.request_type in ARRAY_CACHE_TYPES: + array_serializer = cache_args.pop("array_serializer", "byte_string") + if array_serializer not in ["local_file", "byte_string"]: + raise ValueError( + "array_serializer must be local_file or byte_string," + f" not {array_serializer}" + ) + self.serializer = ( + ArraySerializer() + if array_serializer == "local_file" + else NumpyByteSerializer() + ) + else: + # If user has array_serializer type, it will throw an error as + # it is not recognized for non-array return types. + self.serializer = Serializer() + + @abstractmethod + def close(self) -> None: + """Close the cache.""" + raise NotImplementedError() + + @abstractmethod + def connect(self, connection_str: str, cache_args: Dict[str, Any]) -> None: + """ + Connect to cache. + + Args: + connection_str: connection string. + """ + raise NotImplementedError() + + @abstractmethod + def get_key(self, key: str, table: str = "default") -> Union[str, None]: + """ + Get the key for a request. + + With return None if key is not in cache. + + Args: + key: key for cache. + table: table to get key in. + """ + raise NotImplementedError() + + @abstractmethod + def set_key(self, key: str, value: str, table: str = "default") -> None: + """ + Set the value for the key. + + Will override old value. + + Args: + key: key for cache. + value: new value for key. + table: table to set key in. + """ + raise NotImplementedError() + + @abstractmethod + def commit(self) -> None: + """Commit any results.""" + raise NotImplementedError() + + def get(self, request: Dict) -> Union[Response, None]: + """Get the result of request (by calling compute as needed). + + Args: + request: request to get. + response: response to get. + + Returns: + Response object or None if not in cache. + """ + key = self.serializer.request_to_key(request) + cached_response = self.get_key(key) + if cached_response: + response = self.serializer.key_to_response(cached_response) + response["cached"] = True + return Response.from_dict(response, request_dict=request) + return None + + def set(self, request: Dict, response: Dict) -> None: + """Set the value for the key. + + Args: + request: request to set. + response: response to set. + """ + key = self.serializer.request_to_key(request) + self.set_key(key, self.serializer.response_to_key(response)) diff --git a/duckdb-nsql/manifest/manifest/caches/noop.py b/duckdb-nsql/manifest/manifest/caches/noop.py new file mode 100644 index 0000000000000000000000000000000000000000..19c90f20764b71bfdf821a0fef11ecc6f4d77422 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/noop.py @@ -0,0 +1,47 @@ +"""Noop cache.""" +from typing import Any, Dict, Union + +from manifest.caches.cache import Cache + + +class NoopCache(Cache): + """A Noop cache that caches nothing for request/response pairs.""" + + def connect(self, connection_str: str, cache_args: Dict[str, Any]) -> None: + """ + Connect to client. + + Args: + connection_str: connection string. + cache_args: arguments for cache. + """ + pass + + def close(self) -> None: + """Close the client.""" + pass + + def get_key(self, key: str, table: str = "default") -> Union[str, None]: + """ + Return None key for never in cache. + + Args: + key: key for cache. + table: table to get key in. + """ + return None + + def set_key(self, key: str, value: str, table: str = "default") -> None: + """ + Do not set anything as no cache. + + Args: + key: key for cache. + value: new value for key. + table: table to set key in. + """ + pass + + def commit(self) -> None: + """Commit any results.""" + pass diff --git a/duckdb-nsql/manifest/manifest/caches/postgres.py b/duckdb-nsql/manifest/manifest/caches/postgres.py new file mode 100644 index 0000000000000000000000000000000000000000..c7932b1c0e53655632403efb152b47ff029a88f2 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/postgres.py @@ -0,0 +1,131 @@ +"""Postgres cache.""" +import hashlib +import logging +from typing import Any, Dict, Union + +logger = logging.getLogger("postgresql") +logger.setLevel(logging.WARNING) + +from ..caches.cache import Cache + +try: + import sqlalchemy # type: ignore + from google.cloud.sql.connector import Connector # type: ignore + from sqlalchemy import Column, String # type: ignore + from sqlalchemy.ext.declarative import declarative_base # type: ignore + from sqlalchemy.orm import sessionmaker # type: ignore + + Base = declarative_base() + + class Request(Base): # type: ignore + """The request table.""" + + __tablename__ = "requests" + key = Column(String, primary_key=True) + response = Column( + String + ) # FIXME: ideally should be an hstore, but I don't want to set it up on GCP + + missing_dependencies = None + +except ImportError as e: + missing_dependencies = e + + +class PostgresCache(Cache): + """A PostgreSQL cache for request/response pairs.""" + + def connect(self, connection_str: str, cache_args: Dict[str, Any]) -> None: + """ + Connect to client. + + Args: + connection_str: connection string. + cache_args: arguments for cache should include the following fields: + { + "cache_user": "", + "cache_password": "", + "cache_db": "" + } + """ + if missing_dependencies: + raise ValueError( + "Missing dependencies for GCP PostgreSQL cache. " + "Install with `pip install manifest[gcp]`", + missing_dependencies, + ) + + connector = Connector() + + def getconn() -> Any: + conn = connector.connect( + connection_str, + "pg8000", + user=cache_args.pop("cache_user"), + password=cache_args.pop("cache_password"), + db=cache_args.pop("cache_db"), + ) + return conn + + engine = sqlalchemy.create_engine( + "postgresql+pg8000://", + creator=getconn, + ) + engine.dialect.description_encoding = None # type: ignore + + db_exists = len(sqlalchemy.inspect(engine).get_table_names()) > 0 + if not db_exists: + logger.info("Creating database...") + Base.metadata.create_all(engine) + + self.session = sessionmaker(bind=engine)() + + def close(self) -> None: + """Close the client.""" + self.session.close() + + @staticmethod + def _hash_key(key: str, table: str) -> str: + """Compute MD5 hash of the key.""" + return hashlib.md5(f"{key}:{table}".encode("utf-8")).hexdigest() + + def get_key(self, key: str, table: str = "default") -> Union[str, None]: + """ + Get the key for a request. + + With return None if key is not in cache. + + Args: + key: key for cache. + table: table to get key in. + """ + request = ( + self.session.query(Request) # type: ignore + .filter_by(key=self._hash_key(key, table)) + .first() + ) + out = request.response if request else None + return out # type: ignore + + def set_key(self, key: str, value: str, table: str = "default") -> None: + """ + Set the value for the key. + + Will override old value. + + Args: + key: key for cache. + value: new value for key. + table: table to set key in. + """ + key = self._hash_key(key, table) + request = self.session.query(Request).filter_by(key=key).first() # type: ignore + if request: + request.response = value # type: ignore + else: + self.session.add(Request(key=key, response=value)) + self.commit() + + def commit(self) -> None: + """Commit any results.""" + self.session.commit() diff --git a/duckdb-nsql/manifest/manifest/caches/redis.py b/duckdb-nsql/manifest/manifest/caches/redis.py new file mode 100644 index 0000000000000000000000000000000000000000..49f82d1dd17271c259e8b0e9c56daafbd1a10e9a --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/redis.py @@ -0,0 +1,64 @@ +"""Redis cache.""" +from typing import Any, Dict, Union + +import redis + +from manifest.caches.cache import Cache + + +class RedisCache(Cache): + """A Redis cache for request/response pairs.""" + + def connect(self, connection_str: str, cache_args: Dict[str, Any]) -> None: + """ + Connect to client. + + Args: + connection_str: connection string. + cache_args: arguments for cache. + """ + host, port = connection_str.split(":") + self.redis = redis.Redis(host=host, port=int(port), db=0) + return + + def close(self) -> None: + """Close the client.""" + self.redis.close() + + def _normalize_table_key(self, key: str, table: str) -> str: + """Cast key for prompt key.""" + return f"{table}:{key}" + + def get_key(self, key: str, table: str = "default") -> Union[str, None]: + """ + Get the key for a request. + + With return None if key is not in cache. + + Args: + key: key for cache. + table: table to get key in. + """ + norm_key = self._normalize_table_key(key, table) + if self.redis.exists(norm_key): + return self.redis.get(norm_key).decode("utf-8") + else: + return None + + def set_key(self, key: str, value: str, table: str = "default") -> None: + """ + Set the value for the key. + + Will override old value. + + Args: + key: key for cache. + value: new value for key. + table: table to set key in. + """ + self.redis.set(self._normalize_table_key(key, table), value) + self.commit() + + def commit(self) -> None: + """Commit any results.""" + pass diff --git a/duckdb-nsql/manifest/manifest/caches/serializers.py b/duckdb-nsql/manifest/manifest/caches/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..a1ec3dd8b2992f37fb3d39699e9e93e5e5f9b6eb --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/serializers.py @@ -0,0 +1,204 @@ +"""Serializer.""" + +import io +import json +import os +from pathlib import Path +from typing import Dict + +import numpy as np +import xxhash + +from manifest.caches.array_cache import ArrayCache + + +class Serializer: + """Serializer.""" + + def request_to_key(self, request: Dict) -> str: + """ + Normalize a request into a key. + + Args: + request: request to normalize. + + Returns: + normalized key. + """ + return json.dumps(request, sort_keys=True) + + def key_to_request(self, key: str) -> Dict: + """ + Convert the normalized version to the request. + + Args: + key: normalized key to convert. + + Returns: + unnormalized request dict. + """ + return json.loads(key) + + def response_to_key(self, response: Dict) -> str: + """ + Normalize a response into a key. + + Args: + response: response to normalize. + + Returns: + normalized key. + """ + return json.dumps(response, sort_keys=True) + + def key_to_response(self, key: str) -> Dict: + """ + Convert the normalized version to the response. + + Args: + key: normalized key to convert. + + Returns: + unnormalized response dict. + """ + return json.loads(key) + + +class NumpyByteSerializer(Serializer): + """Serializer by casting array to byte string.""" + + def response_to_key(self, response: Dict) -> str: + """ + Normalize a response into a key. + + Args: + response: response to normalize. + + Returns: + normalized key. + """ + sub_response = response["response"] + # Assume response is a dict with keys "choices" -> List dicts + # with keys "array". + choices = sub_response["choices"] + # We don't want to modify the response in place + # but we want to avoid calling deepcopy on an array + del sub_response["choices"] + response_copy = sub_response.copy() + sub_response["choices"] = choices + response_copy["choices"] = [] + for choice in choices: + if "array" not in choice: + raise ValueError( + f"Choice with keys {choice.keys()} does not have array key." + ) + arr = choice["array"] + # Avoid copying an array + del choice["array"] + new_choice = choice.copy() + choice["array"] = arr + with io.BytesIO() as f: + np.savez_compressed(f, data=arr) + hash_str = f.getvalue().hex() + new_choice["array"] = hash_str + response_copy["choices"].append(new_choice) + response["response"] = response_copy + return json.dumps(response, sort_keys=True) + + def key_to_response(self, key: str) -> Dict: + """ + Convert the normalized version to the response. + + Args: + key: normalized key to convert. + + Returns: + unnormalized response dict. + """ + response = json.loads(key) + for choice in response["response"]["choices"]: + hash_str = choice["array"] + byte_str = bytes.fromhex(hash_str) + with io.BytesIO(byte_str) as f: + choice["array"] = np.load(f)["data"] + return response + + +class ArraySerializer(Serializer): + """Serializer for array.""" + + def __init__(self) -> None: + """ + Initialize array serializer. + + We don't want to cache the array. We hash the value and + store the array in a memmap file. Store filename/offsets + in sqlitedict to keep track of hash -> array. + """ + super().__init__() + + self.hash = xxhash.xxh64() + manifest_home = Path(os.environ.get("MANIFEST_HOME", Path.home())) + cache_folder = manifest_home / ".manifest" / "array_cache" + self.writer = ArrayCache(cache_folder) + + def response_to_key(self, response: Dict) -> str: + """ + Normalize a response into a key. + + Convert arrays to hash string for cache key. + + Args: + response: response to normalize. + + Returns: + normalized key. + """ + sub_response = response["response"] + # Assume response is a dict with keys "choices" -> List dicts + # with keys "array". + choices = sub_response["choices"] + # We don't want to modify the response in place + # but we want to avoid calling deepcopy on an array + del sub_response["choices"] + response_copy = sub_response.copy() + sub_response["choices"] = choices + response_copy["choices"] = [] + for choice in choices: + if "array" not in choice: + raise ValueError( + f"Choice with keys {choice.keys()} does not have array key." + ) + arr = choice["array"] + # Avoid copying an array + del choice["array"] + new_choice = choice.copy() + choice["array"] = arr + + self.hash.update(arr) + hash_str = self.hash.hexdigest() + self.hash.reset() + new_choice["array"] = hash_str + response_copy["choices"].append(new_choice) + if not self.writer.contains_key(hash_str): + self.writer.put(hash_str, arr) + response["response"] = response_copy + return json.dumps(response, sort_keys=True) + + def key_to_response(self, key: str) -> Dict: + """ + Convert the normalized version to the response. + + Convert the hash string keys to the arrays. + + Args: + key: normalized key to convert. + + Returns: + unnormalized response dict. + """ + response = json.loads(key) + for choice in response["response"]["choices"]: + hash_str = choice["array"] + choice["array"] = self.writer.get(hash_str) + return response diff --git a/duckdb-nsql/manifest/manifest/caches/sqlite.py b/duckdb-nsql/manifest/manifest/caches/sqlite.py new file mode 100644 index 0000000000000000000000000000000000000000..6f842b454a70b57de2fcca41643e958d5471acd2 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/caches/sqlite.py @@ -0,0 +1,65 @@ +"""SQLite cache.""" +import logging +from typing import Any, Dict, Union + +from sqlitedict import SqliteDict + +from manifest.caches.cache import Cache + +logging.getLogger("sqlitedict").setLevel(logging.WARNING) + + +class SQLiteCache(Cache): + """A SQLite cache for request/response pairs.""" + + def connect(self, connection_str: str, cache_args: Dict[str, Any]) -> None: + """ + Connect to client. + + Args: + connection_str: connection string. + cache_args: arguments for cache. + """ + self.cache_file = connection_str + if not self.cache_file: + self.cache_file = ".sqlite.cache" + self.cache = SqliteDict(self.cache_file, autocommit=True) + return + + def close(self) -> None: + """Close the client.""" + self.cache.close() + + def _normalize_table_key(self, key: str, table: str) -> str: + """Cast key for prompt key.""" + return f"{table}:{key}" + + def get_key(self, key: str, table: str = "default") -> Union[str, None]: + """ + Get the key for a request. + + With return None if key is not in cache. + + Args: + key: key for cache. + table: table to get key in. + """ + return self.cache.get(self._normalize_table_key(key, table)) + + def set_key(self, key: str, value: str, table: str = "default") -> None: + """ + Set the value for the key. + + Will override old value. + + Args: + key: key for cache. + value: new value for key. + table: table to set key in. + """ + self.cache[self._normalize_table_key(key, table)] = value + self.commit() + + def commit(self) -> None: + """Commit any results.""" + self.cache.commit() diff --git a/duckdb-nsql/manifest/manifest/clients/__init__.py b/duckdb-nsql/manifest/manifest/clients/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..af6d3d638f88df95f2c17d41488816a1cd358f49 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/__init__.py @@ -0,0 +1 @@ +"""Client init.""" diff --git a/duckdb-nsql/manifest/manifest/clients/ai21.py b/duckdb-nsql/manifest/manifest/clients/ai21.py new file mode 100644 index 0000000000000000000000000000000000000000..8db5b58e8cd465cceca8b15ca9293f6b605b9ff0 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/ai21.py @@ -0,0 +1,125 @@ +"""AI21 client.""" +import logging +import os +from typing import Any, Dict, Optional + +from manifest.clients.client import Client +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + +AI21_ENGINES = { + "j2-ultra", + "j2-mid", + "j2-light", +} + + +class AI21Client(Client): + """AI21Client client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("engine", "j2-ultra"), + "temperature": ("temperature", 0.7), + "max_tokens": ("maxTokens", 40), + "top_k": ("topKReturn", 0), + "n": ("numResults", 1), + "top_p": ("topP", 1.0), + "stop_sequences": ("stopSequences", []), + } + REQUEST_CLS = LMRequest + NAME = "ai21" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the AI21 server. + + connection_str is passed as default AI21_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + # Taken from https://docs.ai21.com/ + self.host = "https://api.ai21.com/studio/v1" + self.api_key = connection_str or os.environ.get("AI21_API_KEY") + if self.api_key is None: + raise ValueError( + "AI21 API key not set. Set AI21_API_KEY environment " + "variable or pass through `client_connection`." + ) + + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in AI21_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {AI21_ENGINES}." + ) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/" + getattr(self, "engine") + "/complete" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {"Authorization": f"Bearer {self.api_key}"} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + return { + "object": "text_completion", + "model": getattr(self, "engine"), + "choices": [ + { + "text": item["data"]["text"], + "token_logprobs": item["data"]["tokens"], + } + for item in response["completions"] + ], + } diff --git a/duckdb-nsql/manifest/manifest/clients/azureendpoint.py b/duckdb-nsql/manifest/manifest/clients/azureendpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..a046298f391a08f8910dd778c4b17946dfddf563 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/azureendpoint.py @@ -0,0 +1,139 @@ +"""OpenRouter client.""" + +import copy +import logging +import os +from typing import Any, Dict, Optional +import time +from manifest.clients.client import Client +from manifest.request import LMRequest +import urllib.request +import json +import os +import ssl + +logger = logging.getLogger(__name__) +def allowSelfSignedHttps(allowed): + # bypass the server certificate verification on client side + if allowed and not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None): + ssl._create_default_https_context = ssl._create_unverified_context + +allowSelfSignedHttps(True) # this line is needed if you use self-signed certificate in your scoring service. + + +class AzureEndpointClient(Client): + """OpenRouter client.""" + + # Params are defined in https://openrouter.ai/docs/parameters + PARAMS = { + "engine": ("model", "meta-llama/codellama-70b-instruct"), + "max_tokens": ("max_tokens", 1000), + "temperature": ("temperature", 0.1), + "top_k": ("k", 0), + "frequency_penalty": ("frequency_penalty", 0.0), + "presence_penalty": ("presence_penalty", 0.0), + "stop_sequences": ("stop", None), + } + REQUEST_CLS = LMRequest + NAME = "azureendpoint" + IS_CHAT = True + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the OpenRouter server. + + connection_str is passed as default OPENROUTER_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + + self.host = os.environ.get("AZURE_HOST") + # Replace this with the primary/secondary key, AMLToken, or Microsoft Entra ID token for the endpoint + self.api_key = os.environ.get("AZURE_API_KEY") + if not self.api_key: + raise Exception("A key should be provided to invoke the endpoint") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + def close(self) -> None: + """Close the client.""" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {'Content-Type':'application/json', 'Authorization':('Bearer '+ self.api_key), 'azureml-model-deployment': 'duckdb-nsql-v2-phi-medium-1' } + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/score" + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return True + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": AzureEndpointClient.NAME, "engine": getattr(self, 'engine')} + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Args: + request: request params. + + Returns: + request params. + """ + # Format for chat model + request = copy.deepcopy(request) + prompt = request.pop("prompt") + data = {"input_data": {"input_string": [{"role": "user", "content": prompt}], "parameters": {"stop":"\n```", "max_tokens": 500}}} + + #body = str(str.encode(json.dumps(data))) + return super().preprocess_request_params(data) + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + new_choices = [] + response = copy.deepcopy(response) + if "output" in response: + new_choices.append({"text": response["output"]}) + else: + new_choices.append({"text": ""}) + response["choices"] = new_choices + return super().postprocess_response(response, request) diff --git a/duckdb-nsql/manifest/manifest/clients/azureopenai.py b/duckdb-nsql/manifest/manifest/clients/azureopenai.py new file mode 100644 index 0000000000000000000000000000000000000000..2bfb9849417dfcd64ac8c286f83dbdf494a98a11 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/azureopenai.py @@ -0,0 +1,113 @@ +"""Azure client.""" +import logging +import os +from typing import Any, Dict, Optional, Type + +from manifest.clients.openai import OPENAI_ENGINES, OpenAIClient +from manifest.request import LMRequest, Request + +logger = logging.getLogger(__name__) + +# Azure deployment name can only use letters and numbers, no spaces. Hyphens ("-") and +# underscores ("_") may be used, except as ending characters. We create this mapping to +# handle difference between Azure and OpenAI +AZURE_DEPLOYMENT_NAME_MAPPING = { + "gpt-3.5-turbo": "gpt-35-turbo", + "gpt-3.5-turbo-0301": "gpt-35-turbo-0301", +} +OPENAI_DEPLOYMENT_NAME_MAPPING = { + "gpt-35-turbo": "gpt-3.5-turbo", + "gpt-35-turbo-0301": "gpt-3.5-turbo-0301", +} + + +class AzureClient(OpenAIClient): + """Azure client.""" + + PARAMS = OpenAIClient.PARAMS + REQUEST_CLS: Type[Request] = LMRequest + NAME = "azureopenai" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the AzureOpenAI server. + + connection_str is passed as default AZURE_OPENAI_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key, self.host = None, None + if connection_str: + connection_parts = connection_str.split("::") + if len(connection_parts) == 1: + self.api_key = connection_parts[0] + elif len(connection_parts) == 2: + self.api_key, self.host = connection_parts + else: + raise ValueError( + "Invalid connection string. " + "Must be either AZURE_OPENAI_KEY or " + "AZURE_OPENAI_KEY::AZURE_OPENAI_ENDPOINT" + ) + self.api_key = self.api_key or os.environ.get("AZURE_OPENAI_KEY") + if self.api_key is None: + raise ValueError( + "AzureOpenAI API key not set. Set AZURE_OPENAI_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = self.host or os.environ.get("AZURE_OPENAI_ENDPOINT") + if self.host is None: + raise ValueError( + "Azure Service URL not set " + "(e.g. https://openai-azure-service.openai.azure.com/)." + " Set AZURE_OPENAI_ENDPOINT or pass through `client_connection`." + " as AZURE_OPENAI_KEY::AZURE_OPENAI_ENDPOINT" + ) + self.host = self.host.rstrip("/") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in OPENAI_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {OPENAI_ENGINES}." + ) + + def get_generation_url(self) -> str: + """Get generation URL.""" + engine = getattr(self, "engine") + deployment_name = AZURE_DEPLOYMENT_NAME_MAPPING.get(engine, engine) + return ( + self.host + + "/openai/deployments/" + + deployment_name + + "/completions?api-version=2023-05-15" + ) + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {"api-key": f"{self.api_key}"} + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + # IMPORTANT!!! + # Azure models are the same as openai models. So we want to unify their + # cached. Make sure we retrun the OpenAI name here. + return {"model_name": OpenAIClient.NAME, "engine": getattr(self, "engine")} diff --git a/duckdb-nsql/manifest/manifest/clients/azureopenai_chat.py b/duckdb-nsql/manifest/manifest/clients/azureopenai_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..19d8d76a223aa445bb645a66cd2ecc8e4c7e1010 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/azureopenai_chat.py @@ -0,0 +1,116 @@ +"""Azure client.""" +import logging +import os +from typing import Any, Dict, Optional + +from manifest.clients.openai_chat import OPENAICHAT_ENGINES, OpenAIChatClient +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + +# Azure deployment name can only use letters and numbers, no spaces. Hyphens ("-") and +# underscores ("_") may be used, except as ending characters. We create this mapping to +# handle difference between Azure and OpenAI +AZURE_DEPLOYMENT_NAME_MAPPING = { + "gpt-3.5-turbo": "gpt-35-turbo", + "gpt-3.5-turbo-0301": "gpt-35-turbo-0301", +} +OPENAI_DEPLOYMENT_NAME_MAPPING = { + "gpt-35-turbo": "gpt-3.5-turbo", + "gpt-35-turbo-0301": "gpt-3.5-turbo-0301", +} + + +class AzureChatClient(OpenAIChatClient): + """Azure chat client.""" + + # User param -> (client param, default value) + PARAMS = OpenAIChatClient.PARAMS + REQUEST_CLS = LMRequest + NAME = "azureopenaichat" + IS_CHAT = True + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the AzureOpenAI server. + + connection_str is passed as default AZURE_OPENAI_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key, self.host = None, None + if connection_str: + connection_parts = connection_str.split("::") + if len(connection_parts) == 1: + self.api_key = connection_parts[0] + elif len(connection_parts) == 2: + self.api_key, self.host = connection_parts + else: + raise ValueError( + "Invalid connection string. " + "Must be either AZURE_OPENAI_KEY or " + "AZURE_OPENAI_KEY::AZURE_OPENAI_ENDPOINT" + ) + self.api_key = self.api_key or os.environ.get("AZURE_OPENAI_KEY") + if self.api_key is None: + raise ValueError( + "AzureOpenAI API key not set. Set AZURE_OPENAI_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = self.host or os.environ.get("AZURE_OPENAI_ENDPOINT") + if self.host is None: + raise ValueError( + "Azure Service URL not set " + "(e.g. https://openai-azure-service.openai.azure.com/)." + " Set AZURE_OPENAI_ENDPOINT or pass through `client_connection`." + " as AZURE_OPENAI_KEY::AZURE_OPENAI_ENDPOINT" + ) + self.host = self.host.rstrip("/") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in OPENAICHAT_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. " + f"Must be {OPENAICHAT_ENGINES}." + ) + + def get_generation_url(self) -> str: + """Get generation URL.""" + engine = getattr(self, "engine") + deployment_name = AZURE_DEPLOYMENT_NAME_MAPPING.get(engine, engine) + return ( + self.host + + "/openai/deployments/" + + deployment_name + + "/chat/completions?api-version=2023-05-15" + ) + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {"api-key": f"{self.api_key}"} + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + # IMPORTANT!!! + # Azure models are the same as openai models. So we want to unify their + # cached. Make sure we retrun the OpenAI name here. + return {"model_name": OpenAIChatClient.NAME, "engine": getattr(self, "engine")} diff --git a/duckdb-nsql/manifest/manifest/clients/client.py b/duckdb-nsql/manifest/manifest/clients/client.py new file mode 100644 index 0000000000000000000000000000000000000000..8bd5ce2265bb8ebc0b631456dc3c98e5fb863dbf --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/client.py @@ -0,0 +1,699 @@ +"""Client class.""" +import asyncio +import copy +import json +import logging +import math +from abc import ABC, abstractmethod +from typing import Any, Dict, Generator, List, Optional, Tuple, Union, cast + +import aiohttp +import requests +import tqdm.asyncio +from tenacity import RetryCallState, retry, stop_after_attempt, wait_random_exponential + +from manifest.request import ( + DEFAULT_REQUEST_KEYS, + NOT_CACHE_KEYS, + LMChatRequest, + LMRequest, + LMScoreRequest, + Request, +) +from manifest.response import ( + RESPONSE_CONSTRUCTORS, + ArrayModelChoice, + LMModelChoice, + ModelChoices, + Response, + Usage, + Usages, +) + +logger = logging.getLogger(__name__) + +ATTEMPTS_BEFORE_STOP = 4 +ATTEMPTS_TIMEOUT = 30 +# http_status mainly for azure and e.code mainly for openai usage +# e.http_status == 408 occurs when Azure times out +# e.code == 429 rate lime +# e.code == 500 or 502 occurs when server error +API_ERROR_CODE = {408, 429, 500, 502, 520, 524} + + +def retry_if_ratelimit(retry_base: RetryCallState) -> bool: + """Return whether to retry if ratelimited.""" + try: + if isinstance(retry_base.outcome.exception(), requests.exceptions.HTTPError): + exception = cast( + requests.exceptions.HTTPError, retry_base.outcome.exception() + ) + # 500 is a server error, 429 is a rate limit error + if exception.response.status_code in API_ERROR_CODE: # type: ignore + return True + except Exception: + pass + return True + + +def return_error_response(retry_state: RetryCallState) -> dict: + """Return error response if all retries failed.""" + request_params = retry_state.args[1] + number_of_prompts = ( + len(request_params["prompt"]) + if "prompt" in request_params + else len(request_params["messages"]) + ) + return { + "choices": [], + "usage": { + "total_tokens": 0, + "prompt_tokens": 0, + "completion_tokens": 0, + }, + "errors": [str(retry_state.outcome.exception())] * number_of_prompts, + } + + +class Client(ABC): + """Client class.""" + + # Must be overridden by child class + PARAMS: Dict[str, Tuple[str, Any]] = {} + REQUEST_CLS = Request + NAME: str = None + IS_CHAT: bool = False + + def __init__( + self, connection_str: Optional[str] = None, client_args: Dict[str, Any] = {} + ): + """ + Initialize client. + + kwargs are passed to client as default parameters. + + For clients like OpenAI that do not require a connection, + the connection_str can be None. + + Args: + connection_str: connection string for client. + client_args: client arguments. + """ + self.connect(connection_str, client_args) + + @abstractmethod + def connect( + self, connection_str: Optional[str], client_args: Dict[str, Any] + ) -> None: + """ + Connect to client. + + Override in child client class. + Args: + connection_str: connection string. + """ + raise NotImplementedError() + + @abstractmethod + def close(self) -> None: + """Close the client. + + Override in child client class. + """ + raise NotImplementedError() + + @abstractmethod + def get_generation_url(self) -> str: + """Get generation URL. + + Override in child client class. + """ + raise NotImplementedError() + + @abstractmethod + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Override in child client class. + Returns: + header. + """ + raise NotImplementedError() + + @abstractmethod + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference. + + Override in child client class. + """ + raise NotImplementedError() + + @abstractmethod + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + raise NotImplementedError() + + @abstractmethod + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Override in child client class. + Returns: + model params. + """ + raise NotImplementedError() + + def get_tokenizer(self, model: str) -> Tuple[Any, int]: + """Get tokenizer for model. + + Override in child client class. Return None, -1 if not supported + or no prompt truncation required. + Returns: + tokenizer: tokenizer with encoder and decode + max_length: max length of model + """ + return None, -1 + + def get_model_inputs(self) -> List: + """ + Get allowable model inputs. + + Returns: + model inputs. + """ + return list(self.PARAMS.keys()) + + def split_usage(self, request: Dict, choices: List[str]) -> List[Dict[str, int]]: + """Split usage into list of usages for each prompt.""" + # TODO: add this in using default tokenizer + return [] + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Override in child client class to reformat requests to model. + + Args: + request: request params. + + Returns: + request params. + """ + return request + + def postprocess_response( + self, response: Dict[str, Any], request: Dict[str, Any] + ) -> Dict[str, Any]: + """ + Postprocess and validate response as dict. + + Override in child client class to reform model responses. + + Args: + response: response + request: request + + Return: + response as dict + """ + if "choices" not in response: + raise ValueError(f"Invalid response: {response}") + if "usage" in response: + # Handle splitting the usages for batch requests + if len(response["choices"]) == 1: + if isinstance(response["usage"], list): + response["usage"] = response["usage"][0] + response["usage"] = [response["usage"]] + else: + # Try to split usage + split_usage = self.split_usage(request, response["choices"]) + if split_usage: + response["usage"] = split_usage + return response + + def get_request( + self, prompt: Union[str, List[str]], request_args: Dict[str, Any] + ) -> Request: + """ + Parse model kwargs to request. + + Args: + prompt: prompt. + request_args: request arguments. + + Returns: + request. + """ + params = {"prompt": prompt} + # Adds default values from self.PARAMS if not in request_args + for key in self.PARAMS: + params[key] = request_args.pop(key, getattr(self, key)) + # Allows for overriding DEFAULT_REQUEST_KEYS even if they are not + # in self.PARAMS. Note that DEFAULT_REQUEST_KEYS match the default + # values in Request. + for key in DEFAULT_REQUEST_KEYS: + if key not in params and key in request_args: + params[key] = request_args.pop(key) + return self.REQUEST_CLS(**params) # type: ignore + + def _get_request_params(self, request: Request) -> Dict[str, Any]: + """Get request params. + + Add default keys that we need for requests such as batch_size. + We drop these before sending to the model. + """ + params_to_add = DEFAULT_REQUEST_KEYS.copy() + # This will override DEFAULT_REQUEST_KEYS with those in PARAMS + params_to_add.update(self.PARAMS) + # to_dict will handle parameter renaming but not any + # default value handling - that is done in get_request() + request_params = request.to_dict(params_to_add) + return request_params + + def get_cache_key(self, request: Request) -> Dict[str, Any]: + """Get cache key for request. + + Skip keys that are not cache keys such as batch_size. + """ + request_params = self._get_request_params(request) + for key in NOT_CACHE_KEYS: + request_params.pop(key, None) + # Make sure to add model params and request class + request_params.update(self.get_model_params()) + request_params["request_cls"] = request.__class__.__name__ + return request_params + + def _split_requests( + self, request_params: Dict[str, Any], batch_size: int, key: str = "prompt" + ) -> List[Dict[str, Any]]: + """Split request into batch_sized request. + + Args: + request_params: request params. + batch_size: batch size for requests. + key: key to batch over + + Returns: + list of request params. + """ + data = copy.deepcopy(request_params[key]) + data_size = len(request_params[key]) + request_params_list = [] + for i in range(0, data_size, batch_size): + params = copy.deepcopy(request_params) + params[key] = data[i] if batch_size == 1 else data[i : i + batch_size] + request_params_list.append(params) + return request_params_list + + def _get_model_choices(self, response: Dict) -> ModelChoices: + """Format response to ModelChoices.""" + # Array or text response + response_type = RESPONSE_CONSTRUCTORS[self.REQUEST_CLS]["response_type"] + if response_type == "array": + choices: List[Union[LMModelChoice, ArrayModelChoice]] = [ + ArrayModelChoice(**choice) for choice in response["choices"] + ] + else: + choices = [LMModelChoice(**choice) for choice in response["choices"]] + return ModelChoices(choices=choices) + + def _stitch_responses(self, request: Request, responses: List[Dict]) -> Response: + """Stitch responses together. + + Useful for batch requests. + """ + choices = [] + usages = [] + for res_dict in responses: + choices.extend(res_dict["choices"]) + if "usage" in res_dict: + usages.extend(res_dict["usage"]) + final_response_dict = {"choices": choices} + final_usages = None + if usages: + final_usages = Usages(usages=[Usage(**usage) for usage in usages]) + # TODO: Add usage based on tokenizer + return Response( + self._get_model_choices(final_response_dict), + cached=False, + request=request, + usages=final_usages, + **RESPONSE_CONSTRUCTORS[self.REQUEST_CLS], # type: ignore + ) + + def _verify_request_lengths( + self, request: Dict[str, Any], model: str, max_tokens: int + ) -> None: + """Verify that the request length is not too long.""" + encoder, max_length = self.get_tokenizer(model) + if not encoder or max_length < 0: + return + if isinstance(request["prompt"], str): + prompts = [request["prompt"]] + else: + prompts = request["prompt"] + for i in range(len(prompts)): + prompt = prompts[i] + encoded_prompt = encoder.encode(prompt) + if len(encoded_prompt) + max_tokens > max_length: + logger.warning( + f"Prompt {prompt} is too long for model {model}. " + "Truncating prompt from left." + ) + # -20 to be safe + prompt = encoder.decode( + encoded_prompt[-int(max_length - max_tokens - 20) :] + ) + prompts[i] = prompt + if isinstance(request["prompt"], str): + request["prompt"] = prompts[0] + else: + request["prompt"] = prompts + + @retry( + reraise=True, + wait=wait_random_exponential(min=1, max=ATTEMPTS_TIMEOUT), + stop=stop_after_attempt(ATTEMPTS_BEFORE_STOP), + ) + def _run_completion( + self, request_params: Dict[str, Any], retry_timeout: int + ) -> Dict: + """Execute completion request. + + Args: + request_params: request params. + retry_timeout: retry timeout. + + Returns: + response as dict. + """ + request_params = self.preprocess_request_params(request_params) + print(request_params) + post_str = self.get_generation_url() + res = requests.post( + post_str, + headers=self.get_generation_header(), + json=request_params, + timeout=retry_timeout, + ) + try: + res.raise_for_status() + except requests.exceptions.HTTPError as e: + logger.warning( + str(e) + ) + raise Exception() + return self.postprocess_response(res.json(), request_params) + + @retry( + reraise=True, + retry=retry_if_ratelimit, + wait=wait_random_exponential(min=1, max=ATTEMPTS_TIMEOUT), + stop=stop_after_attempt(ATTEMPTS_BEFORE_STOP), + ) + async def _arun_completion( + self, request_params: Dict[str, Any], retry_timeout: int + ) -> Dict: + """Async execute completion request. + + Args: + request_params: request params. + retry_timeout: retry timeout. + + Returns: + response as dict. + """ + request_params = self.preprocess_request_params(request_params) + post_str = self.get_generation_url() + async with aiohttp.ClientSession(timeout=retry_timeout) as session: + async with session.post( + post_str, + headers=self.get_generation_header(), + json=request_params, + timeout=retry_timeout, + ) as res: + res.raise_for_status() + res_json = await res.json(content_type=None) + return self.postprocess_response(res_json, request_params) + + @retry( + reraise=True, + retry=retry_if_ratelimit, + wait=wait_random_exponential(min=1, max=ATTEMPTS_TIMEOUT), + stop=stop_after_attempt(ATTEMPTS_BEFORE_STOP), + ) + def _run_streaming_completion( + self, request_params: Dict[str, Any], retry_timeout: int + ) -> Generator[Dict, None, None]: + """Execute completion request streaming. + + Args: + request_params: request params. + retry_timeout: retry timeout. + + Returns: + response as dict. + """ + request_params = self.preprocess_request_params(request_params) + request_params["stream"] = True + post_str = self.get_generation_url() + res_iter = requests.post( + post_str, + headers=self.get_generation_header(), + json=request_params, + timeout=retry_timeout, + stream=True, + ) + for res_token in res_iter.iter_lines(): + if res_token: + decoded_res_token = res_token.decode("utf-8") + decoded_res_token = decoded_res_token.replace("data: ", "") + if decoded_res_token == "[DONE]": + break + try: + decoded_res_token_dct = json.loads(decoded_res_token) + postprocess_res_token_dct = self.postprocess_response( + decoded_res_token_dct, request_params + ) + # If nothing is returned, skip + if ( + not postprocess_res_token_dct + or not postprocess_res_token_dct["choices"] + ): + continue + yield postprocess_res_token_dct + except Exception as e: + raise e + + def run_request(self, request: Request) -> Response: + """ + Run request. + + Args: + request: request. + + Returns: + response. + """ + # Make everything list for consistency + if isinstance(request.prompt, list): + prompt_list = request.prompt + else: + prompt_list = [request.prompt] + + request_params = self._get_request_params(request) + # Set the params as a list. Do not set the request + # object itself as the cache will then store it as a + # list which is inconsistent with the request input. + request_params["prompt"] = prompt_list + + # If batch_size is not set, set it to 1 + batch_size = request_params.pop("batch_size") or 1 + if not self.supports_batch_inference() and batch_size != 1: + logger.warning( + f"{self.__class__.__name__} does not support batch inference." + " Setting batch size to 1" + ) + batch_size = 1 + + # Take the default keys we need and drop the rest as they + # are not part of the model request. + retry_timeout = request_params.pop("client_timeout") + for key in DEFAULT_REQUEST_KEYS: + request_params.pop(key, None) + + # Make sure requests are in the request length + # If no tokenizer is set or not LM request, this + # will do nothing + if isinstance(request, LMRequest): + self._verify_request_lengths( + request_params, model=request.engine, max_tokens=request.max_tokens + ) + + # Batch requests + num_batches = len(prompt_list) // batch_size + if len(prompt_list) % batch_size != 0: + batch_size = int(math.ceil(len(prompt_list) / (num_batches + 1))) + request_batches = self._split_requests(request_params, batch_size) + + response_dicts = [ + self._run_completion(batch, retry_timeout) for batch in request_batches + ] + # Flatten responses + return self._stitch_responses(request, response_dicts) + + async def arun_batch_request( + self, request: Request, verbose: bool = False + ) -> Response: + """ + Run async request. + + Args: + request: request.s + + Returns: + response. + """ + required_batch_size = None + if not self.supports_batch_inference(): + required_batch_size = 1 + if not isinstance(request.prompt, list): + raise AssertionError( + "request.prompt must be a list for async batch inference." + ) + + request_params = self._get_request_params(request) + # Take the default keys we need and drop the rest as they + # are not part of the model request. + retry_timeout = request_params.pop("client_timeout") + batch_size = request_params.pop("batch_size") + batch_size = required_batch_size or batch_size + for key in DEFAULT_REQUEST_KEYS: + request_params.pop(key, None) + + # Make sure requests are in the request length + # If no tokenizer is set or not LM request, this + # will do nothing + if isinstance(request, LMRequest): + self._verify_request_lengths( + request_params, model=request.engine, max_tokens=request.max_tokens + ) + + # Batch requests + num_batches = len(request.prompt) // batch_size + if len(request.prompt) % batch_size != 0: + batch_size = int(math.ceil(len(request.prompt) / (num_batches + 1))) + + request_batches = self._split_requests(request_params, batch_size) + all_tasks = [ + asyncio.create_task(self._arun_completion(batch, retry_timeout)) + for batch in request_batches + ] + responses = await tqdm.asyncio.tqdm.gather(*all_tasks, disable=not verbose) + # Flatten responses + return self._stitch_responses(request, responses) + + def run_chat_request( + self, + request: LMChatRequest, + ) -> Response: + """ + Get the response from chat model. + + Args: + request: request. + + Returns: + response. + """ + request_params = self._get_request_params(request) + # Take the default keys we need and drop the rest as they + # are not part of the model request. + retry_timeout = request_params.pop("client_timeout") + for key in DEFAULT_REQUEST_KEYS: + request_params.pop(key, None) + + # Make sure requests are in the request length + # If no tokenizer is set or not LM request, this + # will do nothing + self._verify_request_lengths( + request_params, model=request.engine, max_tokens=request.max_tokens + ) + + response_dict = self._run_completion(request_params, retry_timeout) + usages = None + if "usage" in response_dict: + usages = [Usage(**usage) for usage in response_dict["usage"]] + + return Response( + response=self._get_model_choices(response_dict), + cached=False, + request=request, + usages=Usages(usages=usages) if usages else None, + **RESPONSE_CONSTRUCTORS[LMChatRequest], # type: ignore + ) + + def run_streaming_request( + self, request: Request + ) -> Generator[Response, None, None]: + """ + Run streaming request. + + Args: + request: request. + + Returns: + response. + """ + if not isinstance(request.prompt, str): + raise ValueError("Streaming requests must have a single prompt.") + if not self.supports_streaming_inference(): + raise ValueError( + f"{self.__class__.__name__} does not support streaming inference." + ) + request_params = self._get_request_params(request) + + # Take the default keys we need and drop the rest as they + # are not part of the model request. + retry_timeout = request_params.pop("client_timeout") + for key in DEFAULT_REQUEST_KEYS: + request_params.pop(key, None) + + # Make sure requests are in the request length + # If no tokenizer is set or not LM request, this + # will do nothing + if isinstance(request, LMRequest): + self._verify_request_lengths( + request_params, model=request.engine, max_tokens=request.max_tokens + ) + + for token_response in self._run_streaming_completion( + request_params, retry_timeout + ): + yield self._stitch_responses(request, [token_response]) + + def run_score_prompt_request( + self, + request: LMScoreRequest, + ) -> Response: + """ + Get the logit score of the prompt via a forward pass of the model. + + Args: + request: request. + + Returns: + response. + """ + raise NotImplementedError( + f"{self.__class__.__name__} does not support prompt scoring request." + ) diff --git a/duckdb-nsql/manifest/manifest/clients/cohere.py b/duckdb-nsql/manifest/manifest/clients/cohere.py new file mode 100644 index 0000000000000000000000000000000000000000..b2192ec358cdb27b502de785ca4021bf03d7a8e3 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/cohere.py @@ -0,0 +1,125 @@ +"""Cohere client.""" + +import logging +import os +from typing import Any, Dict, Optional + +from manifest.clients.client import Client +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + +COHERE_MODELS = {"small", "medium", "large", "xlarge"} + + +class CohereClient(Client): + """Cohere client.""" + + # Params are defined in https://docs.cohere.ai/generate-reference + PARAMS = { + "engine": ("model", "xlarge"), + "max_tokens": ("max_tokens", 20), + "temperature": ("temperature", 0.75), + "n": ("num_generations", 1), + "top_k": ("k", 0), + "top_p": ("p", 0.75), + "frequency_penalty": ("frequency_penalty", 0.0), + "presence_penalty": ("presence_penalty", 0.0), + "stop_sequences": ("stop_sequences", None), + } + REQUEST_CLS = LMRequest + NAME = "cohere" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the Cohere server. + + connection_str is passed as default COHERE_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key = connection_str or os.environ.get("COHERE_API_KEY") + if self.api_key is None: + raise ValueError( + "Cohere API key not set. Set COHERE_API_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = "https://api.cohere.ai" + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in COHERE_MODELS: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {COHERE_MODELS}." + ) + + def close(self) -> None: + """Close the client.""" + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/generate" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return { + "Cohere-Version": "2021-11-08", + "Authorization": f"Bearer {self.api_key}", + } + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + return { + "object": "text_completion", + "model": getattr(self, "engine"), + "choices": [ + { + "text": item["text"], + "text_logprob": item.get("likelihood", None), + "token_logprobs": item.get("token_likelihoods", None), + } + for item in response["generations"] + ], + } diff --git a/duckdb-nsql/manifest/manifest/clients/diffuser.py b/duckdb-nsql/manifest/manifest/clients/diffuser.py new file mode 100644 index 0000000000000000000000000000000000000000..551c3c149a326554886a73b6f65dfc6ffdcf3196 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/diffuser.py @@ -0,0 +1,112 @@ +"""Diffuser client.""" +import logging +from functools import lru_cache +from typing import Any, Dict, Optional + +import numpy as np +import requests + +from manifest.clients.client import Client +from manifest.request import DiffusionRequest + +logger = logging.getLogger(__name__) + + +class DiffuserClient(Client): + """Diffuser client.""" + + # User param -> (client param, default value) + PARAMS = { + "num_inference_steps": ("num_inference_steps", 50), + "height": ("height", 512), + "width": ("width", 512), + "n": ("num_images_per_prompt", 1), + "guidance_scale": ("guidance_scale", 7.5), + "eta": ("eta", 0.0), + } + REQUEST_CLS = DiffusionRequest + NAME = "diffuser" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the Diffuser url. + + Arsg: + connection_str: connection string. + client_args: client arguments. + """ + self.host = connection_str.rstrip("/") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + self.model_params = self.get_model_params() + + def to_numpy(self, image: np.ndarray) -> np.ndarray: + """Convert a numpy image to a PIL image. + + Adapted from https://github.com/huggingface/diffusers/blob/src/diffusers/pipelines/pipeline_utils.py#L808 # noqa: E501 + """ + image = (image * 255).round().astype("uint8") + return image + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/completions" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + @lru_cache(maxsize=1) + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + res = requests.post(self.host + "/params").json() + res["client_name"] = self.NAME + return res + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + # Convert array to np.array + for choice in response["choices"]: + choice["array"] = self.to_numpy(np.array(choice["array"])) + return response diff --git a/duckdb-nsql/manifest/manifest/clients/dummy.py b/duckdb-nsql/manifest/manifest/clients/dummy.py new file mode 100644 index 0000000000000000000000000000000000000000..81bc23ddc5d3945c01ff083a0269a8b161a3fb6e --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/dummy.py @@ -0,0 +1,251 @@ +"""Dummy client.""" +import hashlib +import logging +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np +import tiktoken + +from manifest.clients.client import Client +from manifest.request import LMChatRequest, LMRequest, LMScoreRequest, Request +from manifest.response import LMModelChoice, ModelChoices, Response, Usage, Usages + +logger = logging.getLogger(__name__) + + +class DummyClient(Client): + """Dummy client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "text-davinci-003"), + "temperature": ("temperature", 0.0), + "max_tokens": ("max_tokens", 10), + "n": ("n", 1), + "top_p": ("top_p", 1.0), + "top_k": ("best_of", 1), + "batch_size": ("batch_size", 20), + } + REQUEST_CLS = LMRequest + NAME = "dummy" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to dummpy server. + + This is a dummy client that returns identity responses. Used for testing. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + # We tiktoken as it is faster than HF for tokenizing + # Use any model to create the tokenizer + self.encoder = tiktoken.get_encoding("cl100k_base") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return "dummy" + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {} + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"engine": "dummy", "model": getattr(self, "engine")} + + def get_mock_output( + self, output_toks: int, is_completion: bool, seed: Optional[int] = None + ) -> LMModelChoice: + """Return mock model output by generating random tokens.""" + np.random.seed(seed) + random_tokens = np.random.randint( + 0, self.encoder.max_token_value + 1, output_toks + ) + response = self.encoder.decode(random_tokens) # type: ignore + if is_completion: + np.random.seed(seed) + random_logprobs = np.random.uniform( + low=-2, high=-0.00001, size=output_toks + ).tolist() + else: + # Return all Nones to mimic chat models + # OpenAI chat models do not return logprobs + random_logprobs = [None] * output_toks + return LMModelChoice( + text=response, + token_logprobs=random_logprobs, + tokens=random_tokens.tolist(), + ) + + def get_mock_choices( + self, + prompt_list: List[str], + request_params: Dict, + is_completion: bool, + ) -> Tuple[List[LMModelChoice], List[Usage]]: + """Get choices and usages of mock output.""" + choices = [] + usages = [] + for prompt in prompt_list: + num_prompt_tokens = len(self.encoder.encode(prompt)) + if request_params["temperature"] == 0: + # Get integer seed from hash of prompt + seed = ( + int(hashlib.sha256(prompt.encode("utf-8")).hexdigest(), 16) + % 10**8 + ) + else: + # Get random seed + seed = None + for _ in range(int(request_params["n"])): + choice = self.get_mock_output( + request_params["max_tokens"], is_completion=is_completion, seed=seed + ) + choices.append(choice) + usages.append( + Usage( + prompt_tokens=num_prompt_tokens, + completion_tokens=request_params["max_tokens"], + total_tokens=num_prompt_tokens + request_params["max_tokens"], + ) + ) + return choices, usages + + def run_request(self, request: Request) -> Response: + """ + Get request string function. + + Args: + request: request. + + Returns: + request function that takes no input. + request parameters as dict. + """ + if isinstance(request.prompt, list): + prompt_list = request.prompt + else: + prompt_list = [request.prompt] + request_params = request.to_dict(self.PARAMS) + + choices, usages = self.get_mock_choices( + prompt_list, request_params, is_completion=True + ) + return Response( + response=ModelChoices(choices=choices), # type: ignore + cached=False, + request=request, + usages=Usages(usages=usages), + response_type="text", + request_type=self.REQUEST_CLS, + ) + + async def arun_batch_request( + self, request: Request, verbose: bool = False + ) -> Response: + """ + Get async request string function. + + Args: + request: request. + + Returns: + response. + """ + return self.run_request(request) + + def run_chat_request( + self, + request: LMChatRequest, + ) -> Response: + """ + Get the response from chat model. + + Args: + request: request. + + Returns: + response. + """ + prompt_list = ["_".join(pmp["content"] for pmp in request.prompt)] + request_params = request.to_dict(self.PARAMS) + + choices, usages = self.get_mock_choices( + prompt_list, request_params, is_completion=False + ) + return Response( + response=ModelChoices(choices=choices), # type: ignore + cached=False, + request=request, + usages=Usages(usages=usages), + response_type="text", + request_type=LMChatRequest, + ) + + def run_score_prompt_request( + self, + request: LMScoreRequest, + ) -> Response: + """ + Get the logit score of the prompt via a forward pass of the model. + + Args: + request: request. + + Returns: + request function that takes no input. + request parameters as dict. + """ + if isinstance(request.prompt, list): + prompt_list = request.prompt + else: + prompt_list = [request.prompt] + request_params = request.to_dict(self.PARAMS) + + choices, usages = self.get_mock_choices( + prompt_list, request_params, is_completion=True + ) + return Response( + response=ModelChoices(choices=choices), # type: ignore + cached=False, + request=request, + usages=Usages(usages=usages), + response_type="text", + request_type=LMScoreRequest, + ) diff --git a/duckdb-nsql/manifest/manifest/clients/google.py b/duckdb-nsql/manifest/manifest/clients/google.py new file mode 100644 index 0000000000000000000000000000000000000000..a5b724b4c1782af9458c08b5a3ae2616febb22f1 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/google.py @@ -0,0 +1,197 @@ +"""Google client.""" +import logging +import os +import subprocess +from typing import Any, Dict, Optional, Type + +from manifest.clients.client import Client +from manifest.request import LMRequest, Request + +logger = logging.getLogger(__name__) + +# https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/api-quickstart +GOOGLE_ENGINES = { + "text-bison", +} + + +def get_project_id() -> Optional[str]: + """Get project ID. + + Run + `gcloud config get-value project` + """ + try: + project_id = subprocess.run( + ["gcloud", "config", "get-value", "project"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + if project_id.stderr.decode("utf-8").strip(): + return None + return project_id.stdout.decode("utf-8").strip() + except Exception: + return None + + +class GoogleClient(Client): + """Google client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "text-bison"), + "temperature": ("temperature", 1.0), + "max_tokens": ("maxOutputTokens", 10), + "top_p": ("topP", 1.0), + "top_k": ("topK", 1), + "batch_size": ("batch_size", 20), + } + REQUEST_CLS: Type[Request] = LMRequest + NAME = "google" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the GoogleVertex API. + + connection_str is passed as default GOOGLE_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + connection_parts = connection_str.split("::") + if len(connection_parts) == 1: + self.api_key = connection_parts[0] + self.project_id = None + elif len(connection_parts) == 2: + self.api_key, self.project_id = connection_parts + else: + raise ValueError( + "Invalid connection string. " + "Must be either API_KEY or API_KEY::PROJECT_ID" + ) + self.api_key = self.api_key or os.environ.get("GOOGLE_API_KEY") + if self.api_key is None: + raise ValueError( + "GoogleVertex API key not set. Set GOOGLE_API_KEY environment " + "variable or pass through `client_connection`. This can be " + "found by running `gcloud auth print-access-token`" + ) + self.project_id = ( + self.project_id or os.environ.get("GOOGLE_PROJECT_ID") or get_project_id() + ) + if self.project_id is None: + raise ValueError("GoogleVertex project ID not set. Set GOOGLE_PROJECT_ID") + self.host = f"https://us-central1-aiplatform.googleapis.com/v1/projects/{self.project_id}/locations/us-central1/publishers/google/models" # noqa: E501 + + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in GOOGLE_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {GOOGLE_ENGINES}." + ) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + model = getattr(self, "engine") + return self.host + f"/{model}:predict" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {"Authorization": f"Bearer {self.api_key}"} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Args: + request: request params. + + Returns: + request params. + """ + # Refortmat the request params for google + prompt = request.pop("prompt") + if isinstance(prompt, str): + prompt_list = [prompt] + else: + prompt_list = prompt + google_request = { + "instances": [{"prompt": prompt} for prompt in prompt_list], + "parameters": request, + } + return super().preprocess_request_params(google_request) + + def postprocess_response( + self, response: Dict[str, Any], request: Dict[str, Any] + ) -> Dict[str, Any]: + """ + Validate response as dict. + + Assumes response is dict + { + "predictions": [ + { + "safetyAttributes": { + "categories": ["Violent", "Sexual"], + "blocked": false, + "scores": [0.1, 0.1] + }, + "content": "SELECT * FROM "WWW";" + } + ] + } + + Args: + response: response + request: request + + Return: + response as dict + """ + google_predictions = response.pop("predictions") + new_response = { + "choices": [ + { + "text": prediction["content"], + } + for prediction in google_predictions + ] + } + return super().postprocess_response(new_response, request) diff --git a/duckdb-nsql/manifest/manifest/clients/google_chat.py b/duckdb-nsql/manifest/manifest/clients/google_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..526160629b43f40f725eeca46a447a120f114139 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/google_chat.py @@ -0,0 +1,155 @@ +"""Google client.""" +import copy +import logging +import os +from typing import Any, Dict, Optional, Type + +from manifest.clients.google import GoogleClient, get_project_id +from manifest.request import LMRequest, Request + +logger = logging.getLogger(__name__) + +# https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/api-quickstart +GOOGLE_ENGINES = { + "chat-bison", +} + + +class GoogleChatClient(GoogleClient): + """GoogleChat client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "chat-bison"), + "temperature": ("temperature", 1.0), + "max_tokens": ("maxOutputTokens", 10), + "top_p": ("topP", 1.0), + "top_k": ("topK", 1), + "batch_size": ("batch_size", 20), + } + REQUEST_CLS: Type[Request] = LMRequest + NAME = "googlechat" + IS_CHAT = True + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the GoogleVertex API. + + connection_str is passed as default GOOGLE_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + connection_parts = connection_str.split("::") + if len(connection_parts) == 1: + self.api_key = connection_parts[0] + elif len(connection_parts) == 2: + self.api_key, self.project_id = connection_parts + else: + raise ValueError( + "Invalid connection string. " + "Must be either API_KEY or API_KEY::PROJECT_ID" + ) + self.api_key = self.api_key or os.environ.get("GOOGLE_API_KEY") + if self.api_key is None: + raise ValueError( + "GoogleVertex API key not set. Set GOOGLE_API_KEY environment " + "variable or pass through `client_connection`. This can be " + "found by running `gcloud auth print-access-token`" + ) + self.project_id = ( + self.project_id or os.environ.get("GOOGLE_PROJECT_ID") or get_project_id() + ) + if self.project_id is None: + raise ValueError("GoogleVertex project ID not set. Set GOOGLE_PROJECT_ID") + self.host = f"https://us-central1-aiplatform.googleapis.com/v1/projects/{self.project_id}/locations/us-central1/publishers/google/models" # noqa: E501 + + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in GOOGLE_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {GOOGLE_ENGINES}." + ) + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Args: + request: request params. + + Returns: + request params. + """ + # Format for chat model + request = copy.deepcopy(request) + prompt = request.pop("prompt") + if isinstance(prompt, str): + messages = [{"author": "user", "content": prompt}] + elif isinstance(prompt, list) and isinstance(prompt[0], str): + prompt_list = prompt + messages = [{"author": "user", "content": prompt} for prompt in prompt_list] + elif isinstance(prompt, list) and isinstance(prompt[0], dict): + for pmt_dict in prompt: + if "author" not in pmt_dict or "content" not in pmt_dict: + raise ValueError( + "Prompt must be list of dicts with 'author' and 'content' " + f"keys. Got {prompt}." + ) + messages = prompt + else: + raise ValueError( + "Prompt must be string, list of strings, or list of dicts." + f"Got {prompt}" + ) + new_request = { + "instances": [{"messages": messages}], + "parameters": request, + } + return super(GoogleClient, self).preprocess_request_params(new_request) + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Validate response as dict. + + Assumes response is dict + { + "candidates": [ + { + "safetyAttributes": { + "categories": ["Violent", "Sexual"], + "blocked": false, + "scores": [0.1, 0.1] + }, + "author": "1", + "content": "SELECT * FROM "WWW";" + } + ] + } + + Args: + response: response + request: request + + Return: + response as dict + """ + google_predictions = response.pop("predictions") + new_response = { + "choices": [ + { + "text": prediction["candidates"][0]["content"], + } + for prediction in google_predictions + ] + } + return super(GoogleClient, self).postprocess_response(new_response, request) diff --git a/duckdb-nsql/manifest/manifest/clients/huggingface.py b/duckdb-nsql/manifest/manifest/clients/huggingface.py new file mode 100644 index 0000000000000000000000000000000000000000..11e56ffb90c2ee9b80260617019f0884e469d3dd --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/huggingface.py @@ -0,0 +1,137 @@ +"""Hugging Face client.""" +import logging +from functools import lru_cache +from typing import Any, Dict, Optional + +import requests + +from manifest.clients.client import Client +from manifest.request import DEFAULT_REQUEST_KEYS, LMRequest, LMScoreRequest +from manifest.response import LMModelChoice, ModelChoices, Response + +logger = logging.getLogger(__name__) + + +class HuggingFaceClient(Client): + """HuggingFace client.""" + + # User param -> (client param, default value) + PARAMS = { + "temperature": ("temperature", 0.1), + "max_tokens": ("max_tokens", 10), + "n": ("n", 1), + "top_p": ("top_p", 1.0), + "top_k": ("top_k", 50), + "repetition_penalty": ("repetition_penalty", 1.0), + "do_sample": ("do_sample", True), + } + REQUEST_CLS = LMRequest + NAME = "huggingface" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the HuggingFace url. + + Arsg: + connection_str: connection string. + client_args: client arguments. + """ + if not connection_str: + raise ValueError("Must provide connection string") + self.host = connection_str.rstrip("/") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/completions" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + @lru_cache(maxsize=1) + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + res = requests.post(self.host + "/params").json() + res["client_name"] = self.NAME + return res + + def run_score_prompt_request( + self, + request: LMScoreRequest, + ) -> Response: + """ + Get the logit score of the prompt via a forward pass of the model. + + Args: + request: request. + + Returns: + request function that takes no input. + request parameters as dict. + """ + request_params = self._get_request_params(request) + retry_timeout = request_params.pop("client_timeout") + for key in DEFAULT_REQUEST_KEYS: + request_params.pop(key, None) + # Do not add params like we do with request as the model isn't sampling + request_params = {"prompt": request.prompt} + + post_str = self.host + "/score_sequence" + try: + res = requests.post( + post_str, + json=request_params, + timeout=retry_timeout, + ) + res.raise_for_status() + except requests.Timeout as e: + logger.error("HF request timed out. Increase client_timeout.") + raise e + except requests.exceptions.HTTPError as e: + logger.error(res.text) + raise e + response_dict = res.json() + return Response( + response=ModelChoices( + choices=[LMModelChoice(**choice) for choice in response_dict["choices"]] + ), + cached=False, + request=request, + usages=None, + response_type="text", + request_type=LMScoreRequest, + ) diff --git a/duckdb-nsql/manifest/manifest/clients/huggingface_embedding.py b/duckdb-nsql/manifest/manifest/clients/huggingface_embedding.py new file mode 100644 index 0000000000000000000000000000000000000000..9478355cb350b8c6cebe6f82e74e530026259352 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/huggingface_embedding.py @@ -0,0 +1,98 @@ +"""Hugging Face client.""" +import logging +from functools import lru_cache +from typing import Any, Dict, Optional, Tuple + +import numpy as np +import requests + +from manifest.clients.client import Client +from manifest.request import EmbeddingRequest + +logger = logging.getLogger(__name__) + + +class HuggingFaceEmbeddingClient(Client): + """HuggingFaceEmbedding client.""" + + # User param -> (client param, default value) + PARAMS: Dict[str, Tuple[str, Any]] = {} + REQUEST_CLS = EmbeddingRequest + NAME = "huggingfaceembedding" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the HuggingFace url. + + Arsg: + connection_str: connection string. + client_args: client arguments. + """ + if not connection_str: + raise ValueError("Must provide connection string") + self.host = connection_str.rstrip("/") + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/embed" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + @lru_cache(maxsize=1) + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + res = requests.post(self.host + "/params").json() + res["client_name"] = self.NAME + return res + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + # Convert array to np.array + for choice in response["choices"]: + choice["array"] = np.array(choice["array"]) + return response diff --git a/duckdb-nsql/manifest/manifest/clients/openai.py b/duckdb-nsql/manifest/manifest/clients/openai.py new file mode 100644 index 0000000000000000000000000000000000000000..ef160793af369abdde57d8a2e0da6600bbe6df37 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/openai.py @@ -0,0 +1,162 @@ +"""OpenAI client.""" +import logging +import os +from typing import Any, Dict, List, Optional, Type + +import tiktoken + +from manifest.clients.client import Client +from manifest.request import LMRequest, Request + +logger = logging.getLogger(__name__) + +OPENAI_ENGINES = { + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "davinci", + "curie", + "ada", + "babbage", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + "code-davinci-002", + "code-cushman-001", +} + + +class OpenAIClient(Client): + """OpenAI client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "text-davinci-003"), + "temperature": ("temperature", 1.0), + "max_tokens": ("max_tokens", 10), + "n": ("n", 1), + "top_p": ("top_p", 1.0), + "top_k": ("best_of", 1), + "logprobs": ("logprobs", None), + "stop_sequences": ("stop", None), # OpenAI doesn't like empty lists + "presence_penalty": ("presence_penalty", 0.0), + "frequency_penalty": ("frequency_penalty", 0.0), + "batch_size": ("batch_size", 20), + } + REQUEST_CLS: Type[Request] = LMRequest + NAME = "openai" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the OpenAI server. + + connection_str is passed as default OPENAI_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key = connection_str or os.environ.get("OPENAI_API_KEY") + if self.api_key is None: + raise ValueError( + "OpenAI API key not set. Set OPENAI_API_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = "https://api.openai.com/v1" + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in OPENAI_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. Must be {OPENAI_ENGINES}." + ) + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/completions" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {"Authorization": f"Bearer {self.api_key}"} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return True + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Validate response as dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + validated_response = super().postprocess_response(response, request) + # Handle logprobs + for choice in validated_response["choices"]: + if "logprobs" in choice: + logprobs = choice.pop("logprobs") + if logprobs and "token_logprobs" in logprobs: + choice["token_logprobs"] = logprobs["token_logprobs"] + choice["tokens"] = logprobs["tokens"] + return validated_response + + def split_usage(self, request: Dict, choices: List[str]) -> List[Dict[str, int]]: + """Split usage into list of usages for each prompt.""" + try: + encoding = tiktoken.encoding_for_model(getattr(self, "engine")) + except Exception: + return [] + prompt = request["prompt"] + # If n > 1 and prompt is a string, we need to split it into a list + if isinstance(prompt, str): + prompts = [prompt] * len(choices) + else: + prompts = prompt + assert len(prompts) == len(choices) + usages = [] + for pmt, chc in zip(prompts, choices): + pmt_tokens = len(encoding.encode(pmt)) + chc_tokens = len(encoding.encode(chc["text"])) # type: ignore + usage = { + "prompt_tokens": pmt_tokens, + "completion_tokens": chc_tokens, + "total_tokens": pmt_tokens + chc_tokens, + } + usages.append(usage) + return usages diff --git a/duckdb-nsql/manifest/manifest/clients/openai_chat.py b/duckdb-nsql/manifest/manifest/clients/openai_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..0f0395db6d6c7e1932ae16241c64e2f3dd73f238 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/openai_chat.py @@ -0,0 +1,157 @@ +"""OpenAIChat client.""" +import copy +import logging +import os +from typing import Any, Dict, Optional +import time + +from manifest.clients.openai import OpenAIClient +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + +# List from https://platform.openai.com/docs/models/model-endpoint-compatibility +OPENAICHAT_ENGINES = { + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-4", + "gpt-4o", + "gpt-4o-mini", + "gpt-4-32k", + "gpt-4-1106-preview", + "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + "ft:gpt-4o-mini-2024-07-18:motherduck:duckdb-nsql-v2-s:9pmS9qHq", + "gpt-4o-mini-2024-07-18" +} + + +class OpenAIChatClient(OpenAIClient): + """OpenAI Chat client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "gpt-3.5-turbo"), + "temperature": ("temperature", 1.0), + "max_tokens": ("max_tokens", 10), + "n": ("n", 1), + "top_p": ("top_p", 1.0), + "stop_sequences": ("stop", None), # OpenAI doesn't like empty lists + "presence_penalty": ("presence_penalty", 0.0), + "frequency_penalty": ("frequency_penalty", 0.0), + "batch_size": ("batch_size", 1), + } + REQUEST_CLS = LMRequest + NAME = "openaichat" + IS_CHAT = True + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the OpenAI server. + + connection_str is passed as default OPENAI_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key = connection_str or os.environ.get("OPENAI_API_KEY") + if self.api_key is None: + raise ValueError( + "OpenAI API key not set. Set OPENAI_API_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = "https://api.openai.com/v1" + #self.host = "https://api.together.xyz/v1/" + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in OPENAICHAT_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. " + f"Must be {OPENAICHAT_ENGINES}." + ) + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/chat/completions" + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Args: + request: request params. + + Returns: + request params. + """ + # sleep to stay within rate limit + #time.sleep(2) + + # Format for chat model + request = copy.deepcopy(request) + prompt = request.pop("prompt") + if isinstance(prompt, str): + messages = [{"role": "user", "content": prompt}] + elif isinstance(prompt, list) and isinstance(prompt[0], str): + prompt_list = prompt + messages = [{"role": "user", "content": prompt} for prompt in prompt_list] + elif isinstance(prompt, list) and isinstance(prompt[0], dict): + for pmt_dict in prompt: + if "role" not in pmt_dict or "content" not in pmt_dict: + raise ValueError( + "Prompt must be list of dicts with 'role' and 'content' " + f"keys. Got {prompt}." + ) + messages = prompt + else: + raise ValueError( + "Prompt must be string, list of strings, or list of dicts." + f"Got {prompt}" + ) + request["messages"] = [{"role": "system", "content": "You are a DuckDB SQL generator"}] + messages + return super().preprocess_request_params(request) + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Postprocess and validate response as dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + new_choices = [] + response = copy.deepcopy(response) + for message in response["choices"]: + if "delta" in message: + # This is a streaming response + if "content" in message["delta"]: + new_choices.append({"text": message["delta"]["content"]}) + else: + new_choices.append({"text": message["message"]["content"]}) + response["choices"] = new_choices + return super().postprocess_response(response, request) diff --git a/duckdb-nsql/manifest/manifest/clients/openai_embedding.py b/duckdb-nsql/manifest/manifest/clients/openai_embedding.py new file mode 100644 index 0000000000000000000000000000000000000000..27c116f08913993bb595beb583695c16f7a0278a --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/openai_embedding.py @@ -0,0 +1,208 @@ +"""OpenAI client.""" +import copy +import logging +import os +from typing import Any, Dict, List, Optional + +import numpy as np +import tiktoken + +from manifest.clients.openai import OpenAIClient +from manifest.request import EmbeddingRequest + +logger = logging.getLogger(__name__) + +OPENAI_EMBEDDING_ENGINES = { + "text-embedding-ada-002", +} + + +class OpenAIEmbeddingClient(OpenAIClient): + """OpenAI client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "text-embedding-ada-002"), + } + REQUEST_CLS = EmbeddingRequest + NAME = "openaiembedding" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the OpenAI server. + + connection_str is passed as default OPENAI_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key = connection_str or os.environ.get("OPENAI_API_KEY") + if self.api_key is None: + raise ValueError( + "OpenAI API key not set. Set OPENAI_API_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = "https://api.openai.com/v1" + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + if getattr(self, "engine") not in OPENAI_EMBEDDING_ENGINES: + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. " + f"Must be {OPENAI_EMBEDDING_ENGINES}." + ) + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/embeddings" + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return True + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + if "data" not in response: + raise ValueError(f"Invalid response: {response}") + if "usage" in response: + # Handle splitting the usages for batch requests + if len(response["data"]) == 1: + if isinstance(response["usage"], list): + response["usage"] = response["usage"][0] + response["usage"] = [response["usage"]] + else: + # Try to split usage + split_usage = self.split_usage(request, response["data"]) + if split_usage: + response["usage"] = split_usage + return response + + def _format_request_for_embedding(self, request_params: Dict[str, Any]) -> Dict: + """Format request params for embedding. + + Args: + request_params: request params. + + Returns: + formatted request params. + """ + # Format for embedding model + request_params = copy.deepcopy(request_params) + prompt = request_params.pop("prompt") + if isinstance(prompt, str): + prompt_list = [prompt] + else: + prompt_list = prompt + request_params["input"] = prompt_list + return request_params + + def _format_request_from_embedding(self, response_dict: Dict[str, Any]) -> Dict: + """Format response from embedding for standard response. + + Args: + response_dict: response. + + Return: + formatted response. + """ + new_choices = [] + response_dict = copy.deepcopy(response_dict) + for res in response_dict.pop("data"): + new_choices.append({"array": np.array(res["embedding"])}) + response_dict["choices"] = new_choices + return response_dict + + def _run_completion( + self, request_params: Dict[str, Any], retry_timeout: int + ) -> Dict: + """Execute completion request. + + Args: + request_params: request params. + retry_timeout: retry timeout. + + Returns: + response as dict. + """ + # Format for embedding model + request_params = self._format_request_for_embedding(request_params) + response_dict = super()._run_completion(request_params, retry_timeout) + # Reformat for text model + response_dict = self._format_request_from_embedding(response_dict) + return response_dict + + async def _arun_completion( + self, request_params: Dict[str, Any], retry_timeout: int + ) -> Dict: + """Async execute completion request. + + Args: + request_params: request params. + retry_timeout: retry timeout. + + Returns: + response as dict. + """ + # Format for embedding model + request_params = self._format_request_for_embedding(request_params) + response_dict = await super()._arun_completion(request_params, retry_timeout) + # Reformat for text model + response_dict = self._format_request_from_embedding(response_dict) + return response_dict + + def split_usage(self, request: Dict, choices: List[str]) -> List[Dict[str, int]]: + """Split usage into list of usages for each prompt.""" + try: + encoding = tiktoken.encoding_for_model(getattr(self, "engine")) + except Exception: + return [] + prompt = request["input"] + if isinstance(prompt, str): + prompts = [prompt] + else: + prompts = prompt + assert len(prompts) == len(choices) + usages = [] + for pmt in prompts: + pmt_tokens = len(encoding.encode(pmt)) + # No completion tokens for embedding models + chc_tokens = 0 + usage = { + "prompt_tokens": pmt_tokens, + "completion_tokens": chc_tokens, + "total_tokens": pmt_tokens + chc_tokens, + } + usages.append(usage) + return usages diff --git a/duckdb-nsql/manifest/manifest/clients/openrouter.py b/duckdb-nsql/manifest/manifest/clients/openrouter.py new file mode 100644 index 0000000000000000000000000000000000000000..e70eedd708a0262b53a176866060da51aa9d1a16 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/openrouter.py @@ -0,0 +1,155 @@ +"""OpenRouter client.""" + +import copy +import logging +import os +from typing import Any, Dict, Optional +import time +from manifest.clients.client import Client +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + + +class OpenRouterClient(Client): + """OpenRouter client.""" + + # Params are defined in https://openrouter.ai/docs/parameters + PARAMS = { + "engine": ("model", "meta-llama/codellama-70b-instruct"), + "max_tokens": ("max_tokens", 1000), + "temperature": ("temperature", 0.1), + "top_k": ("k", 0), + "frequency_penalty": ("frequency_penalty", 0.0), + "presence_penalty": ("presence_penalty", 0.0), + "stop_sequences": ("stop", None), + } + REQUEST_CLS = LMRequest + NAME = "openrouter" + IS_CHAT = True + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the OpenRouter server. + + connection_str is passed as default OPENROUTER_API_KEY if variable not set. + + Args: + connection_str: connection string. + client_args: client arguments. + """ + self.api_key = connection_str or os.environ.get("OPENROUTER_API_KEY") + if self.api_key is None: + raise ValueError( + "OpenRouter API key not set. Set OPENROUTER_API_KEY environment " + "variable or pass through `client_connection`." + ) + self.host = "https://openrouter.ai/api/v1" + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + def close(self) -> None: + """Close the client.""" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return { + "Authorization": f"Bearer {self.api_key}", + } + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/chat/completions" + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return True + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def preprocess_request_params(self, request: Dict[str, Any]) -> Dict[str, Any]: + """ + Preprocess request params. + + Args: + request: request params. + + Returns: + request params. + """ + time.sleep(2) + # Format for chat model + request = copy.deepcopy(request) + prompt = request.pop("prompt") + if isinstance(prompt, str): + messages = [{"role": "user", "content": prompt}] + elif isinstance(prompt, list) and isinstance(prompt[0], str): + prompt_list = prompt + messages = [{"role": "user", "content": prompt} for prompt in prompt_list] + elif isinstance(prompt, list) and isinstance(prompt[0], dict): + for pmt_dict in prompt: + if "role" not in pmt_dict or "content" not in pmt_dict: + raise ValueError( + "Prompt must be list of dicts with 'role' and 'content' " + f"keys. Got {prompt}." + ) + messages = prompt + else: + raise ValueError( + "Prompt must be string, list of strings, or list of dicts." + f"Got {prompt}" + ) + request["messages"] = messages + return super().preprocess_request_params(request) + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + new_choices = [] + response = copy.deepcopy(response) + if not "choices" in response: + new_choices.append({"text": ""}) + else: + for message in response["choices"]: + if "delta" in message: + # This is a streaming response + if "content" in message["delta"]: + new_choices.append({"text": message["delta"]["content"]}) + else: + new_choices.append({"text": message["message"]["content"]}) + response["choices"] = new_choices + return super().postprocess_response(response, request) diff --git a/duckdb-nsql/manifest/manifest/clients/toma.py b/duckdb-nsql/manifest/manifest/clients/toma.py new file mode 100644 index 0000000000000000000000000000000000000000..417db9705e70652de978def3b77165aeaf317551 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/toma.py @@ -0,0 +1,173 @@ +"""TOMA client.""" +import logging +import os +from datetime import datetime +from typing import Any, Dict, Optional + +import requests + +from manifest.clients.client import Client +from manifest.request import LMRequest + +logger = logging.getLogger(__name__) + +# Engines are dynamically instantiated from API +# but a few example engines are listed below. +TOMA_ENGINES = { + "Together-gpt-JT-6B-v1", +} + + +class TOMAClient(Client): + """TOMA client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "Together-gpt-JT-6B-v1"), + "temperature": ("temperature", 0.1), + "max_tokens": ("max_tokens", 32), + # n is depricated with new API but will come back online soon + # "n": ("n", 1), + "top_p": ("top_p", 0.9), + "top_k": ("top_k", 40), + "stop_sequences": ("stop", []), + } + REQUEST_CLS = LMRequest + NAME = "toma" + + def connect( + self, + connection_str: Optional[str] = None, + client_args: Dict[str, Any] = {}, + ) -> None: + """ + Connect to the TOMA url. + + Arsg: + connection_str: connection string. + client_args: client arguments. + """ + self.host = os.environ.get("TOMA_URL", None) + if not self.host: + raise ValueError("TOMA_URL environment variable not set.") + # self.api_key = os.environ.get("TOMA_API_KEY", connection_str) + # if self.api_key is None: + # raise ValueError( + # "TOMA API key not set. Set TOMA_API_KEY environment " + # "variable or pass through `client_connection`." + # ) + + for key in self.PARAMS: + setattr(self, key, client_args.pop(key, self.PARAMS[key][1])) + + # Not functioning yet in new TOMA API. Will come back online soon. + """ + model_heartbeats = self.get_model_heartbeats() + if getattr(self, "engine") not in model_heartbeats.keys(): + raise ValueError( + f"Invalid engine {getattr(self, 'engine')}. " + f"Must be {model_heartbeats.keys()}." + ) + model_heartbeat_threshold = 120 + logger.info(f"TOMA model heartbeats\n {json.dumps(model_heartbeats)}") + if ( + model_heartbeats[getattr(self, "engine")]["last_ping"] + > model_heartbeat_threshold + ): + logger.warning( + f"Model {getattr(self, 'engine')} has not been pinged in " + f"{model_heartbeats[getattr(self, 'engine')]} seconds." + ) + if model_heartbeats[getattr(self, "engine")]["expected_runtime"] > getattr( + self, "client_timeout" + ): + logger.warning( + f"Model {getattr(self, 'engine')} has expected runtime " + f"{model_heartbeats[getattr(self, 'engine')]['expected_runtime']} " + f"and may take longer than {getattr(self, 'client_timeout')} " + "seconds to respond. Increase client_timeout " + "to avoid timeout." + ) + """ + + def close(self) -> None: + """Close the client.""" + pass + + def get_generation_url(self) -> str: + """Get generation URL.""" + return self.host + "/inference" + + def get_generation_header(self) -> Dict[str, str]: + """ + Get generation header. + + Returns: + header. + """ + return {} + + def supports_batch_inference(self) -> bool: + """Return whether the client supports batch inference.""" + return False + + def supports_streaming_inference(self) -> bool: + """Return whether the client supports streaming inference. + + Override in child client class. + """ + return False + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def get_model_heartbeats(self) -> Dict[str, Dict]: + """ + Get TOMA models and their last ping time. + + Some TOMA models are not loaded and will not response. + + Returns: + model name to time since last ping (sec). + """ + res = requests.get(self.host + "/model_statuses").json() + heartbeats = {} + for mod in res: + mod_time = datetime.fromisoformat(mod["last_heartbeat"]) + now = datetime.now(mod_time.tzinfo) + heartbeats[mod["name"]] = { + "last_ping": (now - mod_time).total_seconds(), + "expected_runtime": mod["expected_runtime"], + } + return heartbeats + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + return { + "model": getattr(self, "engine"), + "choices": [ + { + "text": item["text"], + # "token_logprobs": [], + } + for item in response["output"]["choices"] + ], + } diff --git a/duckdb-nsql/manifest/manifest/clients/toma_diffuser.py b/duckdb-nsql/manifest/manifest/clients/toma_diffuser.py new file mode 100644 index 0000000000000000000000000000000000000000..008b5279a3947aaff1ef0fed2c1acbbf60dc3460 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/clients/toma_diffuser.py @@ -0,0 +1,74 @@ +"""TOMA client.""" +import base64 +import io +import logging +from typing import Any, Dict + +import numpy as np +from PIL import Image + +from manifest.clients.toma import TOMAClient +from manifest.request import DiffusionRequest + +logger = logging.getLogger(__name__) + +# Engines are dynamically instantiated from API +# but a few example engines are listed below. +TOMA_ENGINES = { + "StableDiffusion", +} + + +class TOMADiffuserClient(TOMAClient): + """TOMADiffuser client.""" + + # User param -> (client param, default value) + PARAMS = { + "engine": ("model", "StableDiffusion"), + "num_inference_steps": ("steps", 50), + "height": ("height", 512), + "width": ("width", 512), + "n": ("n", 1), + "guidance_scale": ("guidance_scale", 7.5), + } + REQUEST_CLS = DiffusionRequest # type: ignore + NAME = "tomadiffuser" + + def get_model_params(self) -> Dict: + """ + Get model params. + + By getting model params from the server, we can add to request + and make sure cache keys are unique to model. + + Returns: + model params. + """ + return {"model_name": self.NAME, "engine": getattr(self, "engine")} + + def postprocess_response(self, response: Dict, request: Dict) -> Dict[str, Any]: + """ + Format response to dict. + + Args: + response: response + request: request + + Return: + response as dict + """ + return { + "model": getattr(self, "engine"), + "choices": [ + { + "array": np.array( + Image.open( + io.BytesIO( + base64.decodebytes(bytes(item["image_base64"], "utf-8")) + ) + ) + ), + } + for item in response["output"]["choices"] + ], + } diff --git a/duckdb-nsql/manifest/manifest/connections/__init__.py b/duckdb-nsql/manifest/manifest/connections/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7c024a619eb3c4e5ed3393acf1cd3b3263da87b8 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/connections/__init__.py @@ -0,0 +1 @@ +"""Connection init.""" diff --git a/duckdb-nsql/manifest/manifest/connections/client_pool.py b/duckdb-nsql/manifest/manifest/connections/client_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..bbc771fffa3839df19eac357c1d257476f4b14ad --- /dev/null +++ b/duckdb-nsql/manifest/manifest/connections/client_pool.py @@ -0,0 +1,193 @@ +"""Client connection.""" +import logging +import time +from typing import Any, Dict, List, Optional, Type + +from pydantic import BaseModel, Extra + +from manifest.clients.ai21 import AI21Client +from manifest.clients.azureopenai import AzureClient +from manifest.clients.azureopenai_chat import AzureChatClient +from manifest.clients.client import Client +from manifest.clients.cohere import CohereClient +from manifest.clients.dummy import DummyClient +from manifest.clients.google import GoogleClient +from manifest.clients.google_chat import GoogleChatClient +from manifest.clients.huggingface import HuggingFaceClient +from manifest.clients.huggingface_embedding import HuggingFaceEmbeddingClient +from manifest.clients.openai import OpenAIClient +from manifest.clients.openai_chat import OpenAIChatClient +from manifest.clients.openai_embedding import OpenAIEmbeddingClient +from manifest.clients.openrouter import OpenRouterClient +from manifest.clients.azureendpoint import AzureEndpointClient +from manifest.clients.toma import TOMAClient +from manifest.connections.scheduler import RandomScheduler, RoundRobinScheduler + +logging.getLogger("openai").setLevel(logging.WARNING) +logger = logging.getLogger(__name__) + +CLIENT_CONSTRUCTORS = { + AI21Client.NAME: AI21Client, + AzureClient.NAME: AzureClient, + AzureChatClient.NAME: AzureChatClient, + CohereClient.NAME: CohereClient, + DummyClient.NAME: DummyClient, + GoogleClient.NAME: GoogleClient, + GoogleChatClient.NAME: GoogleChatClient, + HuggingFaceClient.NAME: HuggingFaceClient, + HuggingFaceEmbeddingClient.NAME: HuggingFaceEmbeddingClient, + OpenAIClient.NAME: OpenAIClient, + OpenAIChatClient.NAME: OpenAIChatClient, + OpenAIEmbeddingClient.NAME: OpenAIEmbeddingClient, + OpenRouterClient.NAME: OpenRouterClient, + AzureEndpointClient.NAME: AzureEndpointClient, + TOMAClient.NAME: TOMAClient, +} + +CLIENT_REQUEST_TYPES: Dict[str, Type] = { + k: v.REQUEST_CLS for k, v in CLIENT_CONSTRUCTORS.items() +} + +# Diffusion +DIFFUSION_CLIENTS = ["diffuser", "tomadiffuser"] +try: + from manifest.clients.diffuser import DiffuserClient + from manifest.clients.toma_diffuser import TOMADiffuserClient + + CLIENT_CONSTRUCTORS[DiffuserClient.NAME] = DiffuserClient + CLIENT_CONSTRUCTORS[TOMADiffuserClient.NAME] = TOMADiffuserClient +except Exception: + logger.info("Diffusion not supported. Skipping import.") + pass + +SCHEDULER_CONSTRUCTORS = { + RandomScheduler.NAME: RandomScheduler, + RoundRobinScheduler.NAME: RoundRobinScheduler, +} + + +class Timing(BaseModel): + """Timing class.""" + + start: float = -1.0 + end: float = -1.0 + + +class ClientConnection(BaseModel): + """Client Connection class.""" + + client_name: str + # Use environment variables (depending on client) + client_connection: Optional[str] = None + # Use default engine + engine: Optional[str] = None + + # Prevent extra args + class Config: + """Config class. + + Allows to override pydantic behavior. + """ + + extra = Extra.forbid + + +class ClientConnectionPool: + """Client connection pool.""" + + def __init__( + self, + client_pool: List[ClientConnection], + client_pool_scheduler: str = "round_robin", + client_args: Dict[str, Any] = {}, + ): + """Init.""" + # Verify the clients are allowed and supported + for client in client_pool: + if client.client_name not in CLIENT_CONSTRUCTORS: + if client.client_name in DIFFUSION_CLIENTS: + raise ImportError( + f"Diffusion client {client.client_name} requires " + "the proper install. Make sure to run " + "`pip install manifest-ml[diffusers]` " + "or install Pillow." + ) + else: + raise ValueError( + f"Unknown client name: {client.client_name}. " + f"Choices are {list(CLIENT_CONSTRUCTORS.keys())}" + ) + # Verify that the serialization of all clients is the same + request_types = set( + [CLIENT_REQUEST_TYPES[client.client_name] for client in client_pool] + ) + if len(request_types) > 1: + raise ValueError( + "All clients in the client pool must use the same request type. " + f"You have {sorted(list(map(str, request_types)))}" + ) + + # Verify scheduler + if client_pool_scheduler not in SCHEDULER_CONSTRUCTORS: + raise ValueError(f"Unknown scheduler: {client_pool_scheduler}.") + + self.request_type = request_types.pop() + # Initialize the clients + # We must keep track of the used args so we know + # if a user passed in an arg that was never used + used_args = set() + self.client_pool = [] + for client in client_pool: + to_pass_kwargs = client_args.copy() + # Override the engine param for each + to_pass_kwargs.pop("engine", None) + if client.engine: + to_pass_kwargs["engine"] = client.engine + self.client_pool.append( + CLIENT_CONSTRUCTORS[client.client_name]( # type: ignore + client.client_connection, client_args=to_pass_kwargs + ) + ) + # Udpate used args + for k in client_args: + if k not in to_pass_kwargs: + used_args.add(k) + # Removed used args + for k in used_args: + client_args.pop(k) + + # Get the scheduler + self.scheduler = SCHEDULER_CONSTRUCTORS[client_pool_scheduler]( + num_clients=len(self.client_pool) + ) + self.current_client_id = 0 + # Record timing metrics for each client for load balancing + # TODO: Implement this in the future + self.client_pool_metrics = [Timing() for _ in self.client_pool] + + def close(self) -> None: + """Close.""" + for client in self.client_pool: + client.close() + + def num_clients(self) -> int: + """Get number of clients.""" + return len(self.client_pool) + + def get_next_client(self) -> Client: + """Get client.""" + client_int = self.scheduler.get_client() + self.current_client_id = client_int + return self.client_pool[client_int] + + def get_current_client(self) -> Client: + """Get current client.""" + return self.client_pool[self.current_client_id] + + def start_timer(self) -> None: + """Start timer.""" + self.client_pool_metrics[self.current_client_id].start = time.time() + + def end_timer(self) -> None: + """End timer.""" + self.client_pool_metrics[self.current_client_id].end = time.time() diff --git a/duckdb-nsql/manifest/manifest/connections/scheduler.py b/duckdb-nsql/manifest/manifest/connections/scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..fc499d3649e42a1e31eda313e4801898431a4026 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/connections/scheduler.py @@ -0,0 +1,52 @@ +"""Request client schedulers. + +Supports random selection and round robin selection. +""" +import numpy as np + + +class Scheduler: + """Scheduler base class.""" + + NAME: str = "scheduler" + + def __init__(self, num_clients: int): + """Initialize scheduler.""" + self.num_clients = num_clients + + def get_client(self) -> int: + """Get client by id.""" + raise NotImplementedError + + +class RandomScheduler(Scheduler): + """Random scheduler.""" + + NAME: str = "random" + + def __init__(self, num_clients: int): + """Initialize scheduler.""" + super().__init__(num_clients) + # Set seed + np.random.seed(0) + + def get_client(self) -> int: + """Get client by id.""" + return np.random.randint(self.num_clients) + + +class RoundRobinScheduler(Scheduler): + """Round robin scheduler.""" + + NAME: str = "round_robin" + + def __init__(self, num_clients: int): + """Initialize scheduler.""" + super().__init__(num_clients) + self.current_client = 0 + + def get_client(self) -> int: + """Get client by id.""" + client = self.current_client + self.current_client = (self.current_client + 1) % self.num_clients + return client diff --git a/duckdb-nsql/manifest/manifest/manifest.py b/duckdb-nsql/manifest/manifest/manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..45413c40e92617c364e4e6b1cb5a6b649b505d01 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/manifest.py @@ -0,0 +1,758 @@ +"""Manifest class.""" +import asyncio +import copy +import logging +from typing import ( + Any, + Dict, + Generator, + Iterator, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +import numpy as np + +from manifest.caches.noop import NoopCache +from manifest.caches.postgres import PostgresCache +from manifest.caches.redis import RedisCache +from manifest.caches.sqlite import SQLiteCache +from manifest.clients.client import Client +from manifest.clients.huggingface import HuggingFaceClient +from manifest.connections.client_pool import ( + CLIENT_CONSTRUCTORS, + ClientConnection, + ClientConnectionPool, +) +from manifest.request import LMChatRequest, LMScoreRequest, Request +from manifest.response import ModelChoices, Response, Usage, Usages + +logging.getLogger("openai").setLevel(logging.WARNING) +logger = logging.getLogger(__name__) + + +CACHE_CONSTRUCTORS = { + "redis": RedisCache, + "sqlite": SQLiteCache, + "noop": NoopCache, + "postgres": PostgresCache, +} + + +class Manifest: + """Manifest session object.""" + + def __init__( + self, + client_name: Optional[str] = None, + client_connection: Optional[str] = None, + client_pool: Optional[List[ClientConnection]] = None, + client_pool_schedule: str = "round_robin", + cache_name: str = "noop", + cache_connection: Optional[str] = None, + stop_token: str = "", + **kwargs: Any, + ): + """ + Initialize manifest. + + Args: + client_name: name of client. + client_connection: connection string for client. + client_pool: list of client connections for multi-client. + client_pool_schedule: schedule for client pool. + cache_name: name of cache. + cache_connection: connection string for cache. + stop_token: stop token prompt generation. + Can be overridden in run + + Remaining kwargs sent to client and cache. + """ + if not client_name and not client_pool: + raise ValueError( + "Must specify client_name or client_pool. " + f"Choices are {list(CLIENT_CONSTRUCTORS.keys())}" + ) + if client_name and client_pool: + raise ValueError("Cannot specify both client_name and client_pool") + if client_name: + client_pool = [ + ClientConnection( + client_name=client_name, + client_connection=client_connection, + # Remove engine from kwargs + engine=kwargs.pop("engine", None), + ) + ] + self.client_pool = ClientConnectionPool( + client_pool, client_pool_schedule, client_args=kwargs + ) + if cache_name not in CACHE_CONSTRUCTORS: + raise ValueError( + f"Unknown cache name: {cache_name}. " + f"Choices are {list(CACHE_CONSTRUCTORS.keys())}" + ) + # Must pass kwargs as dict for client "pop" methods removed used arguments + self.cache = CACHE_CONSTRUCTORS[cache_name]( # type: ignore + cache_connection, self.client_pool.request_type, cache_args=kwargs + ) + if len(kwargs) > 0: + raise ValueError(f"{list(kwargs.items())} arguments are not recognized.") + + self.stop_token = stop_token + + def close(self) -> None: + """Close the client and cache.""" + self.client_pool.close() + self.cache.close() + + def _validate_kwargs(self, kwargs: Dict, request_params: Request) -> None: + """Validate kwargs. + + Args: + kwargs: kwargs to validate. + request_params: request object to validate against. + """ + # Check for invalid kwargs + non_request_kwargs = [ + (k, v) for k, v in kwargs.items() if k not in request_params.__dict__ + ] + if len(non_request_kwargs) > 0: + raise ValueError( + f"{list(non_request_kwargs)} arguments are not recognized." + ) + + # Warn for valid but unused kwargs + request_unused_kwargs = [ + (k, v) for k, v in kwargs.items() if k not in non_request_kwargs + ] + if len(request_unused_kwargs) > 0: + logger.warning(f"{list(request_unused_kwargs)} arguments are unused.") + return + + def _split_cached_requests( + self, + request: Request, + client: Client, + overwrite_cache: bool, + ) -> Tuple[Dict[int, Response], Request]: + """Split a request into cached responses and Requests to run. + + Args: + request: request object. + overwrite_cache: whether to overwrite cache. + + Returns: + cached_idx_to_response: dict of cached responses. + new_request: request object with only prompts to run. + """ + cached_idx_to_response: Dict[int, Response] = {} + new_request = copy.deepcopy(request) + if not overwrite_cache: + if isinstance(new_request.prompt, list) and not isinstance( + request, LMChatRequest + ): + new_request.prompt = [] + for idx, prompt_str in enumerate(request.prompt): + single_request = copy.deepcopy(request) + single_request.prompt = prompt_str + possible_response = self.cache.get( + client.get_cache_key(single_request) + ) + if possible_response: + cached_idx_to_response[idx] = possible_response + else: + new_request.prompt.append(prompt_str) + # Chat or single string requests are not broken down into + # subprompts for caching. + elif (isinstance(new_request.prompt, str)) or ( + isinstance(new_request.prompt, list) + and isinstance(request, LMChatRequest) + ): + possible_response = self.cache.get(client.get_cache_key(new_request)) + if possible_response: + cached_idx_to_response[0] = possible_response + new_request.prompt = None + else: + raise ValueError( + f"Invalid prompt type: {type(new_request.prompt)}" + f" with request type: {type(request)}" + ) + return cached_idx_to_response, new_request + + def _stitch_responses_and_cache( + self, + request: Request, + client: Client, + response: Union[Response, None], + cached_idx_to_response: Dict[int, Response], + ) -> Response: + """Stich together the cached and uncached responses.""" + # We stitch the responses (the choices) here from both the new request the + # cached entries. + all_model_choices = [] + all_usages = [] + all_input_prompts: List[Union[str, List[str], List[Dict]]] = [] + response_idx = 0 + number_prompts = len(cached_idx_to_response) + single_completion_output = False + if response: + if isinstance(response.get_request_obj().prompt, str): + single_completion_output = True + number_prompts += 1 + elif isinstance(response.get_request_obj().prompt, list) and not isinstance( + request, LMChatRequest + ): + number_prompts += len(response.get_request_obj().prompt) + elif isinstance(response.get_request_obj().prompt, list) and isinstance( + request, LMChatRequest + ): + assert len(cached_idx_to_response) <= 1 + number_prompts += 1 + else: + raise ValueError( + f"Invalid prompt type: {type(response.get_request_obj().prompt)}" + f" with request type: {type(request)}" + ) + response_type = None + request_type: Type[Request] = None + for idx in range(number_prompts): + if idx in cached_idx_to_response: + cached_res = cached_idx_to_response[idx] + response_type = cached_res._response_type + request_type = cached_res._request_type + all_input_prompts.append(cached_res.get_request_obj().prompt) + if request.n == 1: + assert ( + len(cached_res.get_response_obj().choices) == 1 + ), "cached response should have only one choice" + all_model_choices.extend(cached_res.get_response_obj().choices) + if cached_res.get_usage_obj().usages: + all_usages.extend(cached_res.get_usage_obj().usages) + else: + assert response is not None, "response should not be None" + response = cast(Response, response) + response_type = response._response_type + request_type = response._request_type + # the choices list in the response is a flat one. + # length is request.n * num_prompts + current_choices = response.get_response_obj().choices[ + response_idx * request.n : (response_idx + 1) * request.n + ] + all_model_choices.extend(current_choices) + + if isinstance( + response.get_request_obj().prompt, list + ) and not isinstance(request, LMChatRequest): + prompt: Union[ + str, List[str], List[Dict] + ] = response.get_request_obj().prompt[response_idx] + # Chat request + elif isinstance(response.get_request_obj().prompt, list) and isinstance( + request, LMChatRequest + ): + # We will only have response_idx == 0 here as we can only + # support single chat requests. + assert request.n == 1 + assert number_prompts <= 1 + prompt = response.get_request_obj().prompt + else: + prompt = str(response.get_request_obj().prompt) + + usages: Optional[List[Usage]] = None + if response.get_usage_obj().usages: + usages = response.get_usage_obj().usages[ + response_idx * request.n : (response_idx + 1) * request.n + ] + all_usages.extend(usages) + all_input_prompts.append(prompt) + # set cache + new_request = copy.deepcopy(request) + new_request.prompt = prompt # type: ignore + cache_key = client.get_cache_key(new_request) + new_response = copy.deepcopy(response) + new_response._response.choices = current_choices + new_response._usages = Usages(usages=(usages or [])) + self.cache.set(cache_key, new_response.to_dict(drop_request=True)) + response_idx += 1 + + new_request = copy.deepcopy(request) + new_request.prompt = ( + all_input_prompts # type: ignore + if len(all_input_prompts) > 1 or not single_completion_output + else all_input_prompts[0] + ) + response_obj = Response( + response=ModelChoices(choices=all_model_choices), + cached=len(cached_idx_to_response) > 0, + request=new_request, + usages=Usages(usages=all_usages), + response_type=response_type, + request_type=request_type, + ) + return response_obj + + def run( + self, + prompt: Union[str, List[str], List[Dict[str, str]]], + overwrite_cache: bool = False, + stop_token: Optional[str] = None, + return_response: bool = False, + stream: bool = False, + **kwargs: Any, + ) -> Union[ + str, + List[str], + np.ndarray, + List[np.ndarray], + Response, + Iterator[str], + Iterator[Response], + ]: + """ + Run the prompt. + + Orchestrates between the standard run and chat run and batch run. + + Args: + prompt: prompt(s) to run. + overwrite_cache: whether to overwrite cache. + stop_token: stop token for prompt generation. + Default is self.stop_token. + "" for no stop token. + return_response: whether to return Response object. + stream: whether to stream the prompt. Only supported + for single string prompts and LMs. + + Returns: + response from prompt. + """ + if not isinstance(prompt, list) and not isinstance(prompt, str): + raise ValueError( + f"Invalid prompt type: {type(prompt)}. " + "Prompt must be a string or list of strings " + "or list of dicts." + ) + if isinstance(prompt, list) and not prompt: + raise ValueError("Prompt cannot be empty list") + # Get the client to run + client = self.client_pool.get_next_client() + if stream: + if not client.supports_streaming_inference(): + raise ValueError( + f"Client {client} does not support streaming inference." + ) + if not isinstance(prompt, str): + raise ValueError( + "Stream is only supported for single string prompts. " + "It will soon be supported for chat dictionary prompts, too." + ) + return self._run_stream( + prompt=cast(str, prompt), + client=client, + overwrite_cache=overwrite_cache, + stop_token=stop_token, + return_response=return_response, + **kwargs, + ) + if isinstance(prompt, list) and isinstance(prompt[0], dict): + if not client.IS_CHAT: + raise ValueError( + f"Client {client} does not support dict chat prompt. " + "Please use a chat model." + ) + if stop_token: + logger.warning( + "stop_token is not supported for chat prompt. " + "Ignoring stop_token." + ) + return self._run_chat( + prompt=cast(List[Dict[str, str]], prompt), + client=client, + overwrite_cache=overwrite_cache, + return_response=return_response, + **kwargs, + ) + return self._run( + prompt=cast(Union[str, List[str]], prompt), + client=client, + overwrite_cache=overwrite_cache, + stop_token=stop_token, + return_response=return_response, + **kwargs, + ) + + def _run( + self, + prompt: Union[str, List[str]], + client: Client, + overwrite_cache: bool = False, + stop_token: Optional[str] = None, + return_response: bool = False, + **kwargs: Any, + ) -> Union[str, List[str], np.ndarray, List[np.ndarray], Response]: + """ + Run the prompt. + + Args: + prompt: prompt(s) to run. + client: client to run. + overwrite_cache: whether to overwrite cache. + stop_token: stop token for prompt generation. + Default is self.stop_token. + "" for no stop token. + return_response: whether to return Response object. + + Returns: + response from prompt. + """ + is_batch = isinstance(prompt, list) + stop_token = stop_token if stop_token is not None else self.stop_token + # Must pass kwargs as dict for client "pop" methods removed used arguments + request_params = client.get_request(prompt, kwargs) + # Avoid nested list of results - enforce n = 1 for batch + if is_batch and request_params.n > 1: + raise ValueError("Batch mode does not support n > 1.") + self._validate_kwargs(kwargs, request_params) + + cached_idx_to_response, request_params = self._split_cached_requests( + request_params, client, overwrite_cache + ) + # If not None value or empty list - run new request + if request_params.prompt: + # Start timing metrics + self.client_pool.start_timer() + response = client.run_request(request_params) + self.client_pool.end_timer() + else: + # Nothing to run + response = None + + final_response = self._stitch_responses_and_cache( + request=request_params, + client=client, + response=response, + cached_idx_to_response=cached_idx_to_response, + ) + # Extract text results + if return_response: + return final_response + else: + return final_response.get_response(stop_token, is_batch) + + def _run_chat( + self, + prompt: List[Dict[str, str]], + client: Client, + overwrite_cache: bool = False, + return_response: bool = False, + **kwargs: Any, + ) -> Union[str, Response]: + """ + Run the prompt. + + Args: + prompt: prompt dictionary to run. + client: client to run. + overwrite_cache: whether to overwrite cache. + stop_token: stop token for prompt generation. + Default is self.stop_token. + "" for no stop token. + return_response: whether to return Response object. + + Returns: + response from prompt. + """ + is_batch = False + # Get a request for an empty prompt to handle all kwargs + request_params = client.get_request("", kwargs) + # Add prompt and cast as chat request + request_params_dict = request_params.to_dict() + request_params_dict["prompt"] = prompt + request_params_as_chat = LMChatRequest(**request_params_dict) + # Avoid nested list of results - enforce n = 1 for batch + if request_params_as_chat.n > 1: + raise ValueError("Chat mode does not support n > 1.") + self._validate_kwargs(kwargs, request_params_as_chat) + + cached_idx_to_response, request_params_as_chat = self._split_cached_requests( # type: ignore # noqa: E501 + request_params_as_chat, client, overwrite_cache + ) + # If not None value or empty list - run new request + if request_params_as_chat.prompt: + # Start timing metrics + self.client_pool.start_timer() + response = client.run_chat_request(request_params_as_chat) + self.client_pool.end_timer() + else: + # Nothing to run + response = None + + final_response = self._stitch_responses_and_cache( + request=request_params_as_chat, + client=client, + response=response, + cached_idx_to_response=cached_idx_to_response, + ) + + # Extract text results + if return_response: + return final_response + else: + return cast(str, final_response.get_response("", is_batch)) + + def _run_stream( + self, + prompt: str, + client: Client, + overwrite_cache: bool = False, + stop_token: Optional[str] = None, + return_response: bool = False, + **kwargs: Any, + ) -> Union[Generator[str, None, None], Generator[Response, None, None]]: + """ + Run the prompt in a stream. + + Args: + prompt: prompt(s) to run. + client: client to run. + overwrite_cache: whether to overwrite cache. + stop_token: stop token for prompt generation. + Default is self.stop_token. + "" for no stop token. + return_response: whether to return Response object. + + Returns: + response from prompt. + """ + is_batch = False + stop_token = stop_token if stop_token is not None else self.stop_token + # Must pass kwargs as dict for client "pop" methods removed used arguments + request_params = client.get_request(prompt, kwargs) + # Avoid nested list of results - enforce n = 1 for batch + if request_params.n > 1: + raise ValueError("Stream mode does not support n > 1.") + self._validate_kwargs(kwargs, request_params) + + cached_idx_to_response, request_params = self._split_cached_requests( + request_params, client, overwrite_cache + ) + if request_params.prompt: + # Because we are streaming, we should have either a cached response + # a prompt to run + assert len(cached_idx_to_response) == 0 + response_iter = client.run_streaming_request(request_params) + is_cached = False + else: + assert len(cached_idx_to_response) == 1 + response_iter = cached_idx_to_response[0].as_iter() + is_cached = True + + saved_responses = [] + # Start timing metrics + self.client_pool.start_timer() + for response_token in response_iter: + saved_responses.append(response_token) + if return_response: + yield response_token + else: + yield cast( + Union[str, Response], response_token.get_response("", is_batch) + ) + self.client_pool.end_timer() + + if not is_cached: + final_response = Response.union_all( + saved_responses, as_single_lmchoice=True + ) + self._stitch_responses_and_cache( + request=request_params, + client=client, + response=final_response, + cached_idx_to_response=cached_idx_to_response, + ) + + async def arun_batch( + self, + prompts: List[str], + overwrite_cache: bool = False, + stop_token: Optional[str] = None, + return_response: bool = False, + chunk_size: int = -1, + verbose: bool = False, + **kwargs: Any, + ) -> Union[List[str], List[np.ndarray], Response]: + """ + Run a batch of prompts with async. + + If the client pool is a single client, all prompts will be sent + to one client and batch_size (which is passed it as kwargs) will + determine how the prompts are split. + + If the client pool is a pool of clients, the prompts will be split + into chunks and sent to the clients. Each client will split the + chunk into batch_size prompts to send to the model. + + Args: + prompts: prompts to run. + overwrite_cache: whether to overwrite cache. + stop_token: stop token for prompt generation. + Default is self.stop_token. + "" for no stop token. + return_response: whether to return Response object. + chunk_size: number of prompts to send to a client in chunks. + For each chunk, the client will split the chunk into + batch_sized prompts to send to the model. + For a single manifest client, there is no impact to + setting chunk_size. For a client pool, chunk_size + can be used to distribute the load across the clients. + verbose: whether to print progress of async tasks. + + Returns: + response from prompt. + """ + if not isinstance(prompts, list): + raise ValueError("Prompts must be a list of strings.") + if not prompts: + raise ValueError("Prompts must not be empty.") + if not isinstance(prompts[0], str): + raise ValueError("Prompts must be a list of strings.") + + # Split the prompts into chunks for connection pool + prompt_chunks: List[Tuple[Client, List[str]]] = [] + if chunk_size > 0: + for i in range(0, len(prompts), chunk_size): + prompt_chunks.append( + (self.client_pool.get_next_client(), prompts[i : i + chunk_size]) + ) + else: + prompt_chunks = [(self.client_pool.get_next_client(), prompts)] + + # Run the chunks + tasks = [] + for client, chunk in prompt_chunks: + tasks.append( + asyncio.create_task( + self._arun_batch_client( + prompts=chunk, + client=client, + overwrite_cache=overwrite_cache, + verbose=verbose, + **kwargs, + ) + ) + ) + logger.info(f"Running {len(tasks)} tasks across all clients.") + responses = await asyncio.gather(*tasks) + final_response = Response.union_all(responses) + stop_token = stop_token if stop_token is not None else self.stop_token + + # Extract text results + if return_response: + return final_response + else: + return cast( + Union[List[str], List[np.ndarray]], + final_response.get_response(stop_token, True), + ) + + async def _arun_batch_client( + self, + prompts: List[str], + client: Client, + overwrite_cache: bool = False, + verbose: bool = False, + **kwargs: Any, + ) -> Response: + """ + Run a batch of prompts with async for single client. + + Args: + prompts: prompts to run. + client: client to run. + overwrite_cache: whether to overwrite cache. + verbose: whether to print progress of async tasks. + + Returns: + response from prompt. + """ + # Must pass kwargs as dict for client "pop" methods removed used arguments + request_params = client.get_request(prompts, kwargs) + # Avoid nested list of results - enforce n = 1 for batch + if request_params.n > 1: + raise ValueError("Batch mode does not support n > 1.") + self._validate_kwargs(kwargs, request_params) + + cached_idx_to_response, request_params = self._split_cached_requests( + request_params, client, overwrite_cache + ) + # If not None value or empty list - run new request + if request_params.prompt: + self.client_pool.start_timer() + response = await client.arun_batch_request(request_params, verbose=verbose) + self.client_pool.end_timer() + else: + # Nothing to run + response = None + + final_response = self._stitch_responses_and_cache( + request=request_params, + client=client, + response=response, + cached_idx_to_response=cached_idx_to_response, + ) + return final_response + + def score_prompt( + self, + prompt: Union[str, List[str]], + overwrite_cache: bool = False, + **kwargs: Any, + ) -> Dict: + """ + Score the prompt via forward pass of the model - no sampling or generation. + + Returns the response object with logits of the prompt. + + Args: + prompt: prompt(s) to run. + overwrite_cache: whether to overwrite cache. + + Returns: + response from prompt. + """ + client = self.client_pool.get_next_client() + # Must pass kwargs as dict for client "pop" methods removed used arguments + request_params = client.get_request(prompt, kwargs) + request_params_as_score = LMScoreRequest(**request_params.to_dict()) + + if request_params_as_score.n > 1: + raise ValueError("Sequence scoring does not support n > 1.") + self._validate_kwargs(kwargs, request_params_as_score) + + cached_idx_to_response, request_params_as_score = self._split_cached_requests( # type: ignore # noqa: E501 + request_params_as_score, client, overwrite_cache + ) + # If not None value or empty list - run new request + if request_params_as_score.prompt: + try: + response = cast(HuggingFaceClient, client).run_score_prompt_request( + request_params_as_score + ) + except AttributeError: + raise ValueError("`score_prompt` only supported for HF models.") + else: + # Nothing to run + response = None + + final_response = self._stitch_responses_and_cache( + request=request_params_as_score, + client=client, + response=response, + cached_idx_to_response=cached_idx_to_response, + ) + return final_response.to_dict() diff --git a/duckdb-nsql/manifest/manifest/request.py b/duckdb-nsql/manifest/manifest/request.py new file mode 100644 index 0000000000000000000000000000000000000000..c69732b48d748dde1bc1cdc6c2e8a4d7bc856980 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/request.py @@ -0,0 +1,138 @@ +"""Request object.""" +from typing import Any, Dict, List, Optional, Tuple, Union + +from pydantic import BaseModel + +# Used when unioning requests after async connection pool +ENGINE_SEP = "::" +NOT_CACHE_KEYS = {"client_timeout", "batch_size"} +# The below should match those in Request. +DEFAULT_REQUEST_KEYS = { + "client_timeout": ("client_timeout", 60), # seconds + "batch_size": ("batch_size", 8), + "run_id": ("run_id", None), +} + + +class Request(BaseModel): + """Request object.""" + + # Prompt + prompt: Union[str, List[str]] = "" + + # Engine + engine: str = "text-ada-001" + + # Number completions + n: int = 1 + + # Timeout + client_timeout: int = 60 + + # Run id used to repeat run with same parameters + run_id: Optional[str] = None + + # Batch size for async batch run + batch_size: int = 8 + + def to_dict( + self, allowable_keys: Dict[str, Tuple[str, Any]] = None, add_prompt: bool = True + ) -> Dict[str, Any]: + """ + Convert request to a dictionary. + + Handles parameter renaming but does not fill in default values. + It will drop any None values. + + Add prompt ensures the prompt is always in the output dictionary. + """ + if allowable_keys: + include_keys = set(allowable_keys.keys()) + if add_prompt and "prompt": + include_keys.add("prompt") + else: + allowable_keys = {} + include_keys = None + request_dict = { + allowable_keys.get(k, (k, None))[0]: v + for k, v in self.dict(include=include_keys).items() + if v is not None + } + return request_dict + + +class LMRequest(Request): + """Language Model Request object.""" + + # Temperature for generation + temperature: float = 0.7 + + # Max tokens for generation + max_tokens: int = 100 + + # Nucleus sampling taking top_p probability mass tokens + top_p: float = 1.0 + + # Top k sampling taking top_k highest probability tokens + top_k: int = 50 + + # Logprobs return value + logprobs: Optional[int] = None + + # Stop sequences + stop_sequences: Optional[List[str]] = None + + # Number beams beam search (HF) + num_beams: int = 1 + + # Whether to sample or do greedy (HF) + do_sample: bool = False + + # Penalize repetition (HF) + repetition_penalty: float = 1.0 + + # Length penalty (HF) + length_penalty: float = 1.0 + + # Penalize resence + presence_penalty: float = 0 + + # Penalize frequency + frequency_penalty: float = 0 + + +class LMChatRequest(LMRequest): + """Language Model Chat Request object.""" + + prompt: List[Dict[str, str]] = {} # type: ignore + + +class LMScoreRequest(LMRequest): + """Language Model Score Request object.""" + + pass + + +class EmbeddingRequest(Request): + """Embedding Request object.""" + + pass + + +class DiffusionRequest(Request): + """Diffusion Model Request object.""" + + # Number of steps + num_inference_steps: int = 50 + + # Height of image + height: int = 512 + + # Width of image + width: int = 512 + + # Guidance scale + guidance_scale: float = 7.5 + + # Eta + eta: float = 0.0 diff --git a/duckdb-nsql/manifest/manifest/response.py b/duckdb-nsql/manifest/manifest/response.py new file mode 100644 index 0000000000000000000000000000000000000000..7e61b7b0713cc35692de7cdc707374b8ca997de4 --- /dev/null +++ b/duckdb-nsql/manifest/manifest/response.py @@ -0,0 +1,445 @@ +"""Client response.""" +import copy +import json +from typing import Any, Dict, Generator, List, Optional, Type, Union, cast + +import numpy as np +from pydantic import BaseModel + +from manifest.request import ( + ENGINE_SEP, + DiffusionRequest, + EmbeddingRequest, + LMChatRequest, + LMRequest, + LMScoreRequest, + Request, +) + +RESPONSE_CONSTRUCTORS: Dict[Type[Request], Dict[str, Union[str, Type[Request]]]] = { + LMRequest: {"response_type": "text", "request_type": LMRequest}, + LMChatRequest: {"response_type": "text", "request_type": LMChatRequest}, + LMScoreRequest: {"response_type": "text", "request_type": LMScoreRequest}, + EmbeddingRequest: {"response_type": "array", "request_type": EmbeddingRequest}, + DiffusionRequest: {"response_type": "array", "request_type": DiffusionRequest}, +} + + +class NumpyArrayEncoder(json.JSONEncoder): + """Numpy array encoder.""" + + def default(self, obj: Any) -> str: + """Encode numpy array.""" + if isinstance(obj, np.ndarray): + return obj.tolist() + return json.JSONEncoder.default(self, obj) + + +class Usage(BaseModel): + """Prompt usage class.""" + + completion_tokens: int = 0 + prompt_tokens: int = 0 + total_tokens: int = 0 + + +class Usages(BaseModel): + """Prompt usage class.""" + + usages: List[Usage] + + +class LMModelChoice(BaseModel): + """Model single completion.""" + + text: str + token_logprobs: Optional[List[Optional[float]]] = None + tokens: Optional[List[str]] = None + + +class ArrayModelChoice(BaseModel): + """Model single completion.""" + + array: np.ndarray + token_logprobs: Optional[List[float]] = None + + class Config: + """Pydantic config class.""" + + arbitrary_types_allowed = True + + +class ModelChoices(BaseModel): + """Model choices.""" + + choices: List[Union[LMModelChoice, ArrayModelChoice]] + + +class Response: + """Response class.""" + + def __init__( + self, + response: ModelChoices, + cached: bool, + request: Request, + response_type: str, + request_type: Type[Request], + usages: Optional[Usages] = None, + ): + """ + Initialize response. + + Args: + response: response dict. + usages: usage dict. + cached: whether response is cached. + request: request. + response_type: response type. + request_type: request type. + """ + self._item_dtype = None + self._response_type = response_type + if self._response_type not in {"array", "text"}: + raise ValueError(f"Invalid response type {self._response_type}") + self._request_type = request_type + self._response = response + self._usages = usages or Usages(usages=[]) + self._cached = cached + self._request = request + if self._response.choices: + if response_type == "array": + if not isinstance(self._response.choices[0], ArrayModelChoice): + raise ValueError( + "response_type is array but response is " + f"{self._response.choices[0].__class__}" + ) + self._item_dtype = str( + cast(ArrayModelChoice, self._response.choices[0]).array.dtype + ) + else: + if not isinstance(self._response.choices[0], LMModelChoice): + raise ValueError( + "response_type is text but response is " + f"{self._response.choices[0].__class__}" + ) + + def is_cached(self) -> bool: + """Check if response is cached.""" + return self._cached + + def get_request_obj(self) -> Request: + """Get request parameters.""" + return self._request + + def get_response_obj(self) -> ModelChoices: + """Get response object.""" + return self._response + + def get_usage_obj(self) -> Usages: + """Get usage object.""" + return self._usages + + def get_json_response(self) -> Dict: + """Get response dict without parsing.""" + return self._response.dict() + + def get_response( + self, stop_token: str = "", is_batch: bool = False + ) -> Union[str, List[str], np.ndarray, List[np.ndarray]]: + """ + Get all results from response. + + Args: + stop_token: stop token for string generation + is_batch: whether response is batched + """ + process_result = lambda x: x.split(stop_token)[0] if stop_token else x + extracted_items = [ + choice.text if isinstance(choice, LMModelChoice) else choice.array + for choice in self._response.choices + ] + if len(extracted_items) == 0: + return None + if isinstance(extracted_items[0], str): + processed_results = list(map(process_result, extracted_items)) + else: + processed_results = extracted_items + if len(processed_results) == 1 and not is_batch: + return processed_results[0] + else: + return processed_results + + @classmethod + def union_all( + cls, responses: List["Response"], as_single_lmchoice: bool = False + ) -> "Response": + """Union a list of response. + + Args: + responses: list of responses to union. + as_single_lmchoice: if True, will concatenate all responses into a single + model choice. Useful for merging streaming responses. Only valid + for LMRequest responses. + """ + if not responses: + raise ValueError("Response list is empty.") + if len(responses) == 1: + return responses[0] + first_response = responses[0] + request_type = first_response._request_type + response_type = first_response._response_type + request = first_response.get_request_obj() + + if as_single_lmchoice and response_type != "text": + raise ValueError("as_single_lmchoice=True only works for text responses.") + + # Make sure all responses have the same keys + if not all( + [ + (r._request_type == request_type) + and (r._response_type == response_type) + for r in responses + ] + ): + raise ValueError("All responses must have the same keys.") + + # Get all the prompts and model choices + all_prompts = [] + all_choices = [] + all_usages: List[Usage] = [] + all_engines = [] + for res in responses: + all_engines.extend(res.get_request_obj().engine.split(ENGINE_SEP)) + res_prompt = res.get_request_obj().prompt + if isinstance(res_prompt, str): + res_prompt = [res_prompt] + all_prompts.extend(res_prompt) + all_choices.extend(res.get_response_obj().choices) + if res.get_usage_obj().usages: + all_usages.extend(res.get_usage_obj().usages) + else: + # Add empty usages if not present + all_usages.extend([Usage()] * len(res_prompt)) + new_request = copy.deepcopy(request) + new_request.engine = ENGINE_SEP.join(sorted(set(all_engines))) + + if as_single_lmchoice: + if len(set(all_prompts)) != 1: + raise ValueError("Prompts must be the same for as_single_lmchoice=True") + all_choices_txt = cast(List[LMModelChoice], all_choices) # type: ignore + single_prompt = all_prompts[0] + single_text = "".join([choice.text for choice in all_choices_txt]) + single_logprobs = [ + logprob + for choice in all_choices_txt + for logprob in choice.token_logprobs or [] + ] + single_tokens = [ + token for choice in all_choices_txt for token in choice.tokens or [] + ] + single_usage = Usage( + completion_tokens=sum(usg.completion_tokens for usg in all_usages), + prompt_tokens=sum(usg.prompt_tokens for usg in all_usages), + total_tokens=sum(usg.total_tokens for usg in all_usages), + ) + new_choices = [ + LMModelChoice( + text=single_text, + token_logprobs=single_logprobs, + tokens=single_tokens, + ) + ] + new_responses = ModelChoices(choices=new_choices) # type: ignore + new_usages = Usages(usages=[single_usage]) + new_request.prompt = single_prompt + response_obj = cls( + response=new_responses, + cached=any(res.is_cached() for res in responses), + request=new_request, + usages=new_usages, + request_type=request_type, + response_type=response_type, + ) + return response_obj + else: + new_request.prompt = all_prompts + new_response = ModelChoices(choices=all_choices) + new_usages = Usages(usages=all_usages) + response_obj = cls( + response=new_response, + cached=any(res.is_cached() for res in responses), + request=new_request, + usages=new_usages, + request_type=request_type, + response_type=response_type, + ) + return response_obj + + # Return a token by token iterator over the response + def as_iter(self) -> Generator["Response", None, None]: + """Return a token by token iterator over the response. + + Will return iterator of responses with one token each. + """ + if self._response_type not in {"text"}: + raise ValueError( + f"Invalid response type {self._response_type} for as_iter()" + ) + if not self._response.choices: + raise ValueError("No choices in response.") + if len(self._response.choices) > 1: + raise ValueError( + "Response has more than one choice. as_iter() " + "should be over single choice responses." + ) + if not isinstance(self._response.choices[0], LMModelChoice): + raise ValueError( + "response_type is text but response is " + f"{self._response.choices[0].__class__}" + ) + choice = cast(LMModelChoice, self._response.choices[0]) + # If tokens, return iterator of tokens + if choice.tokens: + for token, logprob in zip(choice.tokens, choice.token_logprobs): + yield Response( + response=ModelChoices( + choices=[ + LMModelChoice( + text=token, token_logprobs=[logprob], tokens=[token] + ) + ] + ), + cached=self._cached, + request=self._request, + usages=self._usages, + request_type=self._request_type, + response_type=self._response_type, + ) + # Otherwise, do it by words + else: + for i, word in enumerate(choice.text.split(" ")): + word = " " + word if i > 0 else word + yield Response( + response=ModelChoices( + choices=[ + LMModelChoice(text=word, token_logprobs=None, tokens=None) + ] + ), + cached=self._cached, + request=self._request, + usages=self._usages, + request_type=self._request_type, + response_type=self._response_type, + ) + + def serialize(self) -> str: + """ + Serialize response to string. + + Returns: + serialized response. + """ + return json.dumps(self.to_dict(), sort_keys=True, cls=NumpyArrayEncoder) + + @classmethod + def deserialize(cls, value: str) -> "Response": + """ + Deserialize string to response. + + Args: + value: serialized response. + + Returns: + serialized response. + """ + deserialized = json.loads(value) + return cls.from_dict(deserialized) + + def to_dict(self, drop_request: bool = False) -> Dict: + """ + Get dictionary representation of response. + + Returns: + dictionary representation of response. + """ + to_return = { + "response": self._response.dict(), + "usages": self._usages.dict(), + "cached": self._cached, + "request": self._request.dict(), + "response_type": self._response_type, + "request_type": str(self._request_type.__name__), + "item_dtype": self._item_dtype, + } + if drop_request: + to_return.pop("request") + return to_return + + @classmethod + def from_dict( + cls, response_dict: Dict, request_dict: Optional[Dict] = None + ) -> "Response": + """ + Create response from dictionary. + + Args: + response: dictionary representation of response. + request_dict: dictionary representation of request which + will override what is in response_dict. + + Returns: + response. + """ + if "request" not in response_dict and request_dict is None: + raise ValueError( + "Request dictionary must be provided if " + "request is not in response dictionary." + ) + item_dtype = response_dict["item_dtype"] + response_type = response_dict["response_type"] + if response_dict["request_type"] == "LMRequest": + request_type: Type[Request] = LMRequest + elif response_dict["request_type"] == "LMChatRequest": + request_type = LMChatRequest + elif response_dict["request_type"] == "LMScoreRequest": + request_type = LMScoreRequest + elif response_dict["request_type"] == "EmbeddingRequest": + request_type = EmbeddingRequest + elif response_dict["request_type"] == "DiffusionRequest": + request_type = DiffusionRequest + choices: List[Union[LMModelChoice, ArrayModelChoice]] = [] + if item_dtype and response_type == "array": + for choice in response_dict["response"]["choices"]: + choice["array"] = np.array(choice["array"]).astype(item_dtype) + choices.append(ArrayModelChoice(**choice)) + else: + for choice in response_dict["response"]["choices"]: + choices.append(LMModelChoice(**choice)) + response = ModelChoices(choices=choices) + return cls( + response=response, + usages=Usages(**response_dict["usages"]), + cached=response_dict["cached"], + request=request_type(**(request_dict or response_dict["request"])), + response_type=response_type, + request_type=request_type, + ) + + def __str__(self) -> str: + """ + Get string representation of response. + + Returns: + string representation of response. + """ + return self.serialize() + + def __repr__(self) -> str: + """ + Get string representation of response. + + Returns: + string representation of response. + """ + return str(self) diff --git a/duckdb-nsql/manifest/manifest/version.py b/duckdb-nsql/manifest/manifest/version.py new file mode 100644 index 0000000000000000000000000000000000000000..c11f861afbe7abb68881200b40f9ef2c5f08ad1f --- /dev/null +++ b/duckdb-nsql/manifest/manifest/version.py @@ -0,0 +1 @@ +__version__ = "0.1.9" diff --git a/duckdb-nsql/manifest/pyproject.toml b/duckdb-nsql/manifest/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..2a33be2f871a033cb7dc689a90e1a7953db27a42 --- /dev/null +++ b/duckdb-nsql/manifest/pyproject.toml @@ -0,0 +1,58 @@ +# Additional Tool Configurations +[tool.mypy] +disallow_untyped_defs = true +strict_optional = false + +[[tool.mypy.overrides]] +ignore_missing_imports = true +module = [ + "accelerate", + "accelerate.utils.modeling", + "deepspeed", + "diffusers", + "dill", + "flask", + "numpy", + "pyChatGPT", + "torch", + "transformers", + "tqdm", + "tqdm.asyncio", + "sentence_transformers", + "sqlalchemy", + "sqlitedict", +] + +[tool.isort] +combine_as_imports = true +force_grid_wrap = 0 +include_trailing_comma = true +known_first_party = ["manifest"] +known_third_party = [ + "accelerate", + "accelerate.utils.modeling", + "deepspeed", + "diffusers", + "dill", + "flask", + "numpy", + "pyChatGPT", + "torch", + "transformers", + "tqdm", + "tqdm.asyncio", + "sentence_transformers", + "sqlalchemy", + "sqlitedict", +] +line_length = 88 +multi_line_output = 3 + +[tool.pytest.ini_options] +log_format = "[%(levelname)s] %(message)s" +log_date_format = "%Y-%m-%d %H:%M:%S" +addopts = "-v -rsXx" +# The following options are useful for local debugging +# addopts = "-v -rsXx -s -x --pdb" +# log_cli_level = "DEBUG" +# log_cli = true diff --git a/duckdb-nsql/manifest/setup.py b/duckdb-nsql/manifest/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..ebefb41ba65fbec17eb19fb885a53086379496e3 --- /dev/null +++ b/duckdb-nsql/manifest/setup.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Note: To use the 'upload' functionality of this file, you must: +# $ pipenv install twine --dev + +import io +import os +import sys +from distutils.util import convert_path +from shutil import rmtree + +from setuptools import Command, find_packages, setup + +main_ns = {} +ver_path = convert_path("manifest/version.py") +with open(ver_path) as ver_file: + exec(ver_file.read(), main_ns) + +# Package meta-data. +NAME = "manifest-ml" +DESCRIPTION = "Manifest for Prompting Foundation Models." +URL = "https://github.com/HazyResearch/manifest" +EMAIL = "laurel.orr@numbersstation.ai" +AUTHOR = "Laurel Orr" +REQUIRES_PYTHON = ">=3.10.0" +VERSION = main_ns["__version__"] + +# What packages are required for this module to be executed? +REQUIRED = [ + "numpy>=1.20.0", + "pydantic>=1.9.0,<2.0", + "redis>=4.3.1", + "requests>=2.27.1", + "aiohttp>=3.8.0", + "sqlitedict>=2.0.0", + "tenacity>=8.2.0", + "tiktoken>=0.3.0", + "xxhash>=3.0.0", +] + +# What packages are optional? +EXTRAS = { + "api": [ + "accelerate>=0.10.0", + "deepspeed>=0.10.0", + "diffusers>=0.6.0", + "Flask>=2.1.2", + "sentence_transformers>=2.2.0", + "torch>=1.8.0", + "transformers>=4.29.0", + "tokenizers>=0.13.3", + ], + "app": [ + "fastapi>=0.70.0", + "uvicorn>=0.18.0", + ], + "diffusers": [ + "pillow>=9.0.0", + ], + "gcp": [ + "pg8000", + "cloud-sql-python-connector[pg8000]>=1.0.0", + "sqlalchemy", + ], + "dev": [ + "autopep8>=1.6.0", + "black>=22.3.0", + "isort>=5.13.2", + "flake8>=4.0.0", + "flake8-docstrings>=1.6.0", + "mypy>=0.950", + "pep8-naming>=0.12.1", + "docformatter>=1.4", + "pytest>=7.0.0", + "pytest-cov>=3.0.0", + "python-dotenv>=0.20.0", + "sphinx-rtd-theme>=0.5.1", + "nbsphinx>=0.8.0", + "recommonmark>=0.7.1", + "pre-commit>=2.14.0", + "types-redis>=4.2.6", + "types-requests>=2.27.29", + "types-PyYAML>=6.0.7", + "types-protobuf>=3.19.21", + "types-python-dateutil>=2.8.16", + "types-setuptools>=57.4.17", + "types-pillow>=9.0.0", + "types-xxhash>=3.0.0", + "sphinx-autobuild", + "twine", + ], +} +EXTRAS["all"] = list(set(sum(EXTRAS.values(), []))) + +# The rest you shouldn't have to touch too much :) +# ------------------------------------------------ +# Except, perhaps the License and Trove Classifiers! +# If you do change the License, remember to change the Trove Classifier for that! + +here = os.path.abspath(os.path.dirname(__file__)) + +# Import the README and use it as the long-description. +# Note: this will only work if 'README.md' is present in your MANIFEST.in file! +try: + with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f: + long_description = "\n" + f.read() +except FileNotFoundError: + long_description = DESCRIPTION + +# Load the package's __version__.py module as a dictionary. +about = {} +if not VERSION: + project_slug = NAME.lower().replace("-", "_").replace(" ", "_") + with open(os.path.join(here, project_slug, "__version__.py")) as f: + exec(f.read(), about) +else: + about["__version__"] = VERSION + + +class UploadCommand(Command): + """Support setup.py upload.""" + + description = "Build and publish the package." + user_options = [] + + @staticmethod + def status(s): + """Prints things in bold.""" + print("\033[1m{0}\033[0m".format(s)) + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + try: + self.status("Removing previous builds…") + rmtree(os.path.join(here, "dist")) + rmtree(os.path.join(here, "build")) + except OSError: + pass + + self.status("Building Source and Wheel (universal) distribution…") + os.system("{0} setup.py sdist bdist_wheel --universal".format(sys.executable)) + + self.status("Uploading the package to PyPI via Twine…") + os.system("twine upload dist/*") + + self.status("Pushing git tags…") + os.system("git tag v{0}".format(about["__version__"])) + os.system("git push --tags") + + sys.exit() + + +# Where the magic happens: +setup( + name=NAME, + version=about["__version__"], + description=DESCRIPTION, + long_description=long_description, + long_description_content_type="text/markdown", + author=AUTHOR, + author_email=EMAIL, + python_requires=REQUIRES_PYTHON, + url=URL, + packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]), + # If your package is a single module, use this instead of 'packages': + # py_modules=['mypackage'], + # entry_points={ + # 'console_scripts': ['mycli=mymodule:cli'], + # }, + install_requires=REQUIRED, + extras_require=EXTRAS, + include_package_data=True, + license="Apache 2.0", + classifiers=[ + # Trove classifiers + # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + ], + # $ setup.py publish support. + cmdclass={ + "upload": UploadCommand, + }, +) diff --git a/duckdb-nsql/manifest/tests/conftest.py b/duckdb-nsql/manifest/tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..b3a3e75ddda29f7bcdb1867519386e6201e24a3a --- /dev/null +++ b/duckdb-nsql/manifest/tests/conftest.py @@ -0,0 +1,133 @@ +"""Setup for all tests.""" +import os +import shutil +from pathlib import Path +from typing import Generator + +import numpy as np +import pytest +import redis + +from manifest.request import DiffusionRequest, EmbeddingRequest, LMRequest +from manifest.response import ArrayModelChoice, LMModelChoice, ModelChoices + + +@pytest.fixture +def model_choice() -> ModelChoices: + """Get dummy model choice.""" + model_choices = ModelChoices( + choices=[ + LMModelChoice( + text="hello", token_logprobs=[0.1, 0.2], tokens=["hel", "lo"] + ), + LMModelChoice(text="bye", token_logprobs=[0.3], tokens=["bye"]), + ] + ) + return model_choices + + +@pytest.fixture +def model_choice_single() -> ModelChoices: + """Get dummy model choice.""" + model_choices = ModelChoices( + choices=[ + LMModelChoice( + text="helloo", token_logprobs=[0.1, 0.2], tokens=["hel", "loo"] + ), + ] + ) + return model_choices + + +@pytest.fixture +def model_choice_arr() -> ModelChoices: + """Get dummy model choice.""" + np.random.seed(0) + model_choices = ModelChoices( + choices=[ + ArrayModelChoice(array=np.random.randn(4, 4), token_logprobs=[0.1, 0.2]), + ArrayModelChoice(array=np.random.randn(4, 4), token_logprobs=[0.3]), + ] + ) + return model_choices + + +@pytest.fixture +def model_choice_arr_int() -> ModelChoices: + """Get dummy model choice.""" + np.random.seed(0) + model_choices = ModelChoices( + choices=[ + ArrayModelChoice( + array=np.random.randint(20, size=(4, 4)), token_logprobs=[0.1, 0.2] + ), + ArrayModelChoice( + array=np.random.randint(20, size=(4, 4)), token_logprobs=[0.3] + ), + ] + ) + return model_choices + + +@pytest.fixture +def request_lm() -> LMRequest: + """Get dummy request.""" + request = LMRequest(prompt=["what", "cat"]) + return request + + +@pytest.fixture +def request_lm_single() -> LMRequest: + """Get dummy request.""" + request = LMRequest(prompt="monkey", engine="dummy") + return request + + +@pytest.fixture +def request_array() -> EmbeddingRequest: + """Get dummy request.""" + request = EmbeddingRequest(prompt="hello") + return request + + +@pytest.fixture +def request_diff() -> DiffusionRequest: + """Get dummy request.""" + request = DiffusionRequest(prompt="hello") + return request + + +@pytest.fixture +def sqlite_cache(tmp_path: Path) -> Generator[str, None, None]: + """Sqlite Cache.""" + cache = str(tmp_path / "sqlite_cache.sqlite") + yield cache + shutil.rmtree(cache, ignore_errors=True) + + +@pytest.fixture +def redis_cache() -> Generator[str, None, None]: + """Redis cache.""" + host = os.environ.get("REDIS_HOST", "localhost") + port = int(os.environ.get("REDIS_PORT", 6379)) + yield f"{host}:{port}" + # Clear out the database + try: + db = redis.Redis(host=host, port=port) + db.flushdb() + # For better local testing, pass if redis DB not started + except redis.exceptions.ConnectionError: + pass + + +@pytest.fixture +def postgres_cache(monkeypatch: pytest.MonkeyPatch) -> Generator[str, None, None]: + """Postgres cache.""" + import sqlalchemy # type: ignore + + # Replace the sqlalchemy.create_engine function with a function that returns an + # in-memory SQLite engine + url = sqlalchemy.engine.url.URL.create("sqlite", database=":memory:") + engine = sqlalchemy.create_engine(url) + monkeypatch.setattr(sqlalchemy, "create_engine", lambda *args, **kwargs: engine) + return engine # type: ignore diff --git a/duckdb-nsql/manifest/tests/test_array_cache.py b/duckdb-nsql/manifest/tests/test_array_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..df5675ff0d53aa27d90852e0ba8cd269f06d5f30 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_array_cache.py @@ -0,0 +1,81 @@ +"""Array cache test.""" +from pathlib import Path + +import numpy as np +import pytest + +from manifest.caches.array_cache import ArrayCache + + +def test_init(tmpdir: Path) -> None: + """Test cache initialization.""" + cache = ArrayCache(Path(tmpdir)) + assert (tmpdir / "hash2arrloc.sqlite").exists() + assert cache.cur_file_idx == 0 + assert cache.cur_offset == 0 + + +def test_put_get(tmpdir: Path) -> None: + """Test putting and getting.""" + cache = ArrayCache(tmpdir) + cache.max_memmap_size = 5 + arr = np.random.rand(10, 10) + + with pytest.raises(ValueError) as exc_info: + cache.put("key", arr) + assert str(exc_info.value) == ("Array is too large to be cached. Max is 5") + + cache.max_memmap_size = 120 + cache.put("key", arr) + assert np.allclose(cache.get("key"), arr) + assert cache.get("key").dtype == arr.dtype + assert cache.cur_file_idx == 0 + assert cache.cur_offset == 100 + assert cache.hash2arrloc["key"] == { + "file_idx": 0, + "offset": 0, + "flatten_size": 100, + "shape": (10, 10), + "dtype": np.dtype("float64"), + } + + arr2 = np.random.randint(0, 3, size=(10, 10)) + cache.put("key2", arr2) + assert np.allclose(cache.get("key2"), arr2) + assert cache.get("key2").dtype == arr2.dtype + assert cache.cur_file_idx == 1 + assert cache.cur_offset == 100 + assert cache.hash2arrloc["key2"] == { + "file_idx": 1, + "offset": 0, + "flatten_size": 100, + "shape": (10, 10), + "dtype": np.dtype("int64"), + } + + cache = ArrayCache(tmpdir) + assert cache.hash2arrloc["key"] == { + "file_idx": 0, + "offset": 0, + "flatten_size": 100, + "shape": (10, 10), + "dtype": np.dtype("float64"), + } + assert cache.hash2arrloc["key2"] == { + "file_idx": 1, + "offset": 0, + "flatten_size": 100, + "shape": (10, 10), + "dtype": np.dtype("int64"), + } + assert np.allclose(cache.get("key"), arr) + assert np.allclose(cache.get("key2"), arr2) + + +def test_contains_key(tmpdir: Path) -> None: + """Test contains key.""" + cache = ArrayCache(tmpdir) + assert not cache.contains_key("key") + arr = np.random.rand(10, 10) + cache.put("key", arr) + assert cache.contains_key("key") diff --git a/duckdb-nsql/manifest/tests/test_cache.py b/duckdb-nsql/manifest/tests/test_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..266a60fa4ccb51f9108bb491f2723aafccd5e978 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_cache.py @@ -0,0 +1,242 @@ +"""Cache test.""" +from typing import Dict, Type, cast + +import numpy as np +import pytest +from redis import Redis +from sqlitedict import SqliteDict + +from manifest.caches.cache import Cache +from manifest.caches.noop import NoopCache +from manifest.caches.postgres import PostgresCache +from manifest.caches.redis import RedisCache +from manifest.caches.sqlite import SQLiteCache +from manifest.request import DiffusionRequest, LMRequest, Request +from manifest.response import ArrayModelChoice, ModelChoices, Response + + +def _get_postgres_cache( + request_type: Type[Request] = LMRequest, cache_args: Dict = {} +) -> Cache: # type: ignore + """Get postgres cache.""" + cache_args.update({"cache_user": "", "cache_password": "", "cache_db": ""}) + return PostgresCache( + "postgres", + request_type=request_type, + cache_args=cache_args, + ) + + +@pytest.mark.usefixtures("sqlite_cache") +@pytest.mark.usefixtures("redis_cache") +@pytest.mark.usefixtures("postgres_cache") +@pytest.mark.parametrize("cache_type", ["sqlite", "redis", "postgres"]) +def test_init( + sqlite_cache: str, redis_cache: str, postgres_cache: str, cache_type: str +) -> None: + """Test cache initialization.""" + if cache_type == "sqlite": + sql_cache_obj = SQLiteCache(sqlite_cache) + assert isinstance(sql_cache_obj.cache, SqliteDict) + elif cache_type == "redis": + redis_cache_obj = RedisCache(redis_cache) + assert isinstance(redis_cache_obj.redis, Redis) + elif cache_type == "postgres": + postgres_cache_obj = _get_postgres_cache() + isinstance(postgres_cache_obj, PostgresCache) + + +@pytest.mark.usefixtures("sqlite_cache") +@pytest.mark.usefixtures("redis_cache") +@pytest.mark.usefixtures("postgres_cache") +@pytest.mark.parametrize("cache_type", ["sqlite", "postgres", "redis"]) +def test_key_get_and_set( + sqlite_cache: str, redis_cache: str, postgres_cache: str, cache_type: str +) -> None: + """Test cache key get and set.""" + if cache_type == "sqlite": + cache = cast(Cache, SQLiteCache(sqlite_cache)) + elif cache_type == "redis": + cache = cast(Cache, RedisCache(redis_cache)) + elif cache_type == "postgres": + cache = cast(Cache, _get_postgres_cache()) + + cache.set_key("test", "valueA") + cache.set_key("testA", "valueB") + assert cache.get_key("test") == "valueA" + assert cache.get_key("testA") == "valueB" + + cache.set_key("testA", "valueC") + assert cache.get_key("testA") == "valueC" + + cache.get_key("test", table="prompt") is None + cache.set_key("test", "valueA", table="prompt") + cache.get_key("test", table="prompt") == "valueA" + + +@pytest.mark.usefixtures("sqlite_cache") +@pytest.mark.usefixtures("redis_cache") +@pytest.mark.usefixtures("postgres_cache") +@pytest.mark.parametrize("cache_type", ["sqlite", "redis", "postgres"]) +def test_get( + sqlite_cache: str, + redis_cache: str, + postgres_cache: str, + cache_type: str, + model_choice: ModelChoices, + model_choice_single: ModelChoices, + model_choice_arr_int: ModelChoices, + request_lm: LMRequest, + request_lm_single: LMRequest, + request_diff: DiffusionRequest, +) -> None: + """Test cache save prompt.""" + if cache_type == "sqlite": + cache = cast(Cache, SQLiteCache(sqlite_cache)) + elif cache_type == "redis": + cache = cast(Cache, RedisCache(redis_cache)) + elif cache_type == "postgres": + cache = cast(Cache, _get_postgres_cache()) + + response = Response( + response=model_choice_single, + cached=False, + request=request_lm_single, + usages=None, + request_type=LMRequest, + response_type="text", + ) + + cache_response = cache.get(request_lm_single.dict()) + assert cache_response is None + + cache.set(request_lm_single.dict(), response.to_dict(drop_request=True)) + cache_response = cache.get(request_lm_single.dict()) + assert cache_response.get_response() == "helloo" + assert cache_response.is_cached() + assert cache_response.get_request_obj() == request_lm_single + + response = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + + cache_response = cache.get(request_lm.dict()) + assert cache_response is None + + cache.set(request_lm.dict(), response.to_dict(drop_request=True)) + cache_response = cache.get(request_lm.dict()) + assert cache_response.get_response() == ["hello", "bye"] + assert cache_response.is_cached() + assert cache_response.get_request_obj() == request_lm + + # Test array + response = Response( + response=model_choice_arr_int, + cached=False, + request=request_diff, + usages=None, + request_type=DiffusionRequest, + response_type="array", + ) + + if cache_type == "sqlite": + cache = SQLiteCache(sqlite_cache, request_type=DiffusionRequest) + elif cache_type == "redis": + cache = RedisCache(redis_cache, request_type=DiffusionRequest) + elif cache_type == "postgres": + cache = _get_postgres_cache(request_type=DiffusionRequest) + + cache_response = cache.get(request_diff.dict()) + assert cache_response is None + + cache.set(request_diff.dict(), response.to_dict(drop_request=True)) + cached_response = cache.get(request_diff.dict()) + assert np.allclose( + cached_response.get_response()[0], + cast(ArrayModelChoice, model_choice_arr_int.choices[0]).array, + ) + assert np.allclose( + cached_response.get_response()[1], + cast(ArrayModelChoice, model_choice_arr_int.choices[1]).array, + ) + assert cached_response.is_cached() + assert cached_response.get_request_obj() == request_diff + + # Test array byte string + # Make sure to not hit the cache + new_request_diff = DiffusionRequest(**request_diff.dict()) + new_request_diff.prompt = ["blahhh", "yayayay"] + response = Response( + response=model_choice_arr_int, + cached=False, + request=new_request_diff, + usages=None, + request_type=DiffusionRequest, + response_type="array", + ) + + if cache_type == "sqlite": + cache = SQLiteCache( + sqlite_cache, + request_type=DiffusionRequest, + cache_args={"array_serializer": "byte_string"}, + ) + elif cache_type == "redis": + cache = RedisCache( + redis_cache, + request_type=DiffusionRequest, + cache_args={"array_serializer": "byte_string"}, + ) + elif cache_type == "postgres": + cache = _get_postgres_cache( + request_type=DiffusionRequest, + cache_args={"array_serializer": "byte_string"}, + ) + + cached_response = cache.get(new_request_diff.dict()) + assert cached_response is None + + cache.set(new_request_diff.dict(), response.to_dict(drop_request=True)) + cached_response = cache.get(new_request_diff.dict()) + assert np.allclose( + cached_response.get_response()[0], + cast(ArrayModelChoice, model_choice_arr_int.choices[0]).array, + ) + assert np.allclose( + cached_response.get_response()[1], + cast(ArrayModelChoice, model_choice_arr_int.choices[1]).array, + ) + assert cached_response.is_cached() + assert cached_response.get_request_obj() == new_request_diff + + +def test_noop_cache() -> None: + """Test cache that is a no-op cache.""" + cache = NoopCache(None) + cache.set_key("test", "valueA") + cache.set_key("testA", "valueB") + assert cache.get_key("test") is None + assert cache.get_key("testA") is None + + cache.set_key("testA", "valueC") + assert cache.get_key("testA") is None + + cache.get_key("test", table="prompt") is None + cache.set_key("test", "valueA", table="prompt") + cache.get_key("test", table="prompt") is None + + # Assert always not cached + test_request = {"test": "hello", "testA": "world"} + test_response = {"choices": [{"text": "hello"}]} + + response = cache.get(test_request) + assert response is None + + cache.set(test_request, test_response) + response = cache.get(test_request) + assert response is None diff --git a/duckdb-nsql/manifest/tests/test_client.py b/duckdb-nsql/manifest/tests/test_client.py new file mode 100644 index 0000000000000000000000000000000000000000..b2b9fb08d4d8b53af68e61842de453bd4c1e4d01 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_client.py @@ -0,0 +1,189 @@ +""" +Test client. + +We just test the dummy client. +""" +from manifest.clients.dummy import DummyClient + + +def test_init() -> None: + """Test client initialization.""" + client = DummyClient(connection_str=None) + assert client.n == 1 # type: ignore + + args = {"n": 3} + client = DummyClient(connection_str=None, client_args=args) + assert client.n == 3 # type: ignore + + +def test_get_params() -> None: + """Test get param functions.""" + client = DummyClient(connection_str=None) + assert client.get_model_params() == { + "engine": "dummy", + "model": "text-davinci-003", + } + assert client.get_model_inputs() == [ + "engine", + "temperature", + "max_tokens", + "n", + "top_p", + "top_k", + "batch_size", + ] + + +def test_get_request() -> None: + """Test client get request.""" + args = {"n": 3} + client = DummyClient(connection_str=None, client_args=args) + request_params = client.get_request("hello", {}) + response = client.run_request(request_params) + assert client.get_cache_key(request_params) == { + "prompt": "hello", + "model": "text-davinci-003", + "n": 3, + "temperature": 0.0, + "max_tokens": 10, + "top_p": 1.0, + "best_of": 1, + "engine": "dummy", + "request_cls": "LMRequest", + } + assert response.get_json_response() == { + "choices": [ + { + "text": " probsuib.FirstName>- commodityting segunda inserted signals Religious", # noqa: E501 + "token_logprobs": [ + -0.2649905035732101, + -1.210794839387105, + -1.2173929801003434, + -0.7758233850171001, + -0.7165940659570416, + -1.7430328887209088, + -1.5379414228820203, + -1.7838011423472508, + -1.139095076944217, + -0.6321855879833425, + ], + "tokens": [ + "70470", + "80723", + "52693", + "39743", + "38983", + "1303", + "56072", + "22306", + "17738", + "53176", + ], + } + ] + * 3 + } + assert response.get_usage_obj().dict() == { + "usages": [{"prompt_tokens": 1, "completion_tokens": 10, "total_tokens": 11}] + * 3, + } + + request_params = client.get_request("hello", {"n": 5}) + response = client.run_request(request_params) + assert client.get_cache_key(request_params) == { + "prompt": "hello", + "model": "text-davinci-003", + "n": 5, + "temperature": 0.0, + "max_tokens": 10, + "top_p": 1.0, + "best_of": 1, + "engine": "dummy", + "request_cls": "LMRequest", + } + assert response.get_json_response() == { + "choices": [ + { + "text": " probsuib.FirstName>- commodityting segunda inserted signals Religious", # noqa: E501 + "token_logprobs": [ + -0.2649905035732101, + -1.210794839387105, + -1.2173929801003434, + -0.7758233850171001, + -0.7165940659570416, + -1.7430328887209088, + -1.5379414228820203, + -1.7838011423472508, + -1.139095076944217, + -0.6321855879833425, + ], + "tokens": [ + "70470", + "80723", + "52693", + "39743", + "38983", + "1303", + "56072", + "22306", + "17738", + "53176", + ], + } + ] + * 5 + } + assert response.get_usage_obj().dict() == { + "usages": [{"prompt_tokens": 1, "completion_tokens": 10, "total_tokens": 11}] + * 5, + } + + request_params = client.get_request(["hello"] * 5, {"n": 1}) + response = client.run_request(request_params) + assert client.get_cache_key(request_params) == { + "prompt": ["hello"] * 5, + "model": "text-davinci-003", + "n": 1, + "temperature": 0.0, + "max_tokens": 10, + "top_p": 1.0, + "best_of": 1, + "engine": "dummy", + "request_cls": "LMRequest", + } + assert response.get_json_response() == { + "choices": [ + { + "text": " probsuib.FirstName>- commodityting segunda inserted signals Religious", # noqa: E501 + "token_logprobs": [ + -0.2649905035732101, + -1.210794839387105, + -1.2173929801003434, + -0.7758233850171001, + -0.7165940659570416, + -1.7430328887209088, + -1.5379414228820203, + -1.7838011423472508, + -1.139095076944217, + -0.6321855879833425, + ], + "tokens": [ + "70470", + "80723", + "52693", + "39743", + "38983", + "1303", + "56072", + "22306", + "17738", + "53176", + ], + } + ] + * 5 + } + assert response.get_usage_obj().dict() == { + "usages": [{"prompt_tokens": 1, "completion_tokens": 10, "total_tokens": 11}] + * 5, + } diff --git a/duckdb-nsql/manifest/tests/test_client_pool.py b/duckdb-nsql/manifest/tests/test_client_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..ddc97ffa56436a5b01c53021db31dcf0a239c428 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_client_pool.py @@ -0,0 +1,63 @@ +"""Test client pool.""" + +import time + +import pytest + +from manifest.connections.client_pool import ClientConnection, ClientConnectionPool +from manifest.request import LMRequest + + +def test_init() -> None: + """Test initialization.""" + client_connection1 = ClientConnection( + client_name="openai", client_connection="XXX", engine="text-davinci-002" + ) + client_connection2 = ClientConnection( + client_name="openai", client_connection="XXX", engine="text-ada-001" + ) + client_connection3 = ClientConnection( + client_name="openaiembedding", client_connection="XXX" + ) + with pytest.raises(ValueError) as exc_info: + ClientConnectionPool( + [client_connection1, client_connection2], client_pool_scheduler="bad" + ) + assert str(exc_info.value) == "Unknown scheduler: bad." + + with pytest.raises(ValueError) as exc_info: + ClientConnectionPool([client_connection1, client_connection3]) + assert ( + str(exc_info.value) + == "All clients in the client pool must use the same request type. You have [\"\", \"\"]" # noqa: E501" + ) + + pool = ClientConnectionPool([client_connection1, client_connection2]) + assert pool.request_type == LMRequest + assert len(pool.client_pool) == 2 + assert len(pool.client_pool_metrics) == 2 + assert pool.client_pool[0].engine == "text-davinci-002" # type: ignore + assert pool.client_pool[1].engine == "text-ada-001" # type: ignore + + +def test_timing() -> None: + """Test timing client.""" + client_connection1 = ClientConnection(client_name="dummy") + client_connection2 = ClientConnection(client_name="dummy") + connection_pool = ClientConnectionPool([client_connection1, client_connection2]) + + connection_pool.get_next_client() + assert connection_pool.current_client_id == 0 + connection_pool.start_timer() + time.sleep(2) + connection_pool.end_timer() + + connection_pool.get_next_client() + assert connection_pool.current_client_id == 1 + connection_pool.start_timer() + time.sleep(1) + connection_pool.end_timer() + + timing = connection_pool.client_pool_metrics + assert timing[0].end - timing[0].start > 1.9 + assert timing[1].end - timing[1].start > 0.9 diff --git a/duckdb-nsql/manifest/tests/test_huggingface_api.py b/duckdb-nsql/manifest/tests/test_huggingface_api.py new file mode 100644 index 0000000000000000000000000000000000000000..75446eb927127ee59f9cad152c040cc1638301dd --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_huggingface_api.py @@ -0,0 +1,280 @@ +"""Test the HuggingFace API.""" + +import math +import os +from subprocess import PIPE, Popen + +import numpy as np +import pytest + +from manifest.api.models.huggingface import MODEL_REGISTRY, TextGenerationModel +from manifest.api.models.sentence_transformer import SentenceTransformerModel + +NOCUDA = 0 +try: + p = Popen( + [ + "nvidia-smi", + ( + "--query-gpu=index,utilization.gpu,memory.total,memory.used," + "memory.free,driver_version,name,gpu_serial,display_active," + "display_mode" + ), + "--format=csv,noheader,nounits", + ], + stdout=PIPE, + ) +except OSError: + NOCUDA = 1 + +MAXGPU = 0 +if NOCUDA == 0: + try: + p = os.popen( # type: ignore + "nvidia-smi --query-gpu=index --format=csv,noheader,nounits" + ) + i = p.read().split("\n") # type: ignore + MAXGPU = int(i[-2]) + 1 + except OSError: + NOCUDA = 1 + + +def test_load_non_registry_model() -> None: + """Test load model not in registry.""" + model_name = "NinedayWang/PolyCoder-160M" + assert model_name not in MODEL_REGISTRY + model = TextGenerationModel( + model_name_or_path=model_name, model_type="text-generation" + ) + result = model.generate("Why is the sky green?", max_tokens=10) + assert result is not None + + +def test_gpt_generate() -> None: + """Test pipeline generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="gpt2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = "Why is the sky green?" + result = model.generate(inputs, max_tokens=10) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "\n\nThe sky is green.\n\nThe" + assert math.isclose(round(result[0][1], 3), -11.516) + + result = model.generate("Cats are", max_tokens=10) + assert result is not None + assert len(result) == 1 + assert result[0][0] == " not the only ones who are being targeted by the" + assert math.isclose(round(result[0][1], 3), -21.069) + + result = model.generate(inputs, max_tokens=5) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "\n\nThe sky is" + assert math.isclose(round(result[0][1], 3), -6.046) + + # Truncate max length + model.pipeline.max_length = 5 + result = model.generate(inputs, max_tokens=2) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "\n\n" + assert math.isclose(round(result[0][1], 3), -1.414) + + +def test_encdec_generate() -> None: + """Test pipeline generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="google/t5-small-lm-adapt", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = "Why is the sky green?" + result = model.generate(inputs, max_tokens=10) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "What is the sky green? What is the sky" + assert math.isclose(round(result[0][1], 3), -7.271) + + result = model.generate("Cats are", max_tokens=10) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "a great way to get out of the house" + assert math.isclose(round(result[0][1], 3), -13.868) + + result = model.generate(inputs, max_tokens=5) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "What is the sky green" + assert math.isclose(round(result[0][1], 3), -5.144) + + # Truncate max length + model.pipeline.max_length = 5 + result = model.generate(inputs, max_tokens=2) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "Is" + assert math.isclose(round(result[0][1], 3), -4.233) + + +def test_gpt_score() -> None: + """Test pipeline generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="gpt2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = ["Why is the sky green?", "Cats are butterflies"] + result = model.score_sequence(inputs) + assert result is not None + assert len(result) == 2 + assert math.isclose(round(result[0][0], 3), -46.71) + assert math.isclose(round(result[1][0], 3), -12.752) + assert isinstance(result[0][1], list) + assert isinstance(result[1][1], list) + + +def test_embed() -> None: + """Test embedding pipeline.""" + model = TextGenerationModel( + model_name_or_path="gpt2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = ["Why is the sky green?", "Cats are butterflies"] + embeddings = model.embed(inputs) + assert isinstance(embeddings, np.ndarray) + assert embeddings.shape == (2, 768) + + model2 = SentenceTransformerModel( + model_name_or_path="all-mpnet-base-v2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = ["Why is the sky green?", "Cats are butterflies"] + embeddings = model2.embed(inputs) + assert isinstance(embeddings, np.ndarray) + assert embeddings.shape == (2, 768) + + +def test_batch_gpt_generate() -> None: + """Test pipeline generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="gpt2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = ["Why is the sky green?", "Cats are"] + result = model.generate(inputs, max_tokens=10) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "\n\nThe sky is green.\n\nThe" + assert math.isclose(round(result[0][1], 3), -11.516) + assert result[1][0] == " not the only ones who are being targeted by the" + assert math.isclose(round(result[1][1], 3), -21.069) + + result = model.generate(inputs, max_tokens=5) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "\n\nThe sky is" + assert math.isclose(round(result[0][1], 2), -6.05) + assert result[1][0] == " not the only ones who" + assert math.isclose(round(result[1][1], 3), -9.978) + + # Truncate max length + model.pipeline.max_length = 5 + result = model.generate(inputs, max_tokens=2) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "\n\n" + assert math.isclose(round(result[0][1], 3), -1.414) + assert result[1][0] == " not the" + assert math.isclose(round(result[1][1], 3), -6.246) + + +def test_batch_encdec_generate() -> None: + """Test pipeline generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="google/t5-small-lm-adapt", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=False, + use_fp16=False, + device=-1, + ) + inputs = ["Why is the sky green?", "Cats are"] + result = model.generate(inputs, max_tokens=10) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "What is the sky green? What is the sky" + assert math.isclose(round(result[0][1], 3), -7.271) + assert result[1][0] == "a great way to get out of the house" + assert math.isclose(round(result[1][1], 3), -13.868) + + result = model.generate(inputs, max_tokens=5) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "What is the sky green" + assert math.isclose(round(result[0][1], 3), -5.144) + assert result[1][0] == "a great way to" + assert math.isclose(round(result[1][1], 3), -6.353) + + # Truncate max length + model.pipeline.max_length = 5 + result = model.generate(inputs, max_tokens=2) + assert result is not None + assert len(result) == 2 + assert result[0][0] == "Is" + assert math.isclose(round(result[0][1], 3), -4.233) + assert result[1][0] == "a" + assert math.isclose(round(result[1][1], 3), -1.840) + + +@pytest.mark.skipif( + (NOCUDA == 1 or MAXGPU == 0), reason="No cuda or GPUs found through nvidia-smi" +) +def test_gpt_deepspeed_generate() -> None: + """Test deepspeed generation from a gpt model.""" + model = TextGenerationModel( + model_name_or_path="gpt2", + use_accelerate=False, + use_parallelize=False, + use_bitsandbytes=False, + use_deepspeed=True, + use_fp16=False, + device=0, + ) + inputs = "Why is the sky green?" + result = model.generate(inputs, max_tokens=10) + assert result is not None + assert len(result) == 1 + assert result[0][0] == "\n\nThe sky is green.\n\nThe" + assert math.isclose(round(result[0][1], 3), -11.517) diff --git a/duckdb-nsql/manifest/tests/test_manifest.py b/duckdb-nsql/manifest/tests/test_manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..12cf291cbc441c3bd0015cb56f1e8a637fb8a4e1 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_manifest.py @@ -0,0 +1,1449 @@ +"""Manifest test.""" +import asyncio +import os +from typing import Iterator, cast +from unittest.mock import MagicMock, Mock, patch + +import numpy as np +import pytest +import requests +from requests import HTTPError + +from manifest import Manifest, Response +from manifest.caches.noop import NoopCache +from manifest.caches.sqlite import SQLiteCache +from manifest.clients.dummy import DummyClient +from manifest.connections.client_pool import ClientConnection + +URL = "http://localhost:6000" +try: + _ = requests.post(URL + "/params").json() + MODEL_ALIVE = True +except Exception: + MODEL_ALIVE = False + +OPENAI_ALIVE = os.environ.get("OPENAI_API_KEY") is not None + + +@pytest.mark.usefixtures("sqlite_cache") +def test_init(sqlite_cache: str) -> None: + """Test manifest initialization.""" + with pytest.raises(ValueError) as exc_info: + Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + sep_tok="", + ) + assert str(exc_info.value) == "[('sep_tok', '')] arguments are not recognized." + + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + ) + assert len(manifest.client_pool.client_pool) == 1 + client = manifest.client_pool.get_next_client() + assert isinstance(client, DummyClient) + assert isinstance(manifest.cache, SQLiteCache) + assert client.n == 1 # type: ignore + assert manifest.stop_token == "" + + manifest = Manifest( + client_name="dummy", + cache_name="noop", + n=3, + stop_token="\n", + ) + assert len(manifest.client_pool.client_pool) == 1 + client = manifest.client_pool.get_next_client() + assert isinstance(client, DummyClient) + assert isinstance(manifest.cache, NoopCache) + assert client.n == 3 # type: ignore + assert manifest.stop_token == "\n" + + +@pytest.mark.usefixtures("sqlite_cache") +@pytest.mark.parametrize("n", [1, 2]) +@pytest.mark.parametrize("return_response", [True, False]) +def test_run(sqlite_cache: str, n: int, return_response: bool) -> None: + """Test manifest run.""" + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + n=n, + temperature=0.0, + ) + + prompt = "This is a prompt" + with pytest.raises(ValueError) as exc_info: + result = manifest.run(prompt, return_response=return_response, bad_input=5) + assert str(exc_info.value) == "[('bad_input', 5)] arguments are not recognized." + + result = manifest.run(prompt, return_response=return_response, top_k=5) + assert result is not None + + prompt = "This is a prompt" + result = manifest.run(prompt, return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token) + else: + res = cast(str, result) + + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "This is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + if n == 1: + assert res == "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines" + else: + assert res == [ + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + ] + + prompt = "This is a prompt" + result = manifest.run(prompt, run_id="34", return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token) + else: + res = cast(str, result) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "This is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + "run_id": "34", + } + ) + is not None + ) + if n == 1: + assert res == "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines" + else: + assert res == [ + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + ] + + prompt = "Hello is a prompt" + result = manifest.run(prompt, return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token) + else: + res = cast(str, result) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "Hello is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + if n == 1: + assert res == "appersstoff210 currentNodeleh norm unified_voice DIYHam" + else: + assert res == [ + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + ] + + prompt = "Hello is a prompt" + result = manifest.run( + prompt, stop_token=" current", return_response=return_response + ) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(stop_token=" current") + else: + res = cast(str, result) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "Hello is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + if n == 1: + assert res == "appersstoff210" + else: + assert res == ["appersstoff210", "appersstoff210"] + + +@pytest.mark.usefixtures("sqlite_cache") +@pytest.mark.parametrize("n", [1, 2]) +@pytest.mark.parametrize("return_response", [True, False]) +def test_batch_run(sqlite_cache: str, n: int, return_response: bool) -> None: + """Test manifest run.""" + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + n=n, + temperature=0.0, + ) + prompt = ["This is a prompt"] + if n == 2: + with pytest.raises(ValueError) as exc_info: + result = manifest.run(prompt, return_response=return_response) + assert str(exc_info.value) == "Batch mode does not support n > 1." + else: + result = manifest.run(prompt, return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token, is_batch=True) + else: + res = cast(str, result) + assert res == ["Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines"] + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "This is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + prompt = ["Hello is a prompt", "Hello is a prompt"] + result = manifest.run(prompt, return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token, is_batch=True) + else: + res = cast(str, result) + assert res == [ + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + ] + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "Hello is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + result = manifest.run(prompt, return_response=True) + res = cast(Response, result).get_response(manifest.stop_token, is_batch=True) + assert cast(Response, result).is_cached() + + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": n, + "prompt": "New prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is None + ) + prompt = ["This is a prompt", "New prompt"] + result = manifest.run(prompt, return_response=return_response) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(manifest.stop_token, is_batch=True) + # Cached because one item is in cache + assert result.is_cached() + else: + res = cast(str, result) + assert res == [ + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + ".vol.deserializebigmnchantment ROTıl='')\najsС", + ] + + prompt = ["Hello is a prompt", "Hello is a prompt"] + result = manifest.run( + prompt, stop_token=" current", return_response=return_response + ) + if return_response: + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len( + result.get_response_obj().choices + ) + res = result.get_response(stop_token=" current", is_batch=True) + else: + res = cast(str, result) + assert res == ["appersstoff210", "appersstoff210"] + + +@pytest.mark.usefixtures("sqlite_cache") +def test_abatch_run(sqlite_cache: str) -> None: + """Test manifest run.""" + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + temperature=0.0, + ) + prompt = ["This is a prompt"] + result = cast( + Response, asyncio.run(manifest.arun_batch(prompt, return_response=True)) + ) + + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response(manifest.stop_token, is_batch=True) + assert res == ["Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines"] + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "This is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + prompt = ["Hello is a prompt", "Hello is a prompt"] + result = cast( + Response, asyncio.run(manifest.arun_batch(prompt, return_response=True)) + ) + + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response(manifest.stop_token, is_batch=True) + assert res == [ + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + "appersstoff210 currentNodeleh norm unified_voice DIYHam", + ] + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "Hello is a prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + result = cast( + Response, asyncio.run(manifest.arun_batch(prompt, return_response=True)) + ) + + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response(manifest.stop_token, is_batch=True) + assert result.is_cached() + + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "New prompt", + "request_cls": "LMRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is None + ) + prompt = ["This is a prompt", "New prompt"] + result = cast( + Response, asyncio.run(manifest.arun_batch(prompt, return_response=True)) + ) + + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response(manifest.stop_token, is_batch=True) + # Cached because one item is in cache + assert result.is_cached() + assert res == [ + "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + ".vol.deserializebigmnchantment ROTıl='')\najsС", + ] + + prompt = ["Hello is a prompt", "Hello is a prompt"] + result = cast( + Response, asyncio.run(manifest.arun_batch(prompt, return_response=True)) + ) + + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response(stop_token=" current", is_batch=True) + assert res == ["appersstoff210", "appersstoff210"] + + +@pytest.mark.usefixtures("sqlite_cache") +def test_run_chat(sqlite_cache: str) -> None: + """Test manifest run.""" + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + temperature=0.0, + ) + # Set CHAT to be true for this model + manifest.client_pool.client_pool[0].IS_CHAT = True + + prompt = [ + {"role": "system", "content": "Hello."}, + ] + result = manifest.run(prompt, return_response=False) + assert ( + result + == "ectors WortGo ré_sg|--------------------------------------------------------------------------\n contradictory Aad \u200b getUserId" # noqa: E501 + ) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": [{"content": "Hello.", "role": "system"}], + "request_cls": "LMChatRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + prompt = [ + {"role": "system", "content": "Hello."}, + {"role": "user", "content": "Goodbye?"}, + ] + result = manifest.run(prompt, return_response=True) + assert isinstance(result, Response) + result = cast(Response, result) + assert len(result.get_usage_obj().usages) == len(result.get_response_obj().choices) + res = result.get_response() + assert res == "_deploy_age_gp hora Plus Scheduler EisenhowerRF视 chemotherapy" + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": [ + {"role": "system", "content": "Hello."}, + {"role": "user", "content": "Goodbye?"}, + ], + "request_cls": "LMChatRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + + +@pytest.mark.usefixtures("sqlite_cache") +def test_score_run(sqlite_cache: str) -> None: + """Test manifest run.""" + manifest = Manifest( + client_name="dummy", + cache_name="sqlite", + cache_connection=sqlite_cache, + temperature=0.0, + ) + + prompt = "This is a prompt" + result = manifest.score_prompt(prompt) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "This is a prompt", + "request_cls": "LMScoreRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + assert result == { + "response": { + "choices": [ + { + "text": "Nice Employ NFCYouryms“Inwarn\ttemplate europ Moines", + "token_logprobs": [ + -1.827188890438529, + -1.6981601736417915, + -0.24606708391178755, + -1.9209383499010613, + -0.8833563758318617, + -1.4121369466920703, + -0.376352908076236, + -1.3200064558188096, + -0.813028447207917, + -0.5977255311239729, + ], + "tokens": [ + "46078", + "21445", + "48305", + "7927", + "76125", + "46233", + "34581", + "23679", + "63021", + "78158", + ], + } + ] + }, + "usages": { + "usages": [ + {"completion_tokens": 10, "prompt_tokens": 4, "total_tokens": 14} + ] + }, + "cached": False, + "request": { + "prompt": "This is a prompt", + "engine": "text-davinci-003", + "n": 1, + "client_timeout": 60, + "run_id": None, + "batch_size": 20, + "temperature": 0.0, + "max_tokens": 10, + "top_p": 1.0, + "top_k": 1, + "logprobs": None, + "stop_sequences": None, + "num_beams": 1, + "do_sample": False, + "repetition_penalty": 1.0, + "length_penalty": 1.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + }, + "response_type": "text", + "request_type": "LMScoreRequest", + "item_dtype": None, + } + + prompt_list = ["Hello is a prompt", "Hello is another prompt"] + result = manifest.score_prompt(prompt_list) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "Hello is a prompt", + "request_cls": "LMScoreRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + assert ( + manifest.cache.get( + { + "best_of": 1, + "engine": "dummy", + "max_tokens": 10, + "model": "text-davinci-003", + "n": 1, + "prompt": "Hello is another prompt", + "request_cls": "LMScoreRequest", + "temperature": 0.0, + "top_p": 1.0, + } + ) + is not None + ) + assert result == { + "response": { + "choices": [ + { + "text": "appersstoff210 currentNodeleh norm unified_voice DIYHam", + "token_logprobs": [ + -0.5613340599860608, + -1.2822870706137146, + -1.9909319620162806, + -0.6312373658222814, + -1.9066239705571664, + -1.2420939968397082, + -0.7208735169940805, + -1.9144266963723062, + -0.041181937860757856, + -0.5356282450367043, + ], + "tokens": [ + "28921", + "81056", + "8848", + "47399", + "74890", + "7617", + "43790", + "77865", + "32558", + "41041", + ], + }, + { + "text": ".addAttribute_size DE imageUrl_datas\tapFixed(hour setups\tcomment", # noqa: E501 + "token_logprobs": [ + -1.1142500072582333, + -0.819706434396527, + -1.9956443391600693, + -0.8425896744807639, + -1.8398050571245623, + -1.912564137256891, + -1.6677665162080606, + -1.1579612203844727, + -1.9876114502998343, + -0.2698297864722319, + ], + "tokens": [ + "26300", + "2424", + "3467", + "40749", + "47630", + "70998", + "13829", + "72135", + "84823", + "97368", + ], + }, + ] + }, + "usages": { + "usages": [ + {"completion_tokens": 10, "prompt_tokens": 4, "total_tokens": 14}, + {"completion_tokens": 10, "prompt_tokens": 4, "total_tokens": 14}, + ] + }, + "cached": False, + "request": { + "prompt": ["Hello is a prompt", "Hello is another prompt"], + "engine": "text-davinci-003", + "n": 1, + "client_timeout": 60, + "run_id": None, + "batch_size": 20, + "temperature": 0.0, + "max_tokens": 10, + "top_p": 1.0, + "top_k": 1, + "logprobs": None, + "stop_sequences": None, + "num_beams": 1, + "do_sample": False, + "repetition_penalty": 1.0, + "length_penalty": 1.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + }, + "response_type": "text", + "request_type": "LMScoreRequest", + "item_dtype": None, + } + + +@pytest.mark.skipif(not MODEL_ALIVE, reason=f"No model at {URL}") +@pytest.mark.usefixtures("sqlite_cache") +def test_local_huggingface(sqlite_cache: str) -> None: + """Test local huggingface client.""" + client = Manifest( + client_name="huggingface", + client_connection=URL, + cache_name="sqlite", + cache_connection=sqlite_cache, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, str) and len(res) > 0 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert isinstance(response.get_response(), str) and len(response.get_response()) > 0 + assert response.is_cached() is True + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert response.is_cached() is True + + res_list = client.run(["Why are there apples?", "Why are there bananas?"]) + assert isinstance(res_list, list) and len(res_list) == 2 + + response = cast( + Response, client.run("Why are there bananas?", return_response=True) + ) + assert response.is_cached() is True + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is True + + scores = client.score_prompt("Why are there apples?") + assert isinstance(scores, dict) and len(scores) > 0 + assert scores["cached"] is False + assert len(scores["response"]["choices"][0]["token_logprobs"]) == len( + scores["response"]["choices"][0]["tokens"] + ) + + scores = client.score_prompt(["Why are there apples?", "Why are there bananas?"]) + assert isinstance(scores, dict) and len(scores) > 0 + assert scores["cached"] is True + assert len(scores["response"]["choices"][0]["token_logprobs"]) == len( + scores["response"]["choices"][0]["tokens"] + ) + assert len(scores["response"]["choices"][0]["token_logprobs"]) == len( + scores["response"]["choices"][0]["tokens"] + ) + + +@pytest.mark.skipif(not MODEL_ALIVE, reason=f"No model at {URL}") +@pytest.mark.usefixtures("sqlite_cache") +def test_local_huggingfaceembedding(sqlite_cache: str) -> None: + """Test openaichat client.""" + client = Manifest( + client_name="huggingfaceembedding", + client_connection=URL, + cache_name="sqlite", + cache_connection=sqlite_cache, + ) + + res = client.run("Why are there carrots?") + assert isinstance(res, np.ndarray) + + response = cast( + Response, client.run("Why are there carrots?", return_response=True) + ) + assert isinstance(response.get_response(), np.ndarray) + assert np.allclose(response.get_response(), res) + + client = Manifest( + client_name="huggingfaceembedding", + client_connection=URL, + cache_name="sqlite", + cache_connection=sqlite_cache, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, np.ndarray) + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert isinstance(response.get_response(), np.ndarray) + assert np.allclose(response.get_response(), res) + assert response.is_cached() is True + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert response.is_cached() is True + + res_list = client.run(["Why are there apples?", "Why are there bananas?"]) + assert ( + isinstance(res_list, list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + + response = cast( + Response, + client.run( + ["Why are there apples?", "Why are there mangos?"], return_response=True + ), + ) + assert ( + isinstance(response.get_response(), list) and len(response.get_response()) == 2 + ) + + response = cast( + Response, client.run("Why are there bananas?", return_response=True) + ) + assert response.is_cached() is True + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is False + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert ( + isinstance(res_list, list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + + response = cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pinenuts?", "Why are there cocoa?"], + return_response=True, + ) + ), + ) + assert ( + isinstance(response.get_response(), list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is True + + +@pytest.mark.skipif(not OPENAI_ALIVE, reason="No openai key set") +@pytest.mark.usefixtures("sqlite_cache") +def test_openai(sqlite_cache: str) -> None: + """Test openai client.""" + client = Manifest( + client_name="openai", + engine="text-ada-001", + cache_name="sqlite", + cache_connection=sqlite_cache, + temperature=0.0, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, str) and len(res) > 0 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert isinstance(response.get_response(), str) and len(response.get_response()) > 0 + assert response.get_response() == res + assert response.is_cached() is True + assert response.get_usage_obj().usages + assert response.get_usage_obj().usages[0].total_tokens == 15 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert response.is_cached() is True + + res_list = client.run(["Why are there apples?", "Why are there bananas?"]) + assert isinstance(res_list, list) and len(res_list) == 2 + + response = cast( + Response, + client.run( + ["Why are there apples?", "Why are there mangos?"], return_response=True + ), + ) + assert ( + isinstance(response.get_response(), list) and len(response.get_response()) == 2 + ) + assert response.get_usage_obj().usages and len(response.get_usage_obj().usages) == 2 + assert response.get_usage_obj().usages[0].total_tokens == 15 + assert response.get_usage_obj().usages[1].total_tokens == 16 + + response = cast( + Response, client.run("Why are there bananas?", return_response=True) + ) + assert response.is_cached() is True + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + + response = cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pinenuts?", "Why are there cocoa?"], + return_response=True, + ) + ), + ) + assert ( + isinstance(response.get_response(), list) and len(response.get_response()) == 2 + ) + assert response.get_usage_obj().usages and len(response.get_usage_obj().usages) == 2 + assert response.get_usage_obj().usages[0].total_tokens == 17 + assert response.get_usage_obj().usages[1].total_tokens == 15 + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is True + + # Test streaming + num_responses = 0 + streaming_response_text = cast( + Iterator[str], client.run("Why are there oranges?", stream=True) + ) + for res_text in streaming_response_text: + num_responses += 1 + assert isinstance(res_text, str) and len(res_text) > 0 + assert num_responses == 8 + + streaming_response = cast( + Iterator[Response], + client.run("Why are there mandarines?", return_response=True, stream=True), + ) + num_responses = 0 + merged_res = [] + for res in streaming_response: + num_responses += 1 + assert isinstance(res, Response) and len(res.get_response()) > 0 + merged_res.append(cast(str, res.get_response())) + assert not res.is_cached() + assert num_responses == 10 + + # Make sure cached + streaming_response = cast( + Iterator[Response], + client.run("Why are there mandarines?", return_response=True, stream=True), + ) + num_responses = 0 + merged_res_cachced = [] + for res in streaming_response: + num_responses += 1 + assert isinstance(res, Response) and len(res.get_response()) > 0 + merged_res_cachced.append(cast(str, res.get_response())) + assert res.is_cached() + # OpenAI stream does not return logprobs, so this is by number of words + assert num_responses == 7 + assert "".join(merged_res) == "".join(merged_res_cachced) + + +@pytest.mark.skipif(not OPENAI_ALIVE, reason="No openai key set") +@pytest.mark.usefixtures("sqlite_cache") +def test_openaichat(sqlite_cache: str) -> None: + """Test openaichat client.""" + client = Manifest( + client_name="openaichat", + cache_name="sqlite", + cache_connection=sqlite_cache, + temperature=0.0, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, str) and len(res) > 0 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert isinstance(response.get_response(), str) and len(response.get_response()) > 0 + assert response.get_response() == res + assert response.is_cached() is True + assert response.get_usage_obj().usages + assert response.get_usage_obj().usages[0].total_tokens == 23 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert response.is_cached() is True + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is False + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + + response = cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pinenuts?", "Why are there cocoa?"], + return_response=True, + ) + ), + ) + assert ( + isinstance(response.get_response(), list) and len(response.get_response()) == 2 + ) + assert response.get_usage_obj().usages and len(response.get_usage_obj().usages) == 2 + assert response.get_usage_obj().usages[0].total_tokens == 25 + assert response.get_usage_obj().usages[1].total_tokens == 23 + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is True + + chat_dict = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Who won the world series in 2020?"}, + { + "role": "assistant", + "content": "The Los Angeles Dodgers won the World Series in 2020.", + }, + {"role": "user", "content": "Where was it played?"}, + ] + res = client.run(chat_dict) + assert isinstance(res, str) and len(res) > 0 + response = cast(Response, client.run(chat_dict, return_response=True)) + assert response.is_cached() is True + assert response.get_usage_obj().usages[0].total_tokens == 67 + chat_dict = [ + {"role": "system", "content": "You are a helpful assistanttttt."}, + {"role": "user", "content": "Who won the world series in 2020?"}, + { + "role": "assistant", + "content": "The Los Angeles Dodgers won the World Series in 2020.", + }, + {"role": "user", "content": "Where was it played?"}, + ] + response = cast(Response, client.run(chat_dict, return_response=True)) + assert response.is_cached() is False + + # Test streaming + num_responses = 0 + streaming_response_text = cast( + Iterator[str], client.run("Why are there oranges?", stream=True) + ) + for res_text in streaming_response_text: + num_responses += 1 + assert isinstance(res_text, str) and len(res_text) > 0 + assert num_responses == 9 + + streaming_response = cast( + Iterator[Response], + client.run("Why are there mandarines?", return_response=True, stream=True), + ) + num_responses = 0 + merged_res = [] + for res in streaming_response: + num_responses += 1 + assert isinstance(res, Response) and len(res.get_response()) > 0 + merged_res.append(cast(str, res.get_response())) + assert not res.is_cached() + assert num_responses == 10 + + # Make sure cached + streaming_response = cast( + Iterator[Response], + client.run("Why are there mandarines?", return_response=True, stream=True), + ) + num_responses = 0 + merged_res_cachced = [] + for res in streaming_response: + num_responses += 1 + assert isinstance(res, Response) and len(res.get_response()) > 0 + merged_res_cachced.append(cast(str, res.get_response())) + assert res.is_cached() + # OpenAI stream does not return logprobs, so this is by number of words + assert num_responses == 7 + assert "".join(merged_res) == "".join(merged_res_cachced) + + +@pytest.mark.skipif(not OPENAI_ALIVE, reason="No openai key set") +@pytest.mark.usefixtures("sqlite_cache") +def test_openaiembedding(sqlite_cache: str) -> None: + """Test openaichat client.""" + client = Manifest( + client_name="openaiembedding", + cache_name="sqlite", + cache_connection=sqlite_cache, + array_serializer="local_file", + ) + + res = client.run("Why are there carrots?") + assert isinstance(res, np.ndarray) + + response = cast( + Response, client.run("Why are there carrots?", return_response=True) + ) + assert isinstance(response.get_response(), np.ndarray) + assert np.allclose(response.get_response(), res) + + client = Manifest( + client_name="openaiembedding", + cache_name="sqlite", + cache_connection=sqlite_cache, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, np.ndarray) + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert isinstance(response.get_response(), np.ndarray) + assert np.allclose(response.get_response(), res) + assert response.is_cached() is True + assert response.get_usage_obj().usages + assert response.get_usage_obj().usages[0].total_tokens == 5 + + response = cast(Response, client.run("Why are there apples?", return_response=True)) + assert response.is_cached() is True + + res_list = client.run(["Why are there apples?", "Why are there bananas?"]) + assert ( + isinstance(res_list, list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + + response = cast( + Response, + client.run( + ["Why are there apples?", "Why are there mangos?"], return_response=True + ), + ) + assert ( + isinstance(response.get_response(), list) and len(response.get_response()) == 2 + ) + assert response.get_usage_obj().usages and len(response.get_usage_obj().usages) == 2 + assert response.get_usage_obj().usages[0].total_tokens == 5 + assert response.get_usage_obj().usages[1].total_tokens == 6 + + response = cast( + Response, client.run("Why are there bananas?", return_response=True) + ) + assert response.is_cached() is True + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is False + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert ( + isinstance(res_list, list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + + response = cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pinenuts?", "Why are there cocoa?"], + return_response=True, + ) + ), + ) + assert ( + isinstance(response.get_response(), list) + and len(res_list) == 2 + and isinstance(res_list[0], np.ndarray) + ) + assert response.get_usage_obj().usages and len(response.get_usage_obj().usages) == 2 + assert response.get_usage_obj().usages[0].total_tokens == 7 + assert response.get_usage_obj().usages[1].total_tokens == 5 + + response = cast( + Response, client.run("Why are there oranges?", return_response=True) + ) + assert response.is_cached() is True + + +@pytest.mark.skipif(not OPENAI_ALIVE, reason="No openai key set") +@pytest.mark.usefixtures("sqlite_cache") +def test_openai_pool(sqlite_cache: str) -> None: + """Test openai and openaichat client.""" + client_connection1 = ClientConnection( + client_name="openaichat", + ) + client_connection2 = ClientConnection(client_name="openai", engine="text-ada-001") + client = Manifest( + client_pool=[client_connection1, client_connection2], + cache_name="sqlite", + client_connection=sqlite_cache, + ) + res = client.run("Why are there apples?") + assert isinstance(res, str) and len(res) > 0 + + res2 = client.run("Why are there apples?") + assert isinstance(res2, str) and len(res2) > 0 + # Different models + assert res != res2 + + assert cast( + Response, client.run("Why are there apples?", return_response=True) + ).is_cached() + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + res_list2 = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list2, list) and len(res_list2) == 2 + # Different models + assert res_list != res_list2 + + assert cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pears?", "Why are there oranges?"], return_response=True + ) + ), + ).is_cached() + + # Test chunk size of 1 + res_list = asyncio.run( + client.arun_batch( + ["Why are there pineapples?", "Why are there pinecones?"], chunk_size=1 + ) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + res_list2 = asyncio.run( + client.arun_batch( + ["Why are there pineapples?", "Why are there pinecones?"], chunk_size=1 + ) + ) + # Because we split across both models exactly in first run, + # we will get the same result + assert res_list == res_list2 + + +@pytest.mark.skipif( + not OPENAI_ALIVE or not MODEL_ALIVE, reason="No openai or local model set" +) +@pytest.mark.usefixtures("sqlite_cache") +def test_mixed_pool(sqlite_cache: str) -> None: + """Test openai and openaichat client.""" + client_connection1 = ClientConnection( + client_name="huggingface", + client_connection=URL, + ) + client_connection2 = ClientConnection(client_name="openai", engine="text-ada-001") + client = Manifest( + client_pool=[client_connection1, client_connection2], + cache_name="sqlite", + client_connection=sqlite_cache, + ) + + res = client.run("Why are there apples?") + assert isinstance(res, str) and len(res) > 0 + + res2 = client.run("Why are there apples?") + assert isinstance(res2, str) and len(res2) > 0 + # Different models + assert res != res2 + assert cast( + Response, client.run("Why are there apples?", return_response=True) + ).is_cached() + + res_list = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + res_list2 = asyncio.run( + client.arun_batch(["Why are there pears?", "Why are there oranges?"]) + ) + assert isinstance(res_list2, list) and len(res_list2) == 2 + # Different models + assert res_list != res_list2 + + assert cast( + Response, + asyncio.run( + client.arun_batch( + ["Why are there pears?", "Why are there oranges?"], return_response=True + ) + ), + ).is_cached() + + # Test chunk size of 1 + res_list = asyncio.run( + client.arun_batch( + ["Why are there pineapples?", "Why are there pinecones?"], chunk_size=1 + ) + ) + assert isinstance(res_list, list) and len(res_list) == 2 + res_list2 = asyncio.run( + client.arun_batch( + ["Why are there pineapples?", "Why are there pinecones?"], chunk_size=1 + ) + ) + # Because we split across both models exactly in first run, + # we will get the same result + assert res_list == res_list2 + + +def test_retry_handling() -> None: + """Test retry handling.""" + # We'll mock the response so we won't need a real connection + client = Manifest(client_name="openai", client_connection="fake") + mock_create = MagicMock( + side_effect=[ + # raise a 429 error + HTTPError( + response=Mock(status_code=429, json=Mock(return_value={})), + request=Mock(), + ), + # get a valid http response with a 200 status code + Mock( + status_code=200, + json=Mock( + return_value={ + "choices": [ + { + "finish_reason": "length", + "index": 0, + "logprobs": None, + "text": " WHATTT.", + }, + { + "finish_reason": "length", + "index": 1, + "logprobs": None, + "text": " UH OH.", + }, + { + "finish_reason": "length", + "index": 2, + "logprobs": None, + "text": " HARG", + }, + ], + "created": 1679469056, + "id": "cmpl-6wmuWfmyuzi68B6gfeNC0h5ywxXL5", + "model": "text-ada-001", + "object": "text_completion", + "usage": { + "completion_tokens": 30, + "prompt_tokens": 24, + "total_tokens": 54, + }, + } + ), + ), + ] + ) + prompts = [ + "The sky is purple. This is because", + "The sky is magnet. This is because", + "The sky is fuzzy. This is because", + ] + with patch("manifest.clients.client.requests.post", mock_create): + # Run manifest + result = client.run(prompts, temperature=0, overwrite_cache=True) + assert result == [" WHATTT.", " UH OH.", " HARG"] + + # Assert that OpenAI client was called twice + assert mock_create.call_count == 2 + + # Now make sure it errors when not a 429 or 500 + mock_create = MagicMock( + side_effect=[ + # raise a 505 error + HTTPError( + response=Mock(status_code=505, json=Mock(return_value={})), + request=Mock(), + ), + ] + ) + with patch("manifest.clients.client.requests.post", mock_create): + # Run manifest + with pytest.raises(HTTPError): + client.run(prompts, temperature=0, overwrite_cache=True) + + # Assert that OpenAI client was called once + assert mock_create.call_count == 1 diff --git a/duckdb-nsql/manifest/tests/test_request.py b/duckdb-nsql/manifest/tests/test_request.py new file mode 100644 index 0000000000000000000000000000000000000000..0b3ba223769adf9637613cc860376b2dcd78539b --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_request.py @@ -0,0 +1,63 @@ +"""Request test.""" +from manifest.request import DiffusionRequest, LMRequest + + +def test_llm_init() -> None: + """Test request initialization.""" + request = LMRequest() + assert request.temperature == 0.7 + + request = LMRequest(temperature=0.5) + assert request.temperature == 0.5 + + request = LMRequest(**{"temperature": 0.5}) # type: ignore + assert request.temperature == 0.5 + + request = LMRequest(**{"temperature": 0.5, "prompt": "test"}) # type: ignore + assert request.temperature == 0.5 + assert request.prompt == "test" + + +def test_diff_init() -> None: + """Test request initialization.""" + request = DiffusionRequest() + assert request.height == 512 + + request = DiffusionRequest(height=128) + assert request.height == 128 + + request = DiffusionRequest(**{"height": 128}) # type: ignore + assert request.height == 128 + + request = DiffusionRequest(**{"height": 128, "prompt": "test"}) # type: ignore + assert request.height == 128 + assert request.prompt == "test" + + +def test_to_dict() -> None: + """Test request to dict.""" + request_lm = LMRequest() + dct = request_lm.to_dict() + + assert dct == {k: v for k, v in request_lm.dict().items() if v is not None} + + # Note the second value is a placeholder for the default value + # It's unused in to_dict + keys = {"temperature": ("temp", 0.7)} + dct = request_lm.to_dict(allowable_keys=keys) + assert dct == {"temp": 0.7, "prompt": ""} + + dct = request_lm.to_dict(allowable_keys=keys, add_prompt=False) + assert dct == {"temp": 0.7} + + request_diff = DiffusionRequest() + dct = request_diff.to_dict() + + assert dct == {k: v for k, v in request_diff.dict().items() if v is not None} + + keys = {"height": ("hgt", 512)} + dct = request_diff.to_dict(allowable_keys=keys) + assert dct == {"hgt": 512, "prompt": ""} + + dct = request_diff.to_dict(allowable_keys=keys, add_prompt=False) + assert dct == {"hgt": 512} diff --git a/duckdb-nsql/manifest/tests/test_response.py b/duckdb-nsql/manifest/tests/test_response.py new file mode 100644 index 0000000000000000000000000000000000000000..eac0123df2d84775af2dd76e70291439bed5af83 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_response.py @@ -0,0 +1,387 @@ +"""Response test.""" +from typing import List, cast + +import numpy as np +import pytest + +from manifest import Response +from manifest.request import EmbeddingRequest, LMRequest +from manifest.response import ( + ArrayModelChoice, + LMModelChoice, + ModelChoices, + Usage, + Usages, +) + + +def test_init( + model_choice: ModelChoices, + model_choice_arr: ModelChoices, + model_choice_arr_int: ModelChoices, + request_lm: LMRequest, + request_array: EmbeddingRequest, +) -> None: + """Test response initialization.""" + response = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + assert response._response == model_choice + assert response._cached is False + assert response._request == request_lm + assert response._usages == Usages(usages=[]) + assert response._request_type == LMRequest + assert response._response_type == "text" + assert response._item_dtype is None + + response = Response( + response=model_choice_arr_int, + cached=False, + request=request_array, + usages=Usages(usages=[Usage(total_tokens=4), Usage(total_tokens=6)]), + request_type=EmbeddingRequest, + response_type="array", + ) + assert response._cached is False + assert response._request == request_array + assert sum([usg.total_tokens for usg in response._usages.usages]) == 10 + assert response._request_type == EmbeddingRequest + assert response._response_type == "array" + assert response._item_dtype == "int64" + + with pytest.raises(ValueError) as excinfo: + Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="blah", + ) + assert "blah" in str(excinfo.value) + + # Can't convert array with text + with pytest.raises(ValueError) as excinfo: + Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="array", + ) + assert str(excinfo.value) == ( + "response_type is array but response is " + "" + ) + + # Can't convert text with array + with pytest.raises(ValueError) as excinfo: + Response( + response=model_choice_arr, + cached=False, + request=request_array, + usages=None, + request_type=LMRequest, + response_type="text", + ) + assert str(excinfo.value) == ( + "response_type is text but response is " + "" + ) + + +def test_getters(model_choice: ModelChoices, request_lm: LMRequest) -> None: + """Test response cached.""" + response = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + assert response.get_response_obj() == model_choice + assert response.is_cached() is False + assert response.get_request_obj() == request_lm + assert response.get_usage_obj() == Usages(usages=[]) + assert response.get_json_response() == model_choice.dict() + assert response.get_response() == ["hello", "bye"] + + +def test_serialize( + model_choice: ModelChoices, + model_choice_arr: ModelChoices, + model_choice_arr_int: ModelChoices, + request_lm: LMRequest, + request_array: EmbeddingRequest, +) -> None: + """Test response serialization.""" + response = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + deserialized_response = Response.deserialize(response.serialize()) + assert deserialized_response.get_response_obj() == model_choice + assert deserialized_response.is_cached() is False + assert deserialized_response.get_request_obj() == request_lm + assert deserialized_response.get_usage_obj() == Usages(usages=[]) + assert deserialized_response.get_json_response() == model_choice.dict() + assert deserialized_response.get_response() == ["hello", "bye"] + + deserialized_response = Response.from_dict(response.to_dict()) + assert deserialized_response.get_response_obj() == model_choice + assert deserialized_response.is_cached() is False + assert deserialized_response.get_request_obj() == request_lm + assert deserialized_response.get_usage_obj() == Usages(usages=[]) + assert deserialized_response.get_json_response() == model_choice.dict() + assert deserialized_response.get_response() == ["hello", "bye"] + + deserialized_response = Response.from_dict( + response.to_dict(drop_request=True), request_dict={"prompt": "blahhhh"} + ) + assert deserialized_response.get_response_obj() == model_choice + assert deserialized_response.is_cached() is False + assert deserialized_response.get_request_obj().prompt == "blahhhh" + assert deserialized_response.get_usage_obj() == Usages(usages=[]) + assert deserialized_response.get_json_response() == model_choice.dict() + assert deserialized_response.get_response() == ["hello", "bye"] + + # Int type + response = Response( + response=model_choice_arr_int, + cached=False, + request=request_array, + usages=Usages(usages=[Usage(total_tokens=4), Usage(total_tokens=6)]), + request_type=EmbeddingRequest, + response_type="array", + ) + deserialized_response = Response.deserialize(response.serialize()) + assert deserialized_response._item_dtype == "int64" + assert ( + cast( + ArrayModelChoice, deserialized_response.get_response_obj().choices[0] + ).array.dtype + == np.int64 + ) + assert np.array_equal( + cast( + ArrayModelChoice, deserialized_response.get_response_obj().choices[0] + ).array, + cast(ArrayModelChoice, model_choice_arr_int.choices[0]).array, + ) + + # Float type + response = Response( + response=model_choice_arr, + cached=False, + request=request_array, + usages=Usages(usages=[Usage(total_tokens=4), Usage(total_tokens=6)]), + request_type=EmbeddingRequest, + response_type="array", + ) + deserialized_response = Response.deserialize(response.serialize()) + assert deserialized_response._item_dtype == "float64" + assert ( + cast( + ArrayModelChoice, deserialized_response.get_response_obj().choices[0] + ).array.dtype + == np.float64 + ) + assert np.array_equal( + cast( + ArrayModelChoice, deserialized_response.get_response_obj().choices[0] + ).array, + cast(ArrayModelChoice, model_choice_arr.choices[0]).array, + ) + + +def test_get_results( + model_choice: ModelChoices, + model_choice_single: ModelChoices, + model_choice_arr: ModelChoices, + request_lm: LMRequest, + request_array: EmbeddingRequest, +) -> None: + """Test response get results.""" + response = Response( + response=model_choice_single, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + assert response.get_response() == "helloo" + assert response.get_response(stop_token="ll") == "he" + assert response.get_response(stop_token="ll", is_batch=True) == ["he"] + + response = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + assert response.get_response() == ["hello", "bye"] + assert response.get_response(stop_token="b") == ["hello", ""] + assert response.get_response(stop_token="y", is_batch=True) == ["hello", "b"] + + float_arr1 = cast(ArrayModelChoice, model_choice_arr.choices[0]).array + float_arr2 = cast(ArrayModelChoice, model_choice_arr.choices[1]).array + response = Response( + response=model_choice_arr, + cached=False, + request=request_array, + usages=Usages(usages=[Usage(total_tokens=4), Usage(total_tokens=6)]), + request_type=EmbeddingRequest, + response_type="array", + ) + assert np.array_equal(response.get_response()[0], float_arr1) + assert np.array_equal(response.get_response()[1], float_arr2) + assert np.array_equal(response.get_response(stop_token="t")[0], float_arr1) + assert np.array_equal(response.get_response(stop_token="t")[1], float_arr2) + + +def test_union_all( + model_choice: ModelChoices, + model_choice_single: ModelChoices, + request_lm: LMRequest, + request_lm_single: LMRequest, +) -> None: + """Test union all.""" + response1 = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=None, + request_type=LMRequest, + response_type="text", + ) + + response2 = Response( + response=model_choice_single, + cached=False, + request=request_lm_single, + usages=None, + request_type=LMRequest, + response_type="text", + ) + + final_response = Response.union_all([response1, response2]) + assert final_response.get_json_response() == { + "choices": [ + {"text": "hello", "token_logprobs": [0.1, 0.2], "tokens": ["hel", "lo"]}, + {"text": "bye", "token_logprobs": [0.3], "tokens": ["bye"]}, + {"text": "helloo", "token_logprobs": [0.1, 0.2], "tokens": ["hel", "loo"]}, + ] + } + assert final_response.get_usage_obj() == Usages(usages=[Usage(), Usage(), Usage()]) + merged_prompts: List[str] = request_lm.prompt + [request_lm_single.prompt] # type: ignore # noqa: E501 + assert final_response.get_request_obj().prompt == merged_prompts + assert final_response.get_request_obj().engine == "dummy::text-ada-001" + + # Modify A to have usage and cached + response1 = Response( + response=model_choice, + cached=False, + request=request_lm, + usages=Usages(usages=[Usage(total_tokens=4), Usage(total_tokens=6)]), + request_type=LMRequest, + response_type="text", + ) + + final_response = Response.union_all([response1, response2]) + assert final_response.get_usage_obj() == Usages( + usages=[Usage(total_tokens=4), Usage(total_tokens=6), Usage()] + ) + + # Test merge to single + model_choices = ModelChoices( + choices=[ + LMModelChoice( + text=" helloo this is a bug", + token_logprobs=[0.1, 0.2, 0.3], + tokens=[" helloo", " this is", " a bug"], + ), + ] + ) + request = LMRequest(prompt="monkey", engine="dummy") + response1 = Response( + response=model_choices, + cached=False, + request=request, + usages=None, + request_type=LMRequest, + response_type="text", + ) + final_response = Response.union_all([response1, response1], as_single_lmchoice=True) + assert final_response.get_json_response() == { + "choices": [ + { + "text": " helloo this is a bug helloo this is a bug", + "token_logprobs": [0.1, 0.2, 0.3, 0.1, 0.2, 0.3], + "tokens": [ + " helloo", + " this is", + " a bug", + " helloo", + " this is", + " a bug", + ], + }, + ] + } + assert final_response.get_usage_obj() == Usages(usages=[Usage()]) + assert final_response.get_request_obj().prompt == "monkey" + assert final_response.get_request_obj().engine == "dummy" + + +def test_as_iter( + model_choice_single: ModelChoices, request_lm_single: LMRequest +) -> None: + """Test as iter.""" + response = Response( + response=model_choice_single, + cached=False, + request=request_lm_single, + usages=None, + request_type=LMRequest, + response_type="text", + ) + response_iter_list = list(response.as_iter()) + assert len(response_iter_list) == 2 + assert response_iter_list[0].get_response() == "hel" + assert response_iter_list[1].get_response() == "loo" + + model_choices = ModelChoices( + choices=[ + LMModelChoice(text="helloo this is a bug"), + ] + ) + request = LMRequest(prompt="monkey", engine="dummy") + response = Response( + response=model_choices, + cached=False, + request=request, + usages=None, + request_type=LMRequest, + response_type="text", + ) + response_iter_list = list(response.as_iter()) + assert len(response_iter_list) == 5 + assert response_iter_list[0].get_response() == "helloo" + assert response_iter_list[1].get_response() == " this" + assert response_iter_list[2].get_response() == " is" + assert response_iter_list[3].get_response() == " a" + assert response_iter_list[4].get_response() == " bug" diff --git a/duckdb-nsql/manifest/tests/test_scheduler.py b/duckdb-nsql/manifest/tests/test_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..7cdd40c654f7e71c819b9ef5c5dd920c57d5c017 --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_scheduler.py @@ -0,0 +1,25 @@ +"""Test scheduler.""" + +from manifest.connections.scheduler import RandomScheduler, RoundRobinScheduler + + +def test_random_scheduler() -> None: + """Test random scheduler.""" + scheduler = RandomScheduler(num_clients=2) + # Try 20 clients and make sure 0 and 1 are both + # returned + client_ids = set() + for _ in range(20): + client_id = scheduler.get_client() + assert client_id in [0, 1] + client_ids.add(client_id) + assert len(client_ids) == 2 + + +def test_round_robin_scheduler() -> None: + """Test round robin scheduler.""" + scheduler = RoundRobinScheduler(num_clients=2) + assert scheduler.get_client() == 0 + assert scheduler.get_client() == 1 + assert scheduler.get_client() == 0 + assert scheduler.get_client() == 1 diff --git a/duckdb-nsql/manifest/tests/test_serializer.py b/duckdb-nsql/manifest/tests/test_serializer.py new file mode 100644 index 0000000000000000000000000000000000000000..0fbe3b92c24e1e1822467c3028445a946ec5b8ba --- /dev/null +++ b/duckdb-nsql/manifest/tests/test_serializer.py @@ -0,0 +1,32 @@ +"""Cache test.""" +import json + +import numpy as np + +from manifest.caches.serializers import ArraySerializer, NumpyByteSerializer + + +def test_response_to_key_array() -> None: + """Test array serializer initialization.""" + serializer = ArraySerializer() + arr = np.random.rand(4, 4) + res = {"response": {"choices": [{"array": arr}]}} + key = serializer.response_to_key(res) + key_dct = json.loads(key) + assert isinstance(key_dct["response"]["choices"][0]["array"], str) + + res2 = serializer.key_to_response(key) + assert np.allclose(arr, res2["response"]["choices"][0]["array"]) + + +def test_response_to_key_numpybytes() -> None: + """Test array serializer initialization.""" + serializer = NumpyByteSerializer() + arr = np.random.rand(4, 4) + res = {"response": {"choices": [{"array": arr}]}} + key = serializer.response_to_key(res) + key_dct = json.loads(key) + assert isinstance(key_dct["response"]["choices"][0]["array"], str) + + res2 = serializer.key_to_response(key) + assert np.allclose(arr, res2["response"]["choices"][0]["array"]) diff --git a/duckdb-nsql/manifest/web_app/README.md b/duckdb-nsql/manifest/web_app/README.md new file mode 100644 index 0000000000000000000000000000000000000000..abca6b8b95f0d10d1110b9412761dac348aaec28 --- /dev/null +++ b/duckdb-nsql/manifest/web_app/README.md @@ -0,0 +1,10 @@ +## Running + +In a separate tmux/terminal session, run + +``` +cd manifest +uvicorn web_app.main:app --reload +``` + +Change the port by ass `--port `. diff --git a/duckdb-nsql/manifest/web_app/__init__.py b/duckdb-nsql/manifest/web_app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..10e09a84a9ff1078eb49f178819ba3e72706fb40 --- /dev/null +++ b/duckdb-nsql/manifest/web_app/__init__.py @@ -0,0 +1 @@ +"""Web application for Manifest.""" diff --git a/duckdb-nsql/manifest/web_app/main.py b/duckdb-nsql/manifest/web_app/main.py new file mode 100644 index 0000000000000000000000000000000000000000..22368e05fa6be331f3b3bd78ee85a3796ab3bc83 --- /dev/null +++ b/duckdb-nsql/manifest/web_app/main.py @@ -0,0 +1,56 @@ +"""Manifest as an app service.""" + +from typing import Any, Dict, cast + +from fastapi import APIRouter, FastAPI, HTTPException + +from manifest import Manifest +from manifest.response import Response as ManifestResponse +from web_app import schemas + +app = FastAPI() +api_router = APIRouter() + + +@app.get("/") +async def root() -> Dict: + """Root endpoint.""" + return {"message": "Hello to the Manifest App"} + + +@api_router.post("/prompt/", status_code=201, response_model=schemas.ManifestResponse) +def prompt_manifest(*, manifest_in: schemas.ManifestCreate) -> Dict: + """Prompt a manifest session and query.""" + manifest = Manifest( + client_name=manifest_in.client_name, + client_connection=manifest_in.client_connection, + engine=manifest_in.engine, + cache_name=manifest_in.cache_name, + cache_connection=manifest_in.cache_connection, + ) + manifest_prompt_args: Dict[str, Any] = { + "n": manifest_in.n, + "max_tokens": manifest_in.max_tokens, + } + if manifest_in.temperature: + manifest_prompt_args["temperature"] = manifest_in.temperature + if manifest_in.top_k: + manifest_prompt_args["top_k"] = manifest_in.top_k + if manifest_in.top_p: + manifest_prompt_args["top_p"] = manifest_in.top_p + + try: + response = manifest.run( + prompt=manifest_in.prompt, return_response=True, **manifest_prompt_args + ) + response = cast(ManifestResponse, response) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + return { + "response": response.get_response(), + "cached": response.is_cached(), + "request_params": response.get_request_obj(), + } + + +app.include_router(api_router) diff --git a/duckdb-nsql/manifest/web_app/schemas.py b/duckdb-nsql/manifest/web_app/schemas.py new file mode 100644 index 0000000000000000000000000000000000000000..4a4fe342c5fb7b3a7b5b73269da518eb0817b21c --- /dev/null +++ b/duckdb-nsql/manifest/web_app/schemas.py @@ -0,0 +1,32 @@ +"""Pydantic models.""" + +from typing import List, Optional, Union + +from pydantic import BaseModel + + +class ManifestCreate(BaseModel): + """Create manifest Pydantic.""" + + # Prompt params + prompt: str + n: int = 1 + max_tokens: int = 132 + temperature: Optional[float] = None + top_k: Optional[int] = None + top_p: Optional[float] = None + + # Manifest client params + client_name: str = "openai" + client_connection: Optional[str] = None + engine: str = "text-davinci-003" + cache_name: str = "noop" + cache_connection: Optional[str] = None + + +class ManifestResponse(BaseModel): + """Manifest response Pydantic.""" + + response: Union[str, List[str]] + cached: bool + request_params: dict diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..707e9981397c83d0fe28b1275f824eda5c6dd68a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,25 @@ +pandas>=2.0.0 +sqlalchemy<2.0.0 +transformers +datasets==2.11.0 +jsonlines>=3.1.0 +sqlglot==11.5.5 +click +rich +nltk>=3.5,<3.6 +sqlparse +pebble +sentencepiece +duckdb +structlog==22.3.0 +tensorboard==2.14.0 +lightning==2.1.0 +lmdb==1.4.1 +cloudpathlib==0.13.0 +peft==0.6.0 +packaging==23.2 +ninja==1.11.1.1 +langchain +pydantic>2 +packaging +manifest-ml @ git+https://github.com/tdoehmen/manifest@added_openrouter