From 18f40a49ecd6ccf7581a9585fba48591f51dcfbe Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Mon, 12 Jun 2023 21:21:02 +0200 Subject: [PATCH 01/15] Support ChatGPT 5 lib w GPT-4 --- CHANGELOG.md | 1 + README.md | 37 +- package-lock.json | 4528 ++++++++++++++++++++---------------------- package.json | 51 +- src/extension.ts | 264 +-- src/view-provider.ts | 243 +++ yarn.lock | 1845 +++++++++-------- 7 files changed, 3358 insertions(+), 3611 deletions(-) create mode 100644 src/view-provider.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b24c0f..03f1d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,4 +6,5 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Unreleased] +- Update to ChatGPT 5.x with API key instead of Session token - Initial release \ No newline at end of file diff --git a/README.md b/README.md index c3477de..fd31be9 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ This Visual Studio Code extension allows you to use the [unofficial ChatGPT API] Refactoring selected code using chatGPT +## Update + +Updated to use version 5.2.x [ChatGPT API](https://www.npmjs.com/package/chatgpt) Node.js library with support for GPT-4 using an API key instead of the old session key. + ## Features - **Ask general questions** or use code snippets from the editor to query ChatGPT via an input box in the sidebar - Right click on a code selection and run one of the context menu **shortcuts** @@ -27,22 +31,39 @@ This Visual Studio Code extension allows you to use the [unofficial ChatGPT API] To use this extension, install it from the VSCode marketplace or download and install `.vsix` file from Releases. -1. After the installation is complete, you will need to add your ChatGPT session token to the extension settings in VSCode. To do this, open the `Settings` panel by going to the `Code` menu and selecting `Preferences`, then `Settings`. +1. After the installation is complete, you will need to add your ChatGPT API key to the extension settings in VSCode. To do this, open the `Settings` panel by going to the `Code` menu and selecting `Preferences`, then `Settings`. 2. In the search bar, type `ChatGPT` to filter the settings list. -3. In the ChatGPT section, enter your session token in the `SESSION_TOKEN` field. +3. In the ChatGPT section, enter your API key in the `API_KEY` field. + +Optionally add + +- `Model` +- `Temperature` +- `TopP` + +This lets you fine-tune how your instance of `ChatGPTAPI` is created, similar to: + +```ts +const api = new ChatGPTAPI({ + apiKey: process.env.OPENAI_API_KEY, + completionParams: { + model: 'gpt-4', + temperature: 0.5, + top_p: 0.8 + } +}) +``` After completing these steps, the extension should be ready to use. -### Obtaining the session token +### Obtaining the API key -To use this extension, you will need to authenticate with a valid session token from ChatGPT. To get a session token: +To use this extension, you will need to authenticate with a valid API key from ChatGPT. To get an API key: 1. Go to https://chat.openai.com/chat and log in or sign up. -2. Open the developer tools in your browser. -3. Go to the `Application` tab and open the `Cookies` section. -4. Copy the value for `__Secure-next-auth.session-token` and save it. +2. Go to Profile -> API key -Once you have obtained a session token, you can configure the extension to use it as described in the previous section. +Once you have obtained a API key, you can configure the extension to use it as described in the previous section. ## Using the Extension diff --git a/package-lock.json b/package-lock.json index 09a2364..21ba89f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,28 +9,124 @@ "version": "0.4.0", "license": "MIT", "dependencies": { - "chatgpt": "^2.0.5" + "chatgpt": "^5.2.5" }, "devDependencies": { - "@types/glob": "^8.0.0", + "@types/glob": "^8.1.0", "@types/mocha": "^10.0.1", - "@types/node": "16.x", - "@types/vscode": "^1.73.0", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "@vscode/test-electron": "^2.2.0", - "eslint": "^8.28.0", - "glob": "^8.0.3", - "mocha": "^10.1.0", - "ts-loader": "^9.4.1", - "typescript": "^4.9.3", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.0" + "@types/node": "20.x", + "@types/vscode": "^1.79.0", + "@typescript-eslint/eslint-plugin": "^5.59.11", + "@typescript-eslint/parser": "^5.59.11", + "@vscode/test-electron": "^2.3.3", + "eslint": "^8.42.0", + "glob": "^10.2.7", + "mocha": "^10.2.0", + "ts-loader": "^9.4.3", + "typescript": "^5.1.3", + "webpack": "^5.86.0", + "webpack-cli": "^5.1.4" }, "engines": { "vscode": "^1.73.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -40,16 +136,40 @@ "node": ">=10.0.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.5.2", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -63,10 +183,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -96,10 +225,106 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", @@ -129,9 +354,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -145,9 +370,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", @@ -189,6 +414,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -198,14 +433,6 @@ "node": ">= 6" } }, - "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "dependencies": { - "@types/ms": "*" - } - }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -227,18 +454,18 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, "node_modules/@types/glob": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", - "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", "dev": true, "dependencies": { - "@types/minimatch": "*", + "@types/minimatch": "^5.1.2", "@types/node": "*" } }, @@ -248,14 +475,6 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "node_modules/@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -268,47 +487,43 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, "node_modules/@types/node": { - "version": "16.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz", - "integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz", + "integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==", "dev": true }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, - "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, "node_modules/@types/vscode": { - "version": "1.73.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.73.1.tgz", - "integrity": "sha512-eArfOrAoZVV+Ao9zQOCaFNaeXj4kTCD+bGS2gyNgIFZH9xVMuLMlRrEkhb22NyxycFWKV1UyTh03vhaVHmqVMg==", + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.0.tgz", + "integrity": "sha512-Tfowu2rSW8hVGbqzQLSPlOEiIOYYryTkgJ+chMecpYiJcnw9n0essvSiclnK+Qh/TcSVJHgaK4EMrQDZjZJ/Sw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", - "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", + "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/type-utils": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/type-utils": "5.59.11", + "@typescript-eslint/utils": "5.59.11", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -330,14 +545,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", - "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", + "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/typescript-estree": "5.59.11", "debug": "^4.3.4" }, "engines": { @@ -357,13 +572,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", - "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", + "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/visitor-keys": "5.45.1" + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/visitor-keys": "5.59.11" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -374,13 +589,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", - "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", + "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/utils": "5.59.11", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -401,9 +616,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", - "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", + "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -414,13 +629,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", - "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", + "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/visitor-keys": "5.45.1", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/visitor-keys": "5.59.11", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -441,18 +656,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", - "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", + "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/typescript-estree": "5.59.11", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -467,12 +682,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", - "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", + "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/types": "5.59.11", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -484,170 +699,170 @@ } }, "node_modules/@vscode/test-electron": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.1.tgz", - "integrity": "sha512-DUdwSYVc9p/PbGveaq20dbAAXHfvdq4zQ24ILp6PKizOBxrOfMsOq8Vts5nMzeIo0CxtA/RxZLFyDv001PiUSg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.3.tgz", + "integrity": "sha512-hgXCkDP0ibboF1K6seqQYyHAzCURgTwHS/6QU7slhwznDLwsRwg9bhfw1CZdyUEw8vvCmlrKWnd7BlQnI0BC4w==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", - "rimraf": "^3.0.2", - "unzipper": "^0.10.11" + "jszip": "^3.10.1", + "semver": "^7.3.8" }, "engines": { "node": ">=16" } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webpack-cli/configtest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", - "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "engines": { "node": ">=14.15.0" @@ -658,9 +873,9 @@ } }, "node_modules/@webpack-cli/info": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", - "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "engines": { "node": ">=14.15.0" @@ -671,9 +886,9 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", - "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "engines": { "node": ">=14.15.0" @@ -701,9 +916,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -713,9 +928,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -758,6 +973,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -828,13 +1079,13 @@ "node": ">=8" } }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/atomically": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz", + "integrity": "sha512-sxBhVZUFBFhqSAsYMM3X2oaUi2NVDJ8U026FsIusM8gYXls9AYs/eXzgGrufs1Qjpkxi9zunds+75QUFz+m7UQ==", + "dependencies": { + "stubborn-fs": "^1.2.4", + "when-exit": "^2.0.0" } }, "node_modules/balanced-match": { @@ -843,27 +1094,24 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dev": true, - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -874,12 +1122,6 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "dev": true - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -942,34 +1184,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "dev": true, - "engines": { - "node": ">=0.2.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "optional": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "engines": { - "node": ">=10.16.0" + "node": ">=8" } }, "node_modules/callsites": { @@ -1009,18 +1229,6 @@ } ] }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dev": true, - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1037,32 +1245,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chatgpt": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz", - "integrity": "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==", - "dependencies": { - "eventsource-parser": "^0.0.5", - "expiry-map": "^2.0.0", - "p-timeout": "^6.0.0", - "remark": "^14.0.2", - "strip-markdown": "^5.0.0", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-5.2.5.tgz", + "integrity": "sha512-DNhBzPb2zTDjJADY44XfngMvsvrvHRq1md2VPXLmnKeP1UCeA1B6pV3s9ZRwlcgjVT0RyM77fRj1xj5V11Vctg==", + "dependencies": { + "cac": "^6.7.14", + "conf": "^11.0.1", + "eventsource-parser": "^1.0.0", + "js-tiktoken": "^1.0.5", + "keyv": "^4.5.2", + "p-timeout": "^6.1.1", + "quick-lru": "^6.1.1", + "read-pkg-up": "^9.1.0", "uuid": "^9.0.0" }, - "engines": { - "node": ">=16.8" + "bin": { + "chatgpt": "bin/cli.js" }, - "optionalDependencies": { - "undici": "^5.13.0" + "engines": { + "node": ">=14" } }, "node_modules/chokidar": { @@ -1174,6 +1376,47 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/conf": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-11.0.1.tgz", + "integrity": "sha512-WlLiQboEjKx0bYx2IIRGedBgNjLAxtwPaCSnsjWPST5xR0DB4q8lcsO/bEH9ZRYNcj63Y9vj/JG/5Fg6uWzI0Q==", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "atomically": "^2.0.0", + "debounce-fn": "^5.1.2", + "dot-prop": "^7.2.0", + "env-paths": "^3.0.0", + "json-schema-typed": "^8.0.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -1194,15 +1437,30 @@ "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/debounce-fn": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz", + "integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==", "dependencies": { - "ms": "2.1.2" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { @@ -1222,36 +1480,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, "engines": { "node": ">=0.3.1" } @@ -1280,15 +1519,26 @@ "node": ">=6.0.0" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, + "node_modules/dot-prop": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz", + "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", "dependencies": { - "readable-stream": "^2.0.2" + "type-fest": "^2.11.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", @@ -1302,9 +1552,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", + "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -1314,6 +1564,17 @@ "node": ">=10.13.0" } }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", @@ -1326,10 +1587,18 @@ "node": ">=4" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, "node_modules/escalade": { @@ -1354,13 +1623,16 @@ } }, "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -1369,24 +1641,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -1394,7 +1664,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -1422,46 +1691,22 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -1469,6 +1714,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -1481,14 +1729,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1498,9 +1746,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -1567,34 +1815,17 @@ } }, "node_modules/eventsource-parser": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-0.0.5.tgz", - "integrity": "sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/expiry-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/expiry-map/-/expiry-map-2.0.0.tgz", - "integrity": "sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==", - "dependencies": { - "map-age-cleaner": "^0.2.0" - }, + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz", + "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==", "engines": { - "node": ">=8" + "node": ">=14.18" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.12", @@ -1722,6 +1953,22 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1742,58 +1989,10 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -1805,19 +2004,22 @@ } }, "node_modules/glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", + "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1851,21 +2053,24 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1877,6 +2082,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -1909,11 +2126,16 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -1939,6 +2161,17 @@ "he": "bin/he" } }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -1975,6 +2208,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2044,6 +2283,11 @@ "node": ">=10.13.0" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2056,33 +2300,10 @@ "node": ">=8" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -2138,17 +2359,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -2194,6 +2404,24 @@ "node": ">=0.10.0" } }, + "node_modules/jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -2223,16 +2451,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "node_modules/js-tiktoken": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.6.tgz", + "integrity": "sha512-lxHntEupgjWvSh37WxpAW4XN6UBXBtFJOpZZq5HN5oNjDfN7L/iJhHOKjyL/DFtuYXUwn5jfTciLtOWpgQmHjQ==", + "dependencies": { + "base64-js": "^1.5.1" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2245,11 +2476,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -2257,12 +2492,37 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json-schema-typed": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.1.tgz", + "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2272,14 +2532,6 @@ "node": ">=0.10.0" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2293,11 +2545,19 @@ "node": ">= 0.8.0" } }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "dev": true + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/loader-runner": { "version": "4.3.0", @@ -2345,20 +2605,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2366,67 +2616,6 @@ "node": ">=10" } }, - "node_modules/map-age-cleaner": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.2.0.tgz", - "integrity": "sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==", - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=7.6" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", - "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz", - "integrity": "sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "longest-streak": "^3.0.0", - "mdast-util-to-string": "^3.0.0", - "micromark-util-decode-string": "^1.0.0", - "unist-util-visit": "^4.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", - "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2442,428 +2631,6 @@ "node": ">= 8" } }, - "node_modules/micromark": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", - "integrity": "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", - "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-factory-destination": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", - "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", - "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", - "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", - "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", - "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", - "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", - "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", - "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", - "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", - "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", - "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", - "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-html-tag-name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", - "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", - "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", - "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz", - "integrity": "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", - "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", - "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", - "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -2898,6 +2665,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2910,31 +2688,19 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -3045,18 +2811,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.3", @@ -3094,6 +2853,20 @@ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3129,14 +2902,6 @@ "node": ">= 0.8.0" } }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3168,9 +2933,9 @@ } }, "node_modules/p-timeout": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz", - "integrity": "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz", + "integrity": "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==", "engines": { "node": ">=14.16" }, @@ -3187,6 +2952,12 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3199,6 +2970,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3232,6 +3020,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", + "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1", + "minipass": "^5.0.0 || ^6.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", + "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -3342,7 +3155,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } @@ -3367,6 +3179,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz", + "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3376,10 +3199,119 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/read-pkg-up/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -3421,61 +3353,6 @@ "node": ">= 10.13.0" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/remark": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz", - "integrity": "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "remark-parse": "^10.0.0", - "remark-stringify": "^10.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", - "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz", - "integrity": "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3485,6 +3362,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -3600,17 +3485,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3632,9 +3506,9 @@ ] }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.2.0.tgz", + "integrity": "sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -3653,7 +3527,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -3712,6 +3585,18 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3740,15 +3625,34 @@ "source-map": "^0.6.0" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "optional": true, - "engines": { - "node": ">=10.0.0" + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3778,6 +3682,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3790,6 +3709,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3802,19 +3734,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-markdown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-markdown/-/strip-markdown-5.0.0.tgz", - "integrity": "sha512-PXSts6Ta9A/TwGxVVSRlQs1ukJTAwwtbip2OheJEjPyfykaQ4sJSTnQWjLTI2vYWNts/R/91/csagp15W8n9gA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.6", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "node_modules/stubborn-fs": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.4.tgz", + "integrity": "sha512-KRa4nIRJ8q6uApQbPwYZVhOof8979fw4xbajBWa5kPJFa4nyY3aFaMWVyIVCDnkNCCG/3HLipUZ4QaNlYsmX1w==" }, "node_modules/supports-color": { "version": "7.2.0", @@ -3850,13 +3773,13 @@ } }, "node_modules/terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz", + "integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -3868,16 +3791,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -3901,6 +3824,15 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3919,28 +3851,10 @@ "node": ">=8.0" } }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/trough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", + "integrity": "sha512-n3hBnm6ozJYzwiwt5YRiJZkzktftRpMiBApHaJPoWLA+qetQBAXkHqCLM6nwSdRDimqVtA5ocIkcTRLMTt7yzA==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -3990,124 +3904,27 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/undici": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", - "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", - "optional": true, - "dependencies": { - "busboy": "^1.6.0" - }, - "engines": { - "node": ">=12.18" - } - }, - "node_modules/unified": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", - "dependencies": { - "@types/unist": "^2.0.0", - "bail": "^2.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz", - "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", - "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.1.tgz", - "integrity": "sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz", - "integrity": "sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unzipper": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", - "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" + "node": ">=14.17" } }, "node_modules/update-browserslist-db": { @@ -4140,7 +3957,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -4159,49 +3975,13 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/vfile": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.6.tgz", - "integrity": "sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz", - "integrity": "sha512-0yaU+rj2gKAyEk12ffdSbBfjnnj+b1zqTBv3OQCTn8yEB02bsPizwdBPrLJjHnK+cU9EMMcUnNv938XcZIkmdA==", + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "node_modules/watchpack": { @@ -4218,22 +3998,22 @@ } }, "node_modules/webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.86.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", + "integrity": "sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.14.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -4242,9 +4022,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.1.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -4265,17 +4045,17 @@ } }, "node_modules/webpack-cli": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", - "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.1", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^9.4.1", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", @@ -4310,12 +4090,12 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "engines": { - "node": "^12.20.0 || >=14" + "node": ">=14" } }, "node_modules/webpack-merge": { @@ -4340,6 +4120,11 @@ "node": ">=10.13.0" } }, + "node_modules/when-exit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz", + "integrity": "sha512-H85ulNwUBU1e6PGxkWUDgxnbohSXD++ah6Xw1VHAN7CtypcbZaC4aYjQ+C2PMVaDkURDuOinNAT+Lnz3utWXxQ==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4393,6 +4178,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4411,8 +4214,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "16.2.0", @@ -4476,34 +4278,114 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } } }, "dependencies": { + "@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "requires": { + "@babel/highlight": "^7.22.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + }, + "@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.5.2", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -4511,10 +4393,16 @@ "strip-json-comments": "^3.1.1" } }, + "@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -4534,10 +4422,75 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -4558,9 +4511,9 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -4574,9 +4527,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", @@ -4609,20 +4562,19 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, - "@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "requires": { - "@types/ms": "*" - } - }, "@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -4644,18 +4596,18 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, "@types/glob": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", - "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", "dev": true, "requires": { - "@types/minimatch": "*", + "@types/minimatch": "^5.1.2", "@types/node": "*" } }, @@ -4665,14 +4617,6 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", - "requires": { - "@types/unist": "*" - } - }, "@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -4685,99 +4629,95 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, "@types/node": { - "version": "16.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz", - "integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz", + "integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, - "@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, "@types/vscode": { - "version": "1.73.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.73.1.tgz", - "integrity": "sha512-eArfOrAoZVV+Ao9zQOCaFNaeXj4kTCD+bGS2gyNgIFZH9xVMuLMlRrEkhb22NyxycFWKV1UyTh03vhaVHmqVMg==", + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.0.tgz", + "integrity": "sha512-Tfowu2rSW8hVGbqzQLSPlOEiIOYYryTkgJ+chMecpYiJcnw9n0essvSiclnK+Qh/TcSVJHgaK4EMrQDZjZJ/Sw==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", - "integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", + "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/type-utils": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/type-utils": "5.59.11", + "@typescript-eslint/utils": "5.59.11", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", - "integrity": "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", + "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/typescript-estree": "5.59.11", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", - "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", + "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/visitor-keys": "5.45.1" + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/visitor-keys": "5.59.11" } }, "@typescript-eslint/type-utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", - "integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", + "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.45.1", - "@typescript-eslint/utils": "5.45.1", + "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/utils": "5.59.11", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", - "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", + "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", - "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", + "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/visitor-keys": "5.45.1", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/visitor-keys": "5.59.11", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4786,207 +4726,207 @@ } }, "@typescript-eslint/utils": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", - "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", + "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", "dev": true, "requires": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.45.1", - "@typescript-eslint/types": "5.45.1", - "@typescript-eslint/typescript-estree": "5.45.1", + "@typescript-eslint/scope-manager": "5.59.11", + "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/typescript-estree": "5.59.11", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.45.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", - "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", + "version": "5.59.11", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", + "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/types": "5.59.11", "eslint-visitor-keys": "^3.3.0" } }, "@vscode/test-electron": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.1.tgz", - "integrity": "sha512-DUdwSYVc9p/PbGveaq20dbAAXHfvdq4zQ24ILp6PKizOBxrOfMsOq8Vts5nMzeIo0CxtA/RxZLFyDv001PiUSg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.3.tgz", + "integrity": "sha512-hgXCkDP0ibboF1K6seqQYyHAzCURgTwHS/6QU7slhwznDLwsRwg9bhfw1CZdyUEw8vvCmlrKWnd7BlQnI0BC4w==", "dev": true, "requires": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", - "rimraf": "^3.0.2", - "unzipper": "^0.10.11" + "jszip": "^3.10.1", + "semver": "^7.3.8" } }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webpack-cli/configtest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", - "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", - "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "requires": {} }, "@webpack-cli/serve": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", - "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "requires": {} }, @@ -5003,15 +4943,15 @@ "dev": true }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true }, "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "requires": {} }, @@ -5043,6 +4983,32 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -5093,10 +5059,14 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + "atomically": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz", + "integrity": "sha512-sxBhVZUFBFhqSAsYMM3X2oaUi2NVDJ8U026FsIusM8gYXls9AYs/eXzgGrufs1Qjpkxi9zunds+75QUFz+m7UQ==", + "requires": { + "stubborn-fs": "^1.2.4", + "when-exit": "^2.0.0" + } }, "balanced-match": { "version": "1.0.2", @@ -5104,21 +5074,10 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dev": true, - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "binary-extensions": { "version": "2.2.0", @@ -5126,12 +5085,6 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5175,26 +5128,10 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "dev": true - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "optional": true, - "requires": { - "streamsearch": "^1.1.0" - } + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" }, "callsites": { "version": "3.1.0", @@ -5214,15 +5151,6 @@ "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==", "dev": true }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dev": true, - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5233,22 +5161,19 @@ "supports-color": "^7.1.0" } }, - "character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" - }, "chatgpt": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz", - "integrity": "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==", - "requires": { - "eventsource-parser": "^0.0.5", - "expiry-map": "^2.0.0", - "p-timeout": "^6.0.0", - "remark": "^14.0.2", - "strip-markdown": "^5.0.0", - "undici": "^5.13.0", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-5.2.5.tgz", + "integrity": "sha512-DNhBzPb2zTDjJADY44XfngMvsvrvHRq1md2VPXLmnKeP1UCeA1B6pV3s9ZRwlcgjVT0RyM77fRj1xj5V11Vctg==", + "requires": { + "cac": "^6.7.14", + "conf": "^11.0.1", + "eventsource-parser": "^1.0.0", + "js-tiktoken": "^1.0.5", + "keyv": "^4.5.2", + "p-timeout": "^6.1.1", + "quick-lru": "^6.1.1", + "read-pkg-up": "^9.1.0", "uuid": "^9.0.0" } }, @@ -5340,6 +5265,39 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "conf": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-11.0.1.tgz", + "integrity": "sha512-WlLiQboEjKx0bYx2IIRGedBgNjLAxtwPaCSnsjWPST5xR0DB4q8lcsO/bEH9ZRYNcj63Y9vj/JG/5Fg6uWzI0Q==", + "requires": { + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "atomically": "^2.0.0", + "debounce-fn": "^5.1.2", + "dot-prop": "^7.2.0", + "env-paths": "^3.0.0", + "json-schema-typed": "^8.0.1", + "semver": "^7.3.8" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -5357,10 +5315,19 @@ "which": "^2.0.1" } }, + "debounce-fn": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz", + "integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==", + "requires": { + "mimic-fn": "^4.0.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -5371,29 +5338,17 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, - "decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "requires": { - "character-entities": "^2.0.0" - } - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true }, "dir-glob": { "version": "3.0.1", @@ -5413,15 +5368,20 @@ "esutils": "^2.0.2" } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, + "dot-prop": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz", + "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", "requires": { - "readable-stream": "^2.0.2" + "type-fest": "^2.11.2" } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", @@ -5435,25 +5395,38 @@ "dev": true }, "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", + "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", "dev": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, + "env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" + }, "envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, "escalade": { @@ -5469,13 +5442,16 @@ "dev": true }, "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -5484,24 +5460,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -5509,16 +5483,15 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -5543,44 +5516,27 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -5630,28 +5586,14 @@ "dev": true }, "eventsource-parser": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-0.0.5.tgz", - "integrity": "sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==" - }, - "expiry-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/expiry-map/-/expiry-map-2.0.0.tgz", - "integrity": "sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==", - "requires": { - "map-age-cleaner": "^0.2.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz", + "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==" }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.12", @@ -5754,6 +5696,16 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5767,48 +5719,10 @@ "dev": true, "optional": true }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "get-caller-file": { "version": "2.0.5", @@ -5817,16 +5731,16 @@ "dev": true }, "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", + "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" }, "dependencies": { "brace-expansion": { @@ -5839,9 +5753,9 @@ } }, "minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -5865,12 +5779,20 @@ "dev": true }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, "globby": { @@ -5899,11 +5821,16 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -5920,6 +5847,14 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -5947,6 +5882,12 @@ "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -5995,6 +5936,11 @@ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -6004,16 +5950,10 @@ "binary-extensions": "^2.0.0" } }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" - }, "is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -6051,11 +5991,6 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, - "is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -6089,6 +6024,16 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, + "jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -6111,11 +6056,18 @@ } } }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true + "js-tiktoken": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.6.tgz", + "integrity": "sha512-lxHntEupgjWvSh37WxpAW4XN6UBXBtFJOpZZq5HN5oNjDfN7L/iJhHOKjyL/DFtuYXUwn5jfTciLtOWpgQmHjQ==", + "requires": { + "base64-js": "^1.5.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "4.1.0", @@ -6126,365 +6078,133 @@ "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "dev": true - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-age-cleaner": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.2.0.tgz", - "integrity": "sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==", - "requires": { - "p-defer": "^1.0.0" - } - }, - "mdast-util-from-markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz", - "integrity": "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - } - }, - "mdast-util-to-markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz", - "integrity": "sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "longest-streak": "^3.0.0", - "mdast-util-to-string": "^3.0.0", - "micromark-util-decode-string": "^1.0.0", - "unist-util-visit": "^4.0.0", - "zwitch": "^2.0.0" - } - }, - "mdast-util-to-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz", - "integrity": "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromark": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", - "integrity": "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==", - "requires": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "micromark-core-commonmark": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz", - "integrity": "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==", - "requires": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "micromark-factory-destination": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", - "integrity": "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-factory-label": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz", - "integrity": "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "micromark-factory-space": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz", - "integrity": "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-factory-title": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz", - "integrity": "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==", - "requires": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "micromark-factory-whitespace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz", - "integrity": "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==", - "requires": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } + "dev": true }, - "micromark-util-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz", - "integrity": "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==", - "requires": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } + "json-schema-typed": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.1.tgz", + "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" }, - "micromark-util-chunked": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz", - "integrity": "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==", - "requires": { - "micromark-util-symbol": "^1.0.0" - } + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, - "micromark-util-classify-character": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz", - "integrity": "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==", + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, - "micromark-util-combine-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz", - "integrity": "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==", + "keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "requires": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" + "json-buffer": "3.0.1" } }, - "micromark-util-decode-numeric-character-reference": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz", - "integrity": "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==", + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "requires": { - "micromark-util-symbol": "^1.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, - "micromark-util-decode-string": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz", - "integrity": "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==", + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, "requires": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" + "immediate": "~3.0.5" } }, - "micromark-util-encode": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz", - "integrity": "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==" + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "micromark-util-html-tag-name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz", - "integrity": "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==" + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true }, - "micromark-util-normalize-identifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz", - "integrity": "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "requires": { - "micromark-util-symbol": "^1.0.0" + "p-locate": "^5.0.0" } }, - "micromark-util-resolve-all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz", - "integrity": "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==", - "requires": { - "micromark-util-types": "^1.0.0" - } + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, - "micromark-util-sanitize-uri": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz", - "integrity": "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==", + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, - "micromark-util-subtokenize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz", - "integrity": "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" + "yallist": "^4.0.0" } }, - "micromark-util-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz", - "integrity": "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==" + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, - "micromark-util-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz", - "integrity": "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==" + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true }, "micromatch": { "version": "4.0.5", @@ -6511,6 +6231,11 @@ "mime-db": "1.52.0" } }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6520,25 +6245,16 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "dev": true }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, "mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "requires": { "ansi-colors": "4.1.1", @@ -6626,15 +6342,11 @@ } } }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "nanoid": { "version": "3.3.3", @@ -6666,6 +6378,17 @@ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6695,11 +6418,6 @@ "word-wrap": "^1.2.3" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==" - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6719,9 +6437,9 @@ } }, "p-timeout": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz", - "integrity": "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==" + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz", + "integrity": "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==" }, "p-try": { "version": "2.2.0", @@ -6729,6 +6447,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6738,6 +6462,17 @@ "callsites": "^3.0.0" } }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6762,6 +6497,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", + "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1", + "minipass": "^5.0.0 || ^6.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", + "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", + "dev": true + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6843,8 +6596,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "queue-microtask": { "version": "1.2.3", @@ -6852,6 +6604,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz", + "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6861,10 +6618,76 @@ "safe-buffer": "^5.1.0" } }, + "read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "requires": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "requires": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "dependencies": { + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "requires": { + "p-locate": "^6.0.0" + } + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "requires": { + "p-limit": "^4.0.0" + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==" + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" + } + } + }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -6902,49 +6725,17 @@ "resolve": "^1.20.0" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "remark": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz", - "integrity": "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==", - "requires": { - "@types/mdast": "^3.0.0", - "remark-parse": "^10.0.0", - "remark-stringify": "^10.0.0", - "unified": "^10.0.0" - } - }, - "remark-parse": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", - "integrity": "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==", - "requires": { - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "unified": "^10.0.0" - } - }, - "remark-stringify": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz", - "integrity": "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==", - "requires": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.0.0", - "unified": "^10.0.0" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -7019,14 +6810,6 @@ "queue-microtask": "^1.2.2" } }, - "sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "requires": { - "mri": "^1.1.0" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7034,9 +6817,9 @@ "dev": true }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.2.0.tgz", + "integrity": "sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -7048,7 +6831,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -7092,6 +6874,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7114,11 +6902,33 @@ "source-map": "^0.6.0" } }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "optional": true + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" }, "string_decoder": { "version": "1.1.1", @@ -7148,6 +6958,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -7157,21 +6978,25 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "strip-markdown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-markdown/-/strip-markdown-5.0.0.tgz", - "integrity": "sha512-PXSts6Ta9A/TwGxVVSRlQs1ukJTAwwtbip2OheJEjPyfykaQ4sJSTnQWjLTI2vYWNts/R/91/csagp15W8n9gA==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.6", - "unified": "^10.0.0" - } + "stubborn-fs": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.4.tgz", + "integrity": "sha512-KRa4nIRJ8q6uApQbPwYZVhOof8979fw4xbajBWa5kPJFa4nyY3aFaMWVyIVCDnkNCCG/3HLipUZ4QaNlYsmX1w==" }, "supports-color": { "version": "7.2.0", @@ -7195,28 +7020,39 @@ "dev": true }, "terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz", + "integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" } }, "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "dependencies": { + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } } }, "text-table": { @@ -7234,21 +7070,10 @@ "is-number": "^7.0.0" } }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "dev": true - }, - "trough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" - }, "ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", + "integrity": "sha512-n3hBnm6ozJYzwiwt5YRiJZkzktftRpMiBApHaJPoWLA+qetQBAXkHqCLM6nwSdRDimqVtA5ocIkcTRLMTt7yzA==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -7282,90 +7107,16 @@ } }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, - "undici": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", - "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", - "optional": true, - "requires": { - "busboy": "^1.6.0" - } - }, - "unified": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", - "requires": { - "@types/unist": "^2.0.0", - "bail": "^2.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^5.0.0" - } - }, - "unist-util-is": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz", - "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==" - }, - "unist-util-stringify-position": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", - "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", - "requires": { - "@types/unist": "^2.0.0" - } - }, - "unist-util-visit": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.1.tgz", - "integrity": "sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - } - }, - "unist-util-visit-parents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz", - "integrity": "sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - } - }, - "unzipper": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", - "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", - "dev": true, - "requires": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, "update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -7380,7 +7131,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -7396,35 +7146,13 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, - "uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "requires": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - } - }, - "vfile": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.6.tgz", - "integrity": "sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==", - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - } - }, - "vfile-message": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz", - "integrity": "sha512-0yaU+rj2gKAyEk12ffdSbBfjnnj+b1zqTBv3OQCTn8yEB02bsPizwdBPrLJjHnK+cU9EMMcUnNv938XcZIkmdA==", + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "watchpack": { @@ -7438,22 +7166,22 @@ } }, "webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.86.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", + "integrity": "sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.14.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -7462,25 +7190,25 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.1.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" } }, "webpack-cli": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", - "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.1", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^9.4.1", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", @@ -7491,9 +7219,9 @@ }, "dependencies": { "commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true } } @@ -7514,6 +7242,11 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, + "when-exit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz", + "integrity": "sha512-H85ulNwUBU1e6PGxkWUDgxnbohSXD++ah6Xw1VHAN7CtypcbZaC4aYjQ+C2PMVaDkURDuOinNAT+Lnz3utWXxQ==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7552,6 +7285,17 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7567,8 +7311,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { "version": "16.2.0", @@ -7616,11 +7359,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true - }, - "zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" } } } diff --git a/package.json b/package.json index 1d1c950..a284b82 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "chatgpt", "displayName": "ChatGPT: write and improve code using AI", "description": "Extension that allows you to use OpeanAI's ChatGPT inside the IDE (unofficial)", - "version": "0.4.0", - "publisher": "timkmecl", + "version": "0.4.1", + "publisher": "kmandrup", "icon": "resources/extensionIcon.png", "license": "MIT", "repository": { @@ -143,9 +143,24 @@ "title": "chatGPT", "type": "object", "properties": { - "chatgpt.sessionToken": { + "chatgpt.apiKey": { "type": "string", - "description": "A session token you get when logged into chat.openai.com", + "description": "The apiKey you get from chat.openai.com", + "order": 1 + }, + "chatgpt.model": { + "type": "number", + "description": "The model to use (default: turbo-gpt-3.5)", + "order": 1 + }, + "chatgpt.temperature": { + "type": "number", + "description": "The temperature to use (default: 1)", + "order": 1 + }, + "chatgpt.topP": { + "type": "number", + "description": "The top_p sampling to use (default: 0.1)", "order": 1 }, "chatgpt.pasteOnClick": { @@ -210,22 +225,22 @@ "test": "node ./out/test/runTest.js" }, "devDependencies": { - "@types/glob": "^8.0.0", + "@types/glob": "^8.1.0", "@types/mocha": "^10.0.1", - "@types/node": "16.x", - "@types/vscode": "^1.73.0", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "@vscode/test-electron": "^2.2.0", - "eslint": "^8.28.0", - "glob": "^8.0.3", - "mocha": "^10.1.0", - "ts-loader": "^9.4.1", - "typescript": "^4.9.3", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.0" + "@types/node": "20.x", + "@types/vscode": "^1.79.0", + "@typescript-eslint/eslint-plugin": "^5.59.11", + "@typescript-eslint/parser": "^5.59.11", + "@vscode/test-electron": "^2.3.3", + "eslint": "^8.42.0", + "glob": "^10.2.7", + "mocha": "^10.2.0", + "ts-loader": "^9.4.3", + "typescript": "^5.1.3", + "webpack": "^5.86.0", + "webpack-cli": "^5.1.4" }, "dependencies": { - "chatgpt": "^2.0.5" + "chatgpt": "^5.2.5" } } diff --git a/src/extension.ts b/src/extension.ts index 0449cd8..61f426b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,15 +1,20 @@ import * as vscode from 'vscode'; -import { ChatGPTAPI, ChatGPTConversation } from 'chatgpt'; - +import { ChatGPTViewProvider } from './view-provider'; export function activate(context: vscode.ExtensionContext) { - // Get the API session token from the extension's configuration - const config = vscode.workspace.getConfiguration('chatgpt'); - const sessionToken = config.get('sessionToken') as string|undefined; + // Get the API key from the extension's configuration + const config = vscode.workspace.getConfiguration('chatgpt'); + const apiKey = config.get('apiKey') as string|undefined; + const model = config.get('model') as string|undefined; + const temperature = config.get('temperature') as string|undefined; + const topP = config.get('topP') as string|undefined; // Create a new ChatGPTViewProvider instance and register it with the extension's context const provider = new ChatGPTViewProvider(context.extensionUri); - provider.setSessionToken(sessionToken); + provider.setApiKey(apiKey); + provider.setModel(model); + provider.setTemperature(temperature); + provider.setTopP(topP); // Put configuration settings into the provider provider.selectedInsideCodeblock = config.get('selectedInsideCodeblock') || false; @@ -23,8 +28,6 @@ export function activate(context: vscode.ExtensionContext) { }) ); - - // Register the commands that can be called from the extension's package.json const commandHandler = (command:string) => { const config = vscode.workspace.getConfiguration('chatgpt'); @@ -42,16 +45,17 @@ export function activate(context: vscode.ExtensionContext) { prompt: 'Set Conversation ID or delete it to reset the conversation', placeHolder: 'conversationId (leave empty to reset)', value: provider.getConversationId() - }).then((conversationId) => { + }).then((conversationId: string | undefined) => { if (!conversationId) { - provider.setConversationId(); - } else { + provider.resetConversationId(); + } + if (conversationId) { vscode.window.showInputBox({ prompt: 'Set Parent Message ID', placeHolder: 'messageId (leave empty to reset)', value: provider.getParentMessageId() }).then((messageId) => { - provider.setConversationId(conversationId, messageId); + provider.setConversationId({conversationId: messageId}); }); } }); @@ -70,22 +74,20 @@ export function activate(context: vscode.ExtensionContext) { }); let commandResetConversation = vscode.commands.registerCommand('chatgpt.resetConversation', () => { - provider.setConversationId(); + provider.resetConversationId(); }); context.subscriptions.push(commandAsk, commandConversationId, commandExplain, commandRefactor, commandOptimize, commandProblems, commandResetConversation); - - - // Change the extension's session token when configuration is changed + // Change the extension's API key when configuration is changed vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => { - if (event.affectsConfiguration('chatgpt.sessionToken')) { + if (event.affectsConfiguration('chatgpt.apiKey')) { // Get the extension's configuration const config = vscode.workspace.getConfiguration('chatgpt'); - const sessionToken = config.get('sessionToken') as string|undefined; + const apiKey = config.get('apiKey') as string|undefined; // add the new token to the provider - provider.setSessionToken(sessionToken); + provider.setApiKey(apiKey); } else if (event.affectsConfiguration('chatgpt.selectedInsideCodeblock')) { const config = vscode.workspace.getConfiguration('chatgpt'); @@ -108,229 +110,5 @@ export function activate(context: vscode.ExtensionContext) { - - -class ChatGPTViewProvider implements vscode.WebviewViewProvider { - public static readonly viewType = 'chatgpt.chatView'; - - private _view?: vscode.WebviewView; - - // This variable holds a reference to the ChatGPTAPI instance - private _chatGPTAPI?: ChatGPTAPI; - private _conversation?: ChatGPTConversation; - - private _response?: string; - private _prompt?: string; - private _fullPrompt?: string; - - - public selectedInsideCodeblock = false; - public pasteOnClick = true; - public keepConversation = true; - public timeoutLength = 60; - private _sessionToken?: string; - - // In the constructor, we store the URI of the extension - constructor(private readonly _extensionUri: vscode.Uri) { - - } - - // Set the session token and create a new API instance based on this token - public setSessionToken(sessionToken?: string) { - this._sessionToken = sessionToken; - this._newAPI(); - } - - public setConversationId(conversationId?: string, parentMessageId?: string) { - if (!conversationId || !parentMessageId) { - this._conversation = this._chatGPTAPI?.getConversation(); - } else if (conversationId && parentMessageId) { - this._conversation = this._chatGPTAPI?.getConversation({conversationId: conversationId, parentMessageId: parentMessageId}); - } - } - - public getConversationId() { - return this._conversation?.conversationId; - } - public getParentMessageId() { - return this._conversation?.parentMessageId; - } - - // This private method initializes a new ChatGPTAPI instance, using the session token if it is set - private _newAPI() { - if (!this._sessionToken) { - console.warn("Session token not set"); - }else{ - this._chatGPTAPI = new ChatGPTAPI({ - sessionToken: this._sessionToken - }); - this._conversation = this._chatGPTAPI.getConversation(); - } - } - - public resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken, - ) { - this._view = webviewView; - - // set options for the webview - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - localResourceRoots: [ - this._extensionUri - ] - }; - - // set the HTML for the webview - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - - // add an event listener for messages received by the webview - webviewView.webview.onDidReceiveMessage(data => { - switch (data.type) { - case 'codeSelected': - { - // do nothing if the pasteOnClick option is disabled - if (!this.pasteOnClick) { - break; - } - - let code = data.value; - code = code.replace(/([^\\])(\$)([^{0-9])/g, "$1\\$$$3"); - - // insert the code as a snippet into the active text editor - vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(code)); - break; - } - case 'prompt': - { - this.search(data.value); - } - } - }); - } - - - - public async search(prompt?:string) { - this._prompt = prompt; - if (!prompt) { - prompt = ''; - }; - - // Check if the ChatGPTAPI instance is defined - if (!this._chatGPTAPI) { - this._newAPI(); - } - - // focus gpt activity from activity bar - if (!this._view) { - await vscode.commands.executeCommand('chatgpt.chatView.focus'); - } else { - this._view?.show?.(true); - } - - let response = ''; - - // Get the selected text of the active editor - const selection = vscode.window.activeTextEditor?.selection; - const selectedText = vscode.window.activeTextEditor?.document.getText(selection); - let searchPrompt = ''; - - if (selection && selectedText) { - // If there is a selection, add the prompt and the selected text to the search prompt - if (this.selectedInsideCodeblock) { - searchPrompt = `${prompt}\n\`\`\`\n${selectedText}\n\`\`\``; - } else { - searchPrompt = `${prompt}\n${selectedText}\n`; - } - } else { - // Otherwise, just use the prompt if user typed it - searchPrompt = prompt; - } - - this._fullPrompt = searchPrompt; - - - if (!this._chatGPTAPI || !this._conversation) { - response = '[ERROR] Please enter an API key in the extension settings'; - } else { - // If successfully signed in - console.log("sendMessage"); - - // Make sure the prompt is shown - this._view?.webview.postMessage({ type: 'setPrompt', value: this._prompt }); - - if (this._view) { - this._view.webview.postMessage({ type: 'addResponse', value: '...' }); - } - - let agent; - if (this.keepConversation) { - agent = this._conversation; - } else { - agent = this._chatGPTAPI; - } - - try { - // Send the search prompt to the ChatGPTAPI instance and store the response - response = await agent.sendMessage(searchPrompt, { - onProgress: (partialResponse) => { - if (this._view && this._view.visible) { - this._view.webview.postMessage({ type: 'addResponse', value: partialResponse }); - } - }, - timeoutMs: this.timeoutLength * 1000 - }); - } catch (e) { - console.error(e); - response = `[ERROR] ${e}`; - } - } - - // Saves the response - this._response = response; - - // Show the view and send a message to the webview with the response - if (this._view) { - this._view.show?.(true); - this._view.webview.postMessage({ type: 'addResponse', value: response }); - } - } - - private _getHtmlForWebview(webview: vscode.Webview) { - - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js')); - const microlightUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'microlight.min.js')); - const tailwindUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'showdown.min.js')); - const showdownUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'tailwind.min.js')); - - return ` - - - - - - - - - - - - -
-
- - - - `; - } -} - // This method is called when your extension is deactivated export function deactivate() {} \ No newline at end of file diff --git a/src/view-provider.ts b/src/view-provider.ts new file mode 100644 index 0000000..94e0819 --- /dev/null +++ b/src/view-provider.ts @@ -0,0 +1,243 @@ +import * as vscode from 'vscode'; +import { ChatGPTAPI, ChatMessage, SendMessageOptions } from 'chatgpt'; + +export class ChatGPTViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = 'chatgpt.chatView'; + + private _view?: vscode.WebviewView; + + // This variable holds a reference to the ChatGPTAPI instance + private _chatGPTAPI?: ChatGPTAPI; + private _conversationId?: string; + private _parentMessageId?: string; + private _prompt?: string; + private _fullPrompt?: string; + private _response?: string + + public selectedInsideCodeblock = false; + public pasteOnClick = true; + public keepConversation = true; + public timeoutLength = 60; + private _apiKey?: string; + private _model?: string; + private _topP?: number; + private _temperature?: number; + + // In the constructor, we store the URI of the extension + constructor(private readonly _extensionUri: vscode.Uri) { + + } + + // Set the API key and create a new API instance based on this key + public setApiKey(apiKey?: string) { + this._apiKey = apiKey; + this._newAPI(); + } + + public setModel(model?: string) { + this._model = model; + } + + public setTemperature(temperature?: string) { + if (!temperature) { return; } + this._temperature = parseFloat(temperature); + } + + public setTopP(topP?: string) { + if (!topP) { + topP = '0.1'; // good value for code generation + } + this._topP = parseFloat(topP); + } + + public resetConversationId() { + this._conversationId = undefined; + } + + public setConversationId(opts: {conversationId?: string, parentMessageId?: string}) { + this._conversationId = opts.conversationId || this._conversationId; + this._parentMessageId = opts.parentMessageId || this._parentMessageId; + } + + public getConversationId() { + return this._conversationId; + } + public getParentMessageId() { + return this._parentMessageId; + } + + // This private method initializes a new ChatGPTAPI instance, using the API key if it is set + private _newAPI() { + if (!this._apiKey) { + console.warn("Api key not set"); + }else{ + this._chatGPTAPI = new ChatGPTAPI({ + apiKey: this._apiKey, + completionParams: { + model: this._model, + temperature: this._temperature, + top_p: this._topP + } + }); + } + } + + public resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken, + ) { + this._view = webviewView; + + // set options for the webview + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + localResourceRoots: [ + this._extensionUri + ] + }; + + // set the HTML for the webview + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + + // add an event listener for messages received by the webview + webviewView.webview.onDidReceiveMessage(data => { + switch (data.type) { + case 'codeSelected': + { + // do nothing if the pasteOnClick option is disabled + if (!this.pasteOnClick) { + break; + } + + let code = data.value; + code = code.replace(/([^\\])(\$)([^{0-9])/g, "$1\\$$$3"); + + // insert the code as a snippet into the active text editor + vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(code)); + break; + } + case 'prompt': + { + this.search(data.value); + } + } + }); + } + + + + public async search(prompt?:string) { + this._prompt = prompt; + if (!prompt) { + prompt = ''; + }; + + // Check if the ChatGPTAPI instance is defined + if (!this._chatGPTAPI) { + this._newAPI(); + } + + // focus gpt activity from activity bar + if (!this._view) { + await vscode.commands.executeCommand('chatgpt.chatView.focus'); + } else { + this._view?.show?.(true); + } + + let response = ''; + let chatResponse: ChatMessage = {} as ChatMessage; + + // Get the selected text of the active editor + const selection = vscode.window.activeTextEditor?.selection; + const selectedText = vscode.window.activeTextEditor?.document.getText(selection); + let searchPrompt = ''; + + if (selection && selectedText) { + // If there is a selection, add the prompt and the selected text to the search prompt + if (this.selectedInsideCodeblock) { + searchPrompt = `${prompt}\n\`\`\`\n${selectedText}\n\`\`\``; + } else { + searchPrompt = `${prompt}\n${selectedText}\n`; + } + } else { + // Otherwise, just use the prompt if user typed it + searchPrompt = prompt; + } + + this._fullPrompt = searchPrompt; + + if (!this._chatGPTAPI || !this._conversationId) { + response = '[ERROR] Please enter an API key in the extension settings'; + } else { + // If successfully signed in + console.log("sendMessage"); + + // Make sure the prompt is shown + this._view?.webview.postMessage({ type: 'setPrompt', value: this._prompt }); + + if (this._view) { + this._view.webview.postMessage({ type: 'addResponse', value: '...' }); + } + try { + let sendOpts: SendMessageOptions = { + onProgress: (partialResponse: any) => { + if (this._view && this._view.visible) { + this._view.webview.postMessage({ type: 'addResponse', value: partialResponse }); + } + }, + timeoutMs: this.timeoutLength * 1000 + } + if (this.keepConversation) { + sendOpts.conversationId = this._conversationId; + } + // Send the search prompt to the ChatGPTAPI instance and store the response + chatResponse = await this._chatGPTAPI.sendMessage(searchPrompt, sendOpts); + } catch (e) { + console.error(e); + response = `[ERROR] ${e}`; + } + } + + // Saves the response + this._response = chatResponse ? chatResponse.text : response; + + // Show the view and send a message to the webview with the response + if (this._view) { + this._view.show?.(true); + this._view.webview.postMessage({ type: 'addResponse', value: response }); + } + } + + private _getHtmlForWebview(webview: vscode.Webview) { + + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js')); + const microlightUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'microlight.min.js')); + const tailwindUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'showdown.min.js')); + const showdownUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'scripts', 'tailwind.min.js')); + + return ` + + + + + + + + + + + + +
+
+ + + + `; + } +} diff --git a/yarn.lock b/yarn.lock index 2545473..2de2904 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,30 +2,68 @@ # yarn lockfile v1 +"@babel/code-frame@^7.0.0": + "integrity" "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==" + "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz" + "version" "7.22.5" + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/helper-validator-identifier@^7.22.5": + "integrity" "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz" + "version" "7.22.5" + +"@babel/highlight@^7.22.5": + "integrity" "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==" + "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz" + "version" "7.22.5" + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + "chalk" "^2.0.0" + "js-tokens" "^4.0.0" + "@discoveryjs/json-ext@^0.5.0": "integrity" "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" "resolved" "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" "version" "0.5.7" -"@eslint/eslintrc@^1.3.3": - "integrity" "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==" - "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz" - "version" "1.3.3" +"@eslint-community/eslint-utils@^4.2.0": + "integrity" "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==" + "resolved" "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" + "version" "4.4.0" + dependencies: + "eslint-visitor-keys" "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + "integrity" "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==" + "resolved" "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz" + "version" "4.5.1" + +"@eslint/eslintrc@^2.0.3": + "integrity" "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==" + "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz" + "version" "2.0.3" dependencies: "ajv" "^6.12.4" "debug" "^4.3.2" - "espree" "^9.4.0" - "globals" "^13.15.0" + "espree" "^9.5.2" + "globals" "^13.19.0" "ignore" "^5.2.0" "import-fresh" "^3.2.1" "js-yaml" "^4.1.0" "minimatch" "^3.1.2" "strip-json-comments" "^3.1.1" -"@humanwhocodes/config-array@^0.11.6": - "integrity" "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==" - "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz" - "version" "0.11.7" +"@eslint/js@8.42.0": + "integrity" "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==" + "resolved" "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz" + "version" "8.42.0" + +"@humanwhocodes/config-array@^0.11.10": + "integrity" "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz" + "version" "0.11.10" dependencies: "@humanwhocodes/object-schema" "^1.2.1" "debug" "^4.1.1" @@ -41,10 +79,22 @@ "resolved" "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" "version" "1.2.1" +"@isaacs/cliui@^8.0.2": + "integrity" "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==" + "resolved" "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + "version" "8.0.2" + dependencies: + "string-width" "^5.1.2" + "string-width-cjs" "npm:string-width@^4.2.0" + "strip-ansi" "^7.0.1" + "strip-ansi-cjs" "npm:strip-ansi@^6.0.1" + "wrap-ansi" "^8.1.0" + "wrap-ansi-cjs" "npm:wrap-ansi@^7.0.0" + "@jridgewell/gen-mapping@^0.3.0": - "integrity" "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==" - "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" - "version" "0.3.2" + "integrity" "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" + "version" "0.3.3" dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -60,10 +110,10 @@ "resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" "version" "1.1.2" -"@jridgewell/source-map@^0.3.2": - "integrity" "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==" - "resolved" "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" - "version" "0.3.2" +"@jridgewell/source-map@^0.3.3": + "integrity" "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==" + "resolved" "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz" + "version" "0.3.3" dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" @@ -73,10 +123,10 @@ "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" "version" "1.4.14" -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": - "integrity" "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==" - "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" - "version" "0.3.17" +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + "integrity" "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz" + "version" "0.3.18" dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" @@ -102,18 +152,16 @@ "@nodelib/fs.scandir" "2.1.5" "fastq" "^1.6.0" +"@pkgjs/parseargs@^0.11.0": + "integrity" "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==" + "resolved" "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + "version" "0.11.0" + "@tootallnate/once@1": "integrity" "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" "resolved" "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" "version" "1.1.2" -"@types/debug@^4.0.0": - "integrity" "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==" - "resolved" "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz" - "version" "4.1.7" - dependencies: - "@types/ms" "*" - "@types/eslint-scope@^3.7.3": "integrity" "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==" "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" @@ -130,17 +178,17 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.51": - "integrity" "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" - "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz" - "version" "0.0.51" +"@types/estree@*", "@types/estree@^1.0.0": + "integrity" "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz" + "version" "1.0.1" -"@types/glob@^8.0.0": - "integrity" "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==" - "resolved" "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz" - "version" "8.0.0" +"@types/glob@^8.1.0": + "integrity" "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==" + "resolved" "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz" + "version" "8.1.0" dependencies: - "@types/minimatch" "*" + "@types/minimatch" "^5.1.2" "@types/node" "*" "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": @@ -148,14 +196,7 @@ "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" "version" "7.0.11" -"@types/mdast@^3.0.0": - "integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==" - "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz" - "version" "3.0.10" - dependencies: - "@types/unist" "*" - -"@types/minimatch@*": +"@types/minimatch@^5.1.2": "integrity" "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" "resolved" "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz" "version" "5.1.2" @@ -165,259 +206,255 @@ "resolved" "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz" "version" "10.0.1" -"@types/ms@*": - "integrity" "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - "resolved" "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz" - "version" "0.7.31" +"@types/node@*", "@types/node@20.x": + "integrity" "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz" + "version" "20.3.0" -"@types/node@*", "@types/node@16.x": - "integrity" "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==" - "resolved" "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz" - "version" "16.18.6" +"@types/normalize-package-data@^2.4.1": + "integrity" "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + "resolved" "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" + "version" "2.4.1" "@types/semver@^7.3.12": - "integrity" "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" - "resolved" "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz" - "version" "7.3.13" - -"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.6": - "integrity" "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - "resolved" "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" - "version" "2.0.6" - -"@types/vscode@^1.73.0": - "integrity" "sha512-eArfOrAoZVV+Ao9zQOCaFNaeXj4kTCD+bGS2gyNgIFZH9xVMuLMlRrEkhb22NyxycFWKV1UyTh03vhaVHmqVMg==" - "resolved" "https://registry.npmjs.org/@types/vscode/-/vscode-1.73.1.tgz" - "version" "1.73.1" - -"@typescript-eslint/eslint-plugin@^5.45.0": - "integrity" "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz" - "version" "5.45.1" - dependencies: - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/type-utils" "5.45.1" - "@typescript-eslint/utils" "5.45.1" + "integrity" "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" + "resolved" "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz" + "version" "7.5.0" + +"@types/vscode@^1.79.0": + "integrity" "sha512-Tfowu2rSW8hVGbqzQLSPlOEiIOYYryTkgJ+chMecpYiJcnw9n0essvSiclnK+Qh/TcSVJHgaK4EMrQDZjZJ/Sw==" + "resolved" "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.0.tgz" + "version" "1.79.0" + +"@typescript-eslint/eslint-plugin@^5.59.11": + "integrity" "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz" + "version" "5.59.11" + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.59.11" + "@typescript-eslint/type-utils" "5.59.11" + "@typescript-eslint/utils" "5.59.11" "debug" "^4.3.4" + "grapheme-splitter" "^1.0.4" "ignore" "^5.2.0" "natural-compare-lite" "^1.4.0" - "regexpp" "^3.2.0" "semver" "^7.3.7" "tsutils" "^3.21.0" -"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.45.0": - "integrity" "sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.59.11": + "integrity" "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz" + "version" "5.59.11" dependencies: - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/typescript-estree" "5.45.1" + "@typescript-eslint/scope-manager" "5.59.11" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/typescript-estree" "5.59.11" "debug" "^4.3.4" -"@typescript-eslint/scope-manager@5.45.1": - "integrity" "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/scope-manager@5.59.11": + "integrity" "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz" + "version" "5.59.11" dependencies: - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/visitor-keys" "5.45.1" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/visitor-keys" "5.59.11" -"@typescript-eslint/type-utils@5.45.1": - "integrity" "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/type-utils@5.59.11": + "integrity" "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz" + "version" "5.59.11" dependencies: - "@typescript-eslint/typescript-estree" "5.45.1" - "@typescript-eslint/utils" "5.45.1" + "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/utils" "5.59.11" "debug" "^4.3.4" "tsutils" "^3.21.0" -"@typescript-eslint/types@5.45.1": - "integrity" "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/types@5.59.11": + "integrity" "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz" + "version" "5.59.11" -"@typescript-eslint/typescript-estree@5.45.1": - "integrity" "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/typescript-estree@5.59.11": + "integrity" "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz" + "version" "5.59.11" dependencies: - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/visitor-keys" "5.45.1" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/visitor-keys" "5.59.11" "debug" "^4.3.4" "globby" "^11.1.0" "is-glob" "^4.0.3" "semver" "^7.3.7" "tsutils" "^3.21.0" -"@typescript-eslint/utils@5.45.1": - "integrity" "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/utils@5.59.11": + "integrity" "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz" + "version" "5.59.11" dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/typescript-estree" "5.45.1" + "@typescript-eslint/scope-manager" "5.59.11" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/typescript-estree" "5.59.11" "eslint-scope" "^5.1.1" - "eslint-utils" "^3.0.0" "semver" "^7.3.7" -"@typescript-eslint/visitor-keys@5.45.1": - "integrity" "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==" - "resolved" "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz" - "version" "5.45.1" +"@typescript-eslint/visitor-keys@5.59.11": + "integrity" "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz" + "version" "5.59.11" dependencies: - "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/types" "5.59.11" "eslint-visitor-keys" "^3.3.0" -"@vscode/test-electron@^2.2.0": - "integrity" "sha512-DUdwSYVc9p/PbGveaq20dbAAXHfvdq4zQ24ILp6PKizOBxrOfMsOq8Vts5nMzeIo0CxtA/RxZLFyDv001PiUSg==" - "resolved" "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.1.tgz" - "version" "2.2.1" +"@vscode/test-electron@^2.3.3": + "integrity" "sha512-hgXCkDP0ibboF1K6seqQYyHAzCURgTwHS/6QU7slhwznDLwsRwg9bhfw1CZdyUEw8vvCmlrKWnd7BlQnI0BC4w==" + "resolved" "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.3.tgz" + "version" "2.3.3" dependencies: "http-proxy-agent" "^4.0.1" "https-proxy-agent" "^5.0.0" - "rimraf" "^3.0.2" - "unzipper" "^0.10.11" - -"@webassemblyjs/ast@1.11.1": - "integrity" "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - -"@webassemblyjs/floating-point-hex-parser@1.11.1": - "integrity" "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" - "version" "1.11.1" - -"@webassemblyjs/helper-api-error@1.11.1": - "integrity" "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" - "version" "1.11.1" - -"@webassemblyjs/helper-buffer@1.11.1": - "integrity" "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" - "version" "1.11.1" - -"@webassemblyjs/helper-numbers@1.11.1": - "integrity" "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "jszip" "^3.10.1" + "semver" "^7.3.8" + +"@webassemblyjs/ast@^1.11.5", "@webassemblyjs/ast@1.11.6": + "integrity" "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + "integrity" "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz" + "version" "1.11.6" + +"@webassemblyjs/helper-api-error@1.11.6": + "integrity" "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz" + "version" "1.11.6" + +"@webassemblyjs/helper-buffer@1.11.6": + "integrity" "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz" + "version" "1.11.6" + +"@webassemblyjs/helper-numbers@1.11.6": + "integrity" "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - "integrity" "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" - "version" "1.11.1" +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + "integrity" "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz" + "version" "1.11.6" -"@webassemblyjs/helper-wasm-section@1.11.1": - "integrity" "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" - "version" "1.11.1" +"@webassemblyjs/helper-wasm-section@1.11.6": + "integrity" "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz" + "version" "1.11.6" dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" -"@webassemblyjs/ieee754@1.11.1": - "integrity" "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" - "version" "1.11.1" +"@webassemblyjs/ieee754@1.11.6": + "integrity" "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz" + "version" "1.11.6" dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - "integrity" "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" - "version" "1.11.1" +"@webassemblyjs/leb128@1.11.6": + "integrity" "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz" + "version" "1.11.6" dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - "integrity" "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" - "version" "1.11.1" - -"@webassemblyjs/wasm-edit@1.11.1": - "integrity" "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - "integrity" "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - "integrity" "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - "integrity" "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - "integrity" "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" - "version" "1.11.1" - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.6": + "integrity" "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz" + "version" "1.11.6" + +"@webassemblyjs/wasm-edit@^1.11.5": + "integrity" "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + "integrity" "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + "integrity" "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@^1.11.5", "@webassemblyjs/wasm-parser@1.11.6": + "integrity" "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + "integrity" "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz" + "version" "1.11.6" + dependencies: + "@webassemblyjs/ast" "1.11.6" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.0.1": - "integrity" "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==" - "resolved" "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz" - "version" "2.0.1" +"@webpack-cli/configtest@^2.1.1": + "integrity" "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==" + "resolved" "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" + "version" "2.1.1" -"@webpack-cli/info@^2.0.1": - "integrity" "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==" - "resolved" "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz" - "version" "2.0.1" +"@webpack-cli/info@^2.0.2": + "integrity" "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==" + "resolved" "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" + "version" "2.0.2" -"@webpack-cli/serve@^2.0.1": - "integrity" "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==" - "resolved" "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz" - "version" "2.0.1" +"@webpack-cli/serve@^2.0.5": + "integrity" "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==" + "resolved" "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" + "version" "2.0.5" "@xtuc/ieee754@^1.2.0": "integrity" "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" @@ -429,20 +466,20 @@ "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" "version" "4.2.2" -"acorn-import-assertions@^1.7.6": - "integrity" "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" - "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" - "version" "1.8.0" +"acorn-import-assertions@^1.9.0": + "integrity" "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" + "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz" + "version" "1.9.0" "acorn-jsx@^5.3.2": "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" "version" "5.3.2" -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8", "acorn@^8.5.0", "acorn@^8.7.1", "acorn@^8.8.0": - "integrity" "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" - "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz" - "version" "8.8.1" +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8", "acorn@^8.7.1", "acorn@^8.8.0", "acorn@^8.8.2": + "integrity" "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" + "version" "8.8.2" "agent-base@6": "integrity" "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==" @@ -451,6 +488,13 @@ dependencies: "debug" "4" +"ajv-formats@^2.1.1": + "integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==" + "resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "ajv" "^8.0.0" + "ajv-keywords@^3.5.2": "integrity" "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" @@ -466,6 +510,26 @@ "json-schema-traverse" "^0.4.1" "uri-js" "^4.2.2" +"ajv@^8.0.0": + "integrity" "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + "version" "8.12.0" + dependencies: + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" + +"ajv@^8.12.0": + "integrity" "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + "version" "8.12.0" + dependencies: + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" + "ansi-colors@4.1.1": "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" @@ -476,6 +540,18 @@ "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" "version" "5.0.1" +"ansi-regex@^6.0.1": + "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + "version" "6.0.1" + +"ansi-styles@^3.2.1": + "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + "version" "3.2.1" + dependencies: + "color-convert" "^1.9.0" + "ansi-styles@^4.0.0", "ansi-styles@^4.1.0": "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" @@ -483,6 +559,11 @@ dependencies: "color-convert" "^2.0.1" +"ansi-styles@^6.1.0": + "integrity" "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + "version" "6.2.1" + "anymatch@~3.1.2": "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==" "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" @@ -501,39 +582,29 @@ "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" "version" "2.1.0" -"bail@^2.0.0": - "integrity" "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" - "resolved" "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz" - "version" "2.0.2" +"atomically@^2.0.0": + "integrity" "sha512-sxBhVZUFBFhqSAsYMM3X2oaUi2NVDJ8U026FsIusM8gYXls9AYs/eXzgGrufs1Qjpkxi9zunds+75QUFz+m7UQ==" + "resolved" "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "stubborn-fs" "^1.2.4" + "when-exit" "^2.0.0" "balanced-match@^1.0.0": "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" "version" "1.0.2" -"big-integer@^1.6.17": - "integrity" "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" - "resolved" "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz" - "version" "1.6.51" +"base64-js@^1.5.1": + "integrity" "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "resolved" "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + "version" "1.5.1" "binary-extensions@^2.0.0": "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" "version" "2.2.0" -"binary@~0.3.0": - "integrity" "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==" - "resolved" "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz" - "version" "0.3.0" - dependencies: - "buffers" "~0.1.1" - "chainsaw" "~0.1.0" - -"bluebird@~3.4.1": - "integrity" "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" - "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz" - "version" "3.4.7" - "brace-expansion@^1.1.7": "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -576,22 +647,10 @@ "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" "version" "1.1.2" -"buffer-indexof-polyfill@~1.0.0": - "integrity" "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==" - "resolved" "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz" - "version" "1.0.2" - -"buffers@~0.1.1": - "integrity" "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==" - "resolved" "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" - "version" "0.1.1" - -"busboy@^1.6.0": - "integrity" "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==" - "resolved" "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" - "version" "1.6.0" - dependencies: - "streamsearch" "^1.1.0" +"cac@^6.7.14": + "integrity" "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" + "resolved" "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" + "version" "6.7.14" "callsites@^3.0.0": "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" @@ -608,12 +667,14 @@ "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz" "version" "1.0.30001436" -"chainsaw@~0.1.0": - "integrity" "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==" - "resolved" "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz" - "version" "0.1.0" +"chalk@^2.0.0": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" dependencies: - "traverse" ">=0.3.0 <0.4" + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" "chalk@^4.0.0", "chalk@^4.1.0": "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" @@ -623,24 +684,20 @@ "ansi-styles" "^4.1.0" "supports-color" "^7.1.0" -"character-entities@^2.0.0": - "integrity" "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" - "resolved" "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz" - "version" "2.0.2" - -"chatgpt@^2.0.5": - "integrity" "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==" - "resolved" "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz" - "version" "2.0.5" - dependencies: - "eventsource-parser" "^0.0.5" - "expiry-map" "^2.0.0" - "p-timeout" "^6.0.0" - "remark" "^14.0.2" - "strip-markdown" "^5.0.0" +"chatgpt@^5.2.5": + "integrity" "sha512-DNhBzPb2zTDjJADY44XfngMvsvrvHRq1md2VPXLmnKeP1UCeA1B6pV3s9ZRwlcgjVT0RyM77fRj1xj5V11Vctg==" + "resolved" "https://registry.npmjs.org/chatgpt/-/chatgpt-5.2.5.tgz" + "version" "5.2.5" + dependencies: + "cac" "^6.7.14" + "conf" "^11.0.1" + "eventsource-parser" "^1.0.0" + "js-tiktoken" "^1.0.5" + "keyv" "^4.5.2" + "p-timeout" "^6.1.1" + "quick-lru" "^6.1.1" + "read-pkg-up" "^9.1.0" "uuid" "^9.0.0" - optionalDependencies: - "undici" "^5.13.0" "chokidar@3.5.3": "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" @@ -680,6 +737,13 @@ "kind-of" "^6.0.2" "shallow-clone" "^3.0.0" +"color-convert@^1.9.0": + "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + "version" "1.9.3" + dependencies: + "color-name" "1.1.3" + "color-convert@^2.0.1": "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" @@ -692,32 +756,51 @@ "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" "version" "1.1.4" +"color-name@1.1.3": + "integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "version" "1.1.3" + "colorette@^2.0.14": "integrity" "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" "version" "2.0.19" +"commander@^10.0.1": + "integrity" "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + "resolved" "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" + "version" "10.0.1" + "commander@^2.20.0": "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" "version" "2.20.3" -"commander@^9.4.1": - "integrity" "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==" - "resolved" "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz" - "version" "9.4.1" - "concat-map@0.0.1": "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" "version" "0.0.1" +"conf@^11.0.1": + "integrity" "sha512-WlLiQboEjKx0bYx2IIRGedBgNjLAxtwPaCSnsjWPST5xR0DB4q8lcsO/bEH9ZRYNcj63Y9vj/JG/5Fg6uWzI0Q==" + "resolved" "https://registry.npmjs.org/conf/-/conf-11.0.1.tgz" + "version" "11.0.1" + dependencies: + "ajv" "^8.12.0" + "ajv-formats" "^2.1.1" + "atomically" "^2.0.0" + "debounce-fn" "^5.1.2" + "dot-prop" "^7.2.0" + "env-paths" "^3.0.0" + "json-schema-typed" "^8.0.1" + "semver" "^7.3.8" + "core-util-is@~1.0.0": "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" "version" "1.0.3" -"cross-spawn@^7.0.2", "cross-spawn@^7.0.3": +"cross-spawn@^7.0.0", "cross-spawn@^7.0.2", "cross-spawn@^7.0.3": "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" "version" "7.0.3" @@ -726,7 +809,14 @@ "shebang-command" "^2.0.0" "which" "^2.0.1" -"debug@^4.0.0", "debug@^4.1.1", "debug@^4.3.2", "debug@^4.3.4", "debug@4", "debug@4.3.4": +"debounce-fn@^5.1.2": + "integrity" "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==" + "resolved" "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "mimic-fn" "^4.0.0" + +"debug@^4.1.1", "debug@^4.3.2", "debug@^4.3.4", "debug@4", "debug@4.3.4": "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" "version" "4.3.4" @@ -738,24 +828,12 @@ "resolved" "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" "version" "4.0.0" -"decode-named-character-reference@^1.0.0": - "integrity" "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==" - "resolved" "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "character-entities" "^2.0.0" - "deep-is@^0.1.3": "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" "version" "0.1.4" -"dequal@^2.0.0": - "integrity" "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - "resolved" "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" - "version" "2.0.3" - -"diff@^5.0.0", "diff@5.0.0": +"diff@5.0.0": "integrity" "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" "resolved" "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" "version" "5.0.0" @@ -774,12 +852,17 @@ dependencies: "esutils" "^2.0.2" -"duplexer2@~0.1.4": - "integrity" "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==" - "resolved" "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz" - "version" "0.1.4" +"dot-prop@^7.2.0": + "integrity" "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==" + "resolved" "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz" + "version" "7.2.0" dependencies: - "readable-stream" "^2.0.2" + "type-fest" "^2.11.2" + +"eastasianwidth@^0.2.0": + "integrity" "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "resolved" "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + "version" "0.2.0" "electron-to-chromium@^1.4.251": "integrity" "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" @@ -791,29 +874,51 @@ "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" "version" "8.0.0" -"enhanced-resolve@^5.0.0", "enhanced-resolve@^5.10.0": - "integrity" "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==" - "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz" - "version" "5.12.0" +"emoji-regex@^9.2.2": + "integrity" "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + "version" "9.2.2" + +"enhanced-resolve@^5.0.0", "enhanced-resolve@^5.14.1": + "integrity" "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==" + "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz" + "version" "5.14.1" dependencies: "graceful-fs" "^4.2.4" "tapable" "^2.2.0" +"env-paths@^3.0.0": + "integrity" "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" + "resolved" "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz" + "version" "3.0.0" + "envinfo@^7.7.3": "integrity" "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" "resolved" "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" "version" "7.8.1" -"es-module-lexer@^0.9.0": - "integrity" "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" - "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" - "version" "0.9.3" +"error-ex@^1.3.1": + "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==" + "resolved" "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + "version" "1.3.2" + dependencies: + "is-arrayish" "^0.2.1" + +"es-module-lexer@^1.2.1": + "integrity" "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" + "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz" + "version" "1.3.0" "escalade@^3.1.1": "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" "version" "3.1.1" +"escape-string-regexp@^1.0.5": + "integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "version" "1.0.5" + "escape-string-regexp@^4.0.0", "escape-string-regexp@4.0.0": "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" @@ -827,38 +932,29 @@ "esrecurse" "^4.3.0" "estraverse" "^4.1.1" -"eslint-scope@^7.1.1": - "integrity" "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==" - "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" - "version" "7.1.1" +"eslint-scope@^7.2.0": + "integrity" "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz" + "version" "7.2.0" dependencies: "esrecurse" "^4.3.0" "estraverse" "^5.2.0" -"eslint-utils@^3.0.0": - "integrity" "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==" - "resolved" "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" - "version" "3.0.0" - dependencies: - "eslint-visitor-keys" "^2.0.0" - -"eslint-visitor-keys@^2.0.0": - "integrity" "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" - "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" - "version" "2.1.0" - -"eslint-visitor-keys@^3.3.0": - "integrity" "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" - "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" - "version" "3.3.0" - -"eslint@*", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^8.28.0", "eslint@>=5": - "integrity" "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==" - "resolved" "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz" - "version" "8.29.0" - dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" +"eslint-visitor-keys@^3.3.0", "eslint-visitor-keys@^3.4.1": + "integrity" "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz" + "version" "3.4.1" + +"eslint@*", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.42.0": + "integrity" "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==" + "resolved" "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz" + "version" "8.42.0" + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.3" + "@eslint/js" "8.42.0" + "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" "ajv" "^6.10.0" @@ -867,24 +963,22 @@ "debug" "^4.3.2" "doctrine" "^3.0.0" "escape-string-regexp" "^4.0.0" - "eslint-scope" "^7.1.1" - "eslint-utils" "^3.0.0" - "eslint-visitor-keys" "^3.3.0" - "espree" "^9.4.0" - "esquery" "^1.4.0" + "eslint-scope" "^7.2.0" + "eslint-visitor-keys" "^3.4.1" + "espree" "^9.5.2" + "esquery" "^1.4.2" "esutils" "^2.0.2" "fast-deep-equal" "^3.1.3" "file-entry-cache" "^6.0.1" "find-up" "^5.0.0" "glob-parent" "^6.0.2" - "globals" "^13.15.0" - "grapheme-splitter" "^1.0.4" + "globals" "^13.19.0" + "graphemer" "^1.4.0" "ignore" "^5.2.0" "import-fresh" "^3.0.0" "imurmurhash" "^0.1.4" "is-glob" "^4.0.0" "is-path-inside" "^3.0.3" - "js-sdsl" "^4.1.4" "js-yaml" "^4.1.0" "json-stable-stringify-without-jsonify" "^1.0.1" "levn" "^0.4.1" @@ -892,24 +986,23 @@ "minimatch" "^3.1.2" "natural-compare" "^1.4.0" "optionator" "^0.9.1" - "regexpp" "^3.2.0" "strip-ansi" "^6.0.1" "strip-json-comments" "^3.1.0" "text-table" "^0.2.0" -"espree@^9.4.0": - "integrity" "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==" - "resolved" "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz" - "version" "9.4.1" +"espree@^9.5.2": + "integrity" "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==" + "resolved" "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz" + "version" "9.5.2" dependencies: "acorn" "^8.8.0" "acorn-jsx" "^5.3.2" - "eslint-visitor-keys" "^3.3.0" + "eslint-visitor-keys" "^3.4.1" -"esquery@^1.4.0": - "integrity" "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==" - "resolved" "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" - "version" "1.4.0" +"esquery@^1.4.2": + "integrity" "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==" + "resolved" "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" + "version" "1.5.0" dependencies: "estraverse" "^5.1.0" @@ -945,22 +1038,10 @@ "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" "version" "3.3.0" -"eventsource-parser@^0.0.5": - "integrity" "sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==" - "resolved" "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-0.0.5.tgz" - "version" "0.0.5" - -"expiry-map@^2.0.0": - "integrity" "sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==" - "resolved" "https://registry.npmjs.org/expiry-map/-/expiry-map-2.0.0.tgz" - "version" "2.0.0" - dependencies: - "map-age-cleaner" "^0.2.0" - -"extend@^3.0.0": - "integrity" "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - "resolved" "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" - "version" "3.0.2" +"eventsource-parser@^1.0.0": + "integrity" "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==" + "resolved" "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz" + "version" "1.0.0" "fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" @@ -1030,6 +1111,14 @@ "locate-path" "^6.0.0" "path-exists" "^4.0.0" +"find-up@^6.3.0": + "integrity" "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz" + "version" "6.3.0" + dependencies: + "locate-path" "^7.1.0" + "path-exists" "^5.0.0" + "flat-cache@^3.0.4": "integrity" "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==" "resolved" "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" @@ -1048,20 +1137,23 @@ "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" "version" "3.2.7" +"foreground-child@^3.1.0": + "integrity" "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==" + "resolved" "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "cross-spawn" "^7.0.0" + "signal-exit" "^4.0.1" + "fs.realpath@^1.0.0": "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" "version" "1.0.0" -"fstream@^1.0.12": - "integrity" "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==" - "resolved" "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" - "version" "1.0.12" - dependencies: - "graceful-fs" "^4.1.2" - "inherits" "~2.0.0" - "mkdirp" ">=0.5 0" - "rimraf" "2" +"fsevents@~2.3.2": + "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + "version" "2.3.2" "function-bind@^1.1.1": "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" @@ -1099,6 +1191,17 @@ "resolved" "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" "version" "0.4.1" +"glob@^10.2.7": + "integrity" "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==" + "resolved" "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz" + "version" "10.2.7" + dependencies: + "foreground-child" "^3.1.0" + "jackspeak" "^2.0.3" + "minimatch" "^9.0.1" + "minipass" "^5.0.0 || ^6.0.2" + "path-scurry" "^1.7.0" + "glob@^7.1.3": "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -1111,17 +1214,6 @@ "once" "^1.3.0" "path-is-absolute" "^1.0.0" -"glob@^8.0.3": - "integrity" "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==" - "resolved" "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz" - "version" "8.0.3" - dependencies: - "fs.realpath" "^1.0.0" - "inflight" "^1.0.4" - "inherits" "2" - "minimatch" "^5.0.1" - "once" "^1.3.0" - "glob@7.2.0": "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" @@ -1134,10 +1226,10 @@ "once" "^1.3.0" "path-is-absolute" "^1.0.0" -"globals@^13.15.0": - "integrity" "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==" - "resolved" "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz" - "version" "13.18.0" +"globals@^13.19.0": + "integrity" "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" + "version" "13.20.0" dependencies: "type-fest" "^0.20.2" @@ -1153,7 +1245,7 @@ "merge2" "^1.4.1" "slash" "^3.0.0" -"graceful-fs@^4.1.2", "graceful-fs@^4.2.2", "graceful-fs@^4.2.4", "graceful-fs@^4.2.9": +"graceful-fs@^4.1.2", "graceful-fs@^4.2.4", "graceful-fs@^4.2.9": "integrity" "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" "version" "4.2.10" @@ -1163,6 +1255,16 @@ "resolved" "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" "version" "1.0.4" +"graphemer@^1.4.0": + "integrity" "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "resolved" "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + "version" "1.4.0" + +"has-flag@^3.0.0": + "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "version" "3.0.0" + "has-flag@^4.0.0": "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" @@ -1180,6 +1282,13 @@ "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" "version" "1.2.0" +"hosted-git-info@^4.0.1": + "integrity" "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==" + "resolved" "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "lru-cache" "^6.0.0" + "http-proxy-agent@^4.0.1": "integrity" "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==" "resolved" "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" @@ -1202,6 +1311,11 @@ "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz" "version" "5.2.1" +"immediate@~3.0.5": + "integrity" "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + "resolved" "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" + "version" "3.0.6" + "import-fresh@^3.0.0", "import-fresh@^3.2.1": "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" @@ -1231,7 +1345,7 @@ "once" "^1.3.0" "wrappy" "1" -"inherits@~2.0.0", "inherits@~2.0.3", "inherits@2": +"inherits@~2.0.3", "inherits@2": "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" "version" "2.0.4" @@ -1241,6 +1355,11 @@ "resolved" "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" "version" "3.1.1" +"is-arrayish@^0.2.1": + "integrity" "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + "version" "0.2.1" + "is-binary-path@~2.1.0": "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -1248,12 +1367,7 @@ dependencies: "binary-extensions" "^2.0.0" -"is-buffer@^2.0.0": - "integrity" "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" - "resolved" "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" - "version" "2.0.5" - -"is-core-module@^2.9.0": +"is-core-module@^2.5.0", "is-core-module@^2.9.0": "integrity" "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==" "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" "version" "2.11.0" @@ -1292,11 +1406,6 @@ "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" "version" "2.1.0" -"is-plain-obj@^4.0.0": - "integrity" "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" - "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" - "version" "4.1.0" - "is-plain-object@^2.0.4": "integrity" "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==" "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" @@ -1324,6 +1433,15 @@ "resolved" "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" "version" "3.0.1" +"jackspeak@^2.0.3": + "integrity" "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==" + "resolved" "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz" + "version" "2.2.1" + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + "jest-worker@^27.4.5": "integrity" "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==" "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" @@ -1333,10 +1451,17 @@ "merge-stream" "^2.0.0" "supports-color" "^8.0.0" -"js-sdsl@^4.1.4": - "integrity" "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==" - "resolved" "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz" - "version" "4.2.0" +"js-tiktoken@^1.0.5": + "integrity" "sha512-lxHntEupgjWvSh37WxpAW4XN6UBXBtFJOpZZq5HN5oNjDfN7L/iJhHOKjyL/DFtuYXUwn5jfTciLtOWpgQmHjQ==" + "resolved" "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "base64-js" "^1.5.1" + +"js-tokens@^4.0.0": + "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + "version" "4.0.0" "js-yaml@^4.1.0", "js-yaml@4.1.0": "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" @@ -1345,7 +1470,12 @@ dependencies: "argparse" "^2.0.1" -"json-parse-even-better-errors@^2.3.1": +"json-buffer@3.0.1": + "integrity" "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + "version" "3.0.1" + +"json-parse-even-better-errors@^2.3.0", "json-parse-even-better-errors@^2.3.1": "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" "resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" "version" "2.3.1" @@ -1355,21 +1485,43 @@ "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" "version" "0.4.1" +"json-schema-traverse@^1.0.0": + "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + "version" "1.0.0" + +"json-schema-typed@^8.0.1": + "integrity" "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" + "resolved" "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.1.tgz" + "version" "8.0.1" + "json-stable-stringify-without-jsonify@^1.0.1": "integrity" "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" "version" "1.0.1" +"jszip@^3.10.1": + "integrity" "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==" + "resolved" "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" + "version" "3.10.1" + dependencies: + "lie" "~3.3.0" + "pako" "~1.0.2" + "readable-stream" "~2.3.6" + "setimmediate" "^1.0.5" + +"keyv@^4.5.2": + "integrity" "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==" + "resolved" "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz" + "version" "4.5.2" + dependencies: + "json-buffer" "3.0.1" + "kind-of@^6.0.2": "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" "version" "6.0.3" -"kleur@^4.0.3": - "integrity" "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" - "resolved" "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz" - "version" "4.1.5" - "levn@^0.4.1": "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" "resolved" "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" @@ -1378,10 +1530,17 @@ "prelude-ls" "^1.2.1" "type-check" "~0.4.0" -"listenercount@~1.0.1": - "integrity" "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" - "resolved" "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz" - "version" "1.0.1" +"lie@~3.3.0": + "integrity" "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==" + "resolved" "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" + "version" "3.3.0" + dependencies: + "immediate" "~3.0.5" + +"lines-and-columns@^1.1.6": + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" "loader-runner@^4.2.0": "integrity" "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" @@ -1402,6 +1561,13 @@ dependencies: "p-locate" "^5.0.0" +"locate-path@^7.1.0": + "integrity" "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "p-locate" "^6.0.0" + "lodash.merge@^4.6.2": "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" @@ -1415,11 +1581,6 @@ "chalk" "^4.1.0" "is-unicode-supported" "^0.1.0" -"longest-streak@^3.0.0": - "integrity" "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" - "resolved" "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz" - "version" "3.1.0" - "lru-cache@^6.0.0": "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -1427,48 +1588,10 @@ dependencies: "yallist" "^4.0.0" -"map-age-cleaner@^0.2.0": - "integrity" "sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==" - "resolved" "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.2.0.tgz" - "version" "0.2.0" - dependencies: - "p-defer" "^1.0.0" - -"mdast-util-from-markdown@^1.0.0": - "integrity" "sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==" - "resolved" "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz" - "version" "1.2.0" - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - "decode-named-character-reference" "^1.0.0" - "mdast-util-to-string" "^3.1.0" - "micromark" "^3.0.0" - "micromark-util-decode-numeric-character-reference" "^1.0.0" - "micromark-util-decode-string" "^1.0.0" - "micromark-util-normalize-identifier" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - "unist-util-stringify-position" "^3.0.0" - "uvu" "^0.5.0" - -"mdast-util-to-markdown@^1.0.0": - "integrity" "sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==" - "resolved" "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz" - "version" "1.3.0" - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - "longest-streak" "^3.0.0" - "mdast-util-to-string" "^3.0.0" - "micromark-util-decode-string" "^1.0.0" - "unist-util-visit" "^4.0.0" - "zwitch" "^2.0.0" - -"mdast-util-to-string@^3.0.0", "mdast-util-to-string@^3.1.0": - "integrity" "sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==" - "resolved" "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz" - "version" "3.1.0" +"lru-cache@^9.1.1": + "integrity" "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz" + "version" "9.1.2" "merge-stream@^2.0.0": "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" @@ -1480,201 +1603,6 @@ "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" "version" "1.4.1" -"micromark-core-commonmark@^1.0.1": - "integrity" "sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==" - "resolved" "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz" - "version" "1.0.6" - dependencies: - "decode-named-character-reference" "^1.0.0" - "micromark-factory-destination" "^1.0.0" - "micromark-factory-label" "^1.0.0" - "micromark-factory-space" "^1.0.0" - "micromark-factory-title" "^1.0.0" - "micromark-factory-whitespace" "^1.0.0" - "micromark-util-character" "^1.0.0" - "micromark-util-chunked" "^1.0.0" - "micromark-util-classify-character" "^1.0.0" - "micromark-util-html-tag-name" "^1.0.0" - "micromark-util-normalize-identifier" "^1.0.0" - "micromark-util-resolve-all" "^1.0.0" - "micromark-util-subtokenize" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.1" - "uvu" "^0.5.0" - -"micromark-factory-destination@^1.0.0": - "integrity" "sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==" - "resolved" "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-character" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-factory-label@^1.0.0": - "integrity" "sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==" - "resolved" "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "micromark-util-character" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - "uvu" "^0.5.0" - -"micromark-factory-space@^1.0.0": - "integrity" "sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==" - "resolved" "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-character" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-factory-title@^1.0.0": - "integrity" "sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==" - "resolved" "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "micromark-factory-space" "^1.0.0" - "micromark-util-character" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - "uvu" "^0.5.0" - -"micromark-factory-whitespace@^1.0.0": - "integrity" "sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==" - "resolved" "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-factory-space" "^1.0.0" - "micromark-util-character" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-util-character@^1.0.0": - "integrity" "sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==" - "resolved" "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-util-chunked@^1.0.0": - "integrity" "sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==" - "resolved" "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-symbol" "^1.0.0" - -"micromark-util-classify-character@^1.0.0": - "integrity" "sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==" - "resolved" "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-character" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-util-combine-extensions@^1.0.0": - "integrity" "sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==" - "resolved" "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-chunked" "^1.0.0" - "micromark-util-types" "^1.0.0" - -"micromark-util-decode-numeric-character-reference@^1.0.0": - "integrity" "sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==" - "resolved" "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-symbol" "^1.0.0" - -"micromark-util-decode-string@^1.0.0": - "integrity" "sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==" - "resolved" "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "decode-named-character-reference" "^1.0.0" - "micromark-util-character" "^1.0.0" - "micromark-util-decode-numeric-character-reference" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - -"micromark-util-encode@^1.0.0": - "integrity" "sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==" - "resolved" "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz" - "version" "1.0.1" - -"micromark-util-html-tag-name@^1.0.0": - "integrity" "sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==" - "resolved" "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz" - "version" "1.1.0" - -"micromark-util-normalize-identifier@^1.0.0": - "integrity" "sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==" - "resolved" "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-symbol" "^1.0.0" - -"micromark-util-resolve-all@^1.0.0": - "integrity" "sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==" - "resolved" "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "micromark-util-types" "^1.0.0" - -"micromark-util-sanitize-uri@^1.0.0": - "integrity" "sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==" - "resolved" "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "micromark-util-character" "^1.0.0" - "micromark-util-encode" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - -"micromark-util-subtokenize@^1.0.0": - "integrity" "sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==" - "resolved" "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "micromark-util-chunked" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.0" - "uvu" "^0.5.0" - -"micromark-util-symbol@^1.0.0": - "integrity" "sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==" - "resolved" "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz" - "version" "1.0.1" - -"micromark-util-types@^1.0.0", "micromark-util-types@^1.0.1": - "integrity" "sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==" - "resolved" "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.2.tgz" - "version" "1.0.2" - -"micromark@^3.0.0": - "integrity" "sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==" - "resolved" "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz" - "version" "3.1.0" - dependencies: - "@types/debug" "^4.0.0" - "debug" "^4.0.0" - "decode-named-character-reference" "^1.0.0" - "micromark-core-commonmark" "^1.0.1" - "micromark-factory-space" "^1.0.0" - "micromark-util-character" "^1.0.0" - "micromark-util-chunked" "^1.0.0" - "micromark-util-combine-extensions" "^1.0.0" - "micromark-util-decode-numeric-character-reference" "^1.0.0" - "micromark-util-encode" "^1.0.0" - "micromark-util-normalize-identifier" "^1.0.0" - "micromark-util-resolve-all" "^1.0.0" - "micromark-util-sanitize-uri" "^1.0.0" - "micromark-util-subtokenize" "^1.0.0" - "micromark-util-symbol" "^1.0.0" - "micromark-util-types" "^1.0.1" - "uvu" "^0.5.0" - "micromatch@^4.0.0", "micromatch@^4.0.4": "integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==" "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" @@ -1695,6 +1623,11 @@ dependencies: "mime-db" "1.52.0" +"mimic-fn@^4.0.0": + "integrity" "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + "resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + "version" "4.0.0" + "minimatch@^3.0.4": "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -1709,10 +1642,10 @@ dependencies: "brace-expansion" "^1.1.7" -"minimatch@^5.0.1": - "integrity" "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==" - "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz" - "version" "5.1.1" +"minimatch@^9.0.1": + "integrity" "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz" + "version" "9.0.1" dependencies: "brace-expansion" "^2.0.1" @@ -1723,22 +1656,15 @@ dependencies: "brace-expansion" "^2.0.1" -"minimist@^1.2.6": - "integrity" "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" - "version" "1.2.7" - -"mkdirp@>=0.5 0": - "integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" - "version" "0.5.6" - dependencies: - "minimist" "^1.2.6" +"minipass@^5.0.0 || ^6.0.2": + "integrity" "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==" + "resolved" "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz" + "version" "6.0.2" -"mocha@^10.1.0": - "integrity" "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==" - "resolved" "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz" - "version" "10.1.0" +"mocha@^10.2.0": + "integrity" "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==" + "resolved" "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + "version" "10.2.0" dependencies: "ansi-colors" "4.1.1" "browser-stdout" "1.3.1" @@ -1762,11 +1688,6 @@ "yargs-parser" "20.2.4" "yargs-unparser" "2.0.0" -"mri@^1.1.0": - "integrity" "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" - "resolved" "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz" - "version" "1.2.0" - "ms@2.1.2": "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -1802,6 +1723,16 @@ "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" "version" "2.0.6" +"normalize-package-data@^3.0.2": + "integrity" "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==" + "resolved" "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + "version" "3.0.3" + dependencies: + "hosted-git-info" "^4.0.1" + "is-core-module" "^2.5.0" + "semver" "^7.3.4" + "validate-npm-package-license" "^3.0.1" + "normalize-path@^3.0.0", "normalize-path@~3.0.0": "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -1826,11 +1757,6 @@ "type-check" "^0.4.0" "word-wrap" "^1.2.3" -"p-defer@^1.0.0": - "integrity" "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==" - "resolved" "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz" - "version" "1.0.0" - "p-limit@^2.2.0": "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -1845,6 +1771,13 @@ dependencies: "yocto-queue" "^0.1.0" +"p-limit@^4.0.0": + "integrity" "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "yocto-queue" "^1.0.0" + "p-locate@^4.1.0": "integrity" "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" @@ -1859,16 +1792,28 @@ dependencies: "p-limit" "^3.0.2" -"p-timeout@^6.0.0": - "integrity" "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==" - "resolved" "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz" +"p-locate@^6.0.0": + "integrity" "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" "version" "6.0.0" + dependencies: + "p-limit" "^4.0.0" + +"p-timeout@^6.1.1": + "integrity" "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==" + "resolved" "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz" + "version" "6.1.1" "p-try@^2.0.0": "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" "resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" "version" "2.2.0" +"pako@~1.0.2": + "integrity" "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "resolved" "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" + "version" "1.0.11" + "parent-module@^1.0.0": "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -1876,11 +1821,26 @@ dependencies: "callsites" "^3.0.0" +"parse-json@^5.2.0": + "integrity" "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==" + "resolved" "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + "version" "5.2.0" + dependencies: + "@babel/code-frame" "^7.0.0" + "error-ex" "^1.3.1" + "json-parse-even-better-errors" "^2.3.0" + "lines-and-columns" "^1.1.6" + "path-exists@^4.0.0": "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" "version" "4.0.0" +"path-exists@^5.0.0": + "integrity" "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" + "version" "5.0.0" + "path-is-absolute@^1.0.0": "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" @@ -1896,6 +1856,14 @@ "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" "version" "1.0.7" +"path-scurry@^1.7.0": + "integrity" "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==" + "resolved" "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz" + "version" "1.9.2" + dependencies: + "lru-cache" "^9.1.1" + "minipass" "^5.0.0 || ^6.0.2" + "path-type@^4.0.0": "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -1938,6 +1906,11 @@ "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" "version" "1.2.3" +"quick-lru@^6.1.1": + "integrity" "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==" + "resolved" "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz" + "version" "6.1.1" + "randombytes@^2.1.0": "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -1945,10 +1918,29 @@ dependencies: "safe-buffer" "^5.1.0" -"readable-stream@^2.0.2", "readable-stream@~2.3.6": - "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" - "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - "version" "2.3.7" +"read-pkg-up@^9.1.0": + "integrity" "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==" + "resolved" "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz" + "version" "9.1.0" + dependencies: + "find-up" "^6.3.0" + "read-pkg" "^7.1.0" + "type-fest" "^2.5.0" + +"read-pkg@^7.1.0": + "integrity" "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==" + "resolved" "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz" + "version" "7.1.0" + dependencies: + "@types/normalize-package-data" "^2.4.1" + "normalize-package-data" "^3.0.2" + "parse-json" "^5.2.0" + "type-fest" "^2.0.0" + +"readable-stream@~2.3.6": + "integrity" "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + "version" "2.3.8" dependencies: "core-util-is" "~1.0.0" "inherits" "~2.0.3" @@ -1972,44 +1964,16 @@ dependencies: "resolve" "^1.20.0" -"regexpp@^3.2.0": - "integrity" "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" - "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" - "version" "3.2.0" - -"remark-parse@^10.0.0": - "integrity" "sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==" - "resolved" "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz" - "version" "10.0.1" - dependencies: - "@types/mdast" "^3.0.0" - "mdast-util-from-markdown" "^1.0.0" - "unified" "^10.0.0" - -"remark-stringify@^10.0.0": - "integrity" "sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==" - "resolved" "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.2.tgz" - "version" "10.0.2" - dependencies: - "@types/mdast" "^3.0.0" - "mdast-util-to-markdown" "^1.0.0" - "unified" "^10.0.0" - -"remark@^14.0.2": - "integrity" "sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==" - "resolved" "https://registry.npmjs.org/remark/-/remark-14.0.2.tgz" - "version" "14.0.2" - dependencies: - "@types/mdast" "^3.0.0" - "remark-parse" "^10.0.0" - "remark-stringify" "^10.0.0" - "unified" "^10.0.0" - "require-directory@^2.1.1": "integrity" "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" "resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" "version" "2.1.1" +"require-from-string@^2.0.2": + "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + "version" "2.0.2" + "resolve-cwd@^3.0.0": "integrity" "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==" "resolved" "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -2048,13 +2012,6 @@ dependencies: "glob" "^7.1.3" -"rimraf@2": - "integrity" "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==" - "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - "version" "2.7.1" - dependencies: - "glob" "^7.1.3" - "run-parallel@^1.1.9": "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -2062,13 +2019,6 @@ dependencies: "queue-microtask" "^1.2.2" -"sade@^1.7.3": - "integrity" "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==" - "resolved" "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz" - "version" "1.8.1" - dependencies: - "mri" "^1.1.0" - "safe-buffer@^5.1.0": "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -2084,30 +2034,37 @@ "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" "version" "5.1.2" -"schema-utils@^3.1.0", "schema-utils@^3.1.1": - "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" - "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" - "version" "3.1.1" +"schema-utils@^3.1.1", "schema-utils@^3.1.2": + "integrity" "sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.2.0.tgz" + "version" "3.2.0" dependencies: "@types/json-schema" "^7.0.8" "ajv" "^6.12.5" "ajv-keywords" "^3.5.2" -"semver@^7.3.4", "semver@^7.3.7": +"semver@^7.3.4", "semver@^7.3.7", "semver@^7.3.8": "integrity" "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==" "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" "version" "7.3.8" dependencies: "lru-cache" "^6.0.0" -"serialize-javascript@^6.0.0", "serialize-javascript@6.0.0": +"serialize-javascript@^6.0.1": + "integrity" "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "randombytes" "^2.1.0" + +"serialize-javascript@6.0.0": "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" "version" "6.0.0" dependencies: "randombytes" "^2.1.0" -"setimmediate@~1.0.4": +"setimmediate@^1.0.5": "integrity" "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" "resolved" "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" "version" "1.0.5" @@ -2131,6 +2088,11 @@ "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" "version" "3.0.0" +"signal-exit@^4.0.1": + "integrity" "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz" + "version" "4.0.2" + "slash@^3.0.0": "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" @@ -2149,10 +2111,31 @@ "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" "version" "0.6.1" -"streamsearch@^1.1.0": - "integrity" "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" - "resolved" "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" - "version" "1.1.0" +"spdx-correct@^3.0.0": + "integrity" "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==" + "resolved" "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + "version" "3.2.0" + dependencies: + "spdx-expression-parse" "^3.0.0" + "spdx-license-ids" "^3.0.0" + +"spdx-exceptions@^2.1.0": + "integrity" "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "resolved" "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + "version" "2.3.0" + +"spdx-expression-parse@^3.0.0": + "integrity" "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==" + "resolved" "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "spdx-exceptions" "^2.1.0" + "spdx-license-ids" "^3.0.0" + +"spdx-license-ids@^3.0.0": + "integrity" "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + "resolved" "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz" + "version" "3.0.13" "string_decoder@~1.1.1": "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==" @@ -2161,6 +2144,15 @@ dependencies: "safe-buffer" "~5.1.0" +"string-width-cjs@npm:string-width@^4.2.0": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" + dependencies: + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" + "string-width@^4.1.0", "string-width@^4.2.0": "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -2170,6 +2162,22 @@ "is-fullwidth-code-point" "^3.0.0" "strip-ansi" "^6.0.1" +"string-width@^5.0.1", "string-width@^5.1.2": + "integrity" "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "eastasianwidth" "^0.2.0" + "emoji-regex" "^9.2.2" + "strip-ansi" "^7.0.1" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "ansi-regex" "^5.0.1" + "strip-ansi@^6.0.0", "strip-ansi@^6.0.1": "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -2177,19 +2185,29 @@ dependencies: "ansi-regex" "^5.0.1" +"strip-ansi@^7.0.1": + "integrity" "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + "version" "7.1.0" + dependencies: + "ansi-regex" "^6.0.1" + "strip-json-comments@^3.1.0", "strip-json-comments@^3.1.1", "strip-json-comments@3.1.1": "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" "version" "3.1.1" -"strip-markdown@^5.0.0": - "integrity" "sha512-PXSts6Ta9A/TwGxVVSRlQs1ukJTAwwtbip2OheJEjPyfykaQ4sJSTnQWjLTI2vYWNts/R/91/csagp15W8n9gA==" - "resolved" "https://registry.npmjs.org/strip-markdown/-/strip-markdown-5.0.0.tgz" - "version" "5.0.0" +"stubborn-fs@^1.2.4": + "integrity" "sha512-KRa4nIRJ8q6uApQbPwYZVhOof8979fw4xbajBWa5kPJFa4nyY3aFaMWVyIVCDnkNCCG/3HLipUZ4QaNlYsmX1w==" + "resolved" "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.4.tgz" + "version" "1.2.4" + +"supports-color@^5.3.0": + "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "version" "5.5.0" dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.6" - "unified" "^10.0.0" + "has-flag" "^3.0.0" "supports-color@^7.1.0": "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" @@ -2222,24 +2240,24 @@ "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" "version" "2.2.1" -"terser-webpack-plugin@^5.1.3": - "integrity" "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==" - "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz" - "version" "5.3.6" +"terser-webpack-plugin@^5.3.7": + "integrity" "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==" + "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz" + "version" "5.3.9" dependencies: - "@jridgewell/trace-mapping" "^0.3.14" + "@jridgewell/trace-mapping" "^0.3.17" "jest-worker" "^27.4.5" "schema-utils" "^3.1.1" - "serialize-javascript" "^6.0.0" - "terser" "^5.14.1" + "serialize-javascript" "^6.0.1" + "terser" "^5.16.8" -"terser@^5.14.1": - "integrity" "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==" - "resolved" "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz" - "version" "5.16.1" +"terser@^5.16.8": + "integrity" "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==" + "resolved" "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz" + "version" "5.18.0" dependencies: - "@jridgewell/source-map" "^0.3.2" - "acorn" "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + "acorn" "^8.8.2" "commander" "^2.20.0" "source-map-support" "~0.5.20" @@ -2255,20 +2273,10 @@ dependencies: "is-number" "^7.0.0" -"traverse@>=0.3.0 <0.4": - "integrity" "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==" - "resolved" "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" - "version" "0.3.9" - -"trough@^2.0.0": - "integrity" "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" - "resolved" "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz" - "version" "2.1.0" - -"ts-loader@^9.4.1": - "integrity" "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==" - "resolved" "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz" - "version" "9.4.2" +"ts-loader@^9.4.3": + "integrity" "sha512-n3hBnm6ozJYzwiwt5YRiJZkzktftRpMiBApHaJPoWLA+qetQBAXkHqCLM6nwSdRDimqVtA5ocIkcTRLMTt7yzA==" + "resolved" "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz" + "version" "9.4.3" dependencies: "chalk" "^4.1.0" "enhanced-resolve" "^5.0.0" @@ -2299,75 +2307,15 @@ "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" "version" "0.20.2" -"typescript@*", "typescript@^4.9.3", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": - "integrity" "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" - "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz" - "version" "4.9.3" - -"undici@^5.13.0": - "integrity" "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==" - "resolved" "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz" - "version" "5.14.0" - dependencies: - "busboy" "^1.6.0" - -"unified@^10.0.0": - "integrity" "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==" - "resolved" "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz" - "version" "10.1.2" - dependencies: - "@types/unist" "^2.0.0" - "bail" "^2.0.0" - "extend" "^3.0.0" - "is-buffer" "^2.0.0" - "is-plain-obj" "^4.0.0" - "trough" "^2.0.0" - "vfile" "^5.0.0" - -"unist-util-is@^5.0.0": - "integrity" "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==" - "resolved" "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz" - "version" "5.1.1" - -"unist-util-stringify-position@^3.0.0": - "integrity" "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==" - "resolved" "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz" - "version" "3.0.2" - dependencies: - "@types/unist" "^2.0.0" +"type-fest@^2.0.0", "type-fest@^2.11.2", "type-fest@^2.5.0": + "integrity" "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" + "version" "2.19.0" -"unist-util-visit-parents@^5.1.1": - "integrity" "sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw==" - "resolved" "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz" - "version" "5.1.1" - dependencies: - "@types/unist" "^2.0.0" - "unist-util-is" "^5.0.0" - -"unist-util-visit@^4.0.0": - "integrity" "sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg==" - "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.1.tgz" - "version" "4.1.1" - dependencies: - "@types/unist" "^2.0.0" - "unist-util-is" "^5.0.0" - "unist-util-visit-parents" "^5.1.1" - -"unzipper@^0.10.11": - "integrity" "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==" - "resolved" "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz" - "version" "0.10.11" - dependencies: - "big-integer" "^1.6.17" - "binary" "~0.3.0" - "bluebird" "~3.4.1" - "buffer-indexof-polyfill" "~1.0.0" - "duplexer2" "~0.1.4" - "fstream" "^1.0.12" - "graceful-fs" "^4.2.2" - "listenercount" "~1.0.1" - "readable-stream" "~2.3.6" - "setimmediate" "~1.0.4" +"typescript@*", "typescript@^5.1.3", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": + "integrity" "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz" + "version" "5.1.3" "update-browserslist-db@^1.0.9": "integrity" "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==" @@ -2394,33 +2342,13 @@ "resolved" "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz" "version" "9.0.0" -"uvu@^0.5.0": - "integrity" "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==" - "resolved" "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz" - "version" "0.5.6" - dependencies: - "dequal" "^2.0.0" - "diff" "^5.0.0" - "kleur" "^4.0.3" - "sade" "^1.7.3" - -"vfile-message@^3.0.0": - "integrity" "sha512-0yaU+rj2gKAyEk12ffdSbBfjnnj+b1zqTBv3OQCTn8yEB02bsPizwdBPrLJjHnK+cU9EMMcUnNv938XcZIkmdA==" - "resolved" "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz" - "version" "3.1.3" - dependencies: - "@types/unist" "^2.0.0" - "unist-util-stringify-position" "^3.0.0" - -"vfile@^5.0.0": - "integrity" "sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==" - "resolved" "https://registry.npmjs.org/vfile/-/vfile-5.3.6.tgz" - "version" "5.3.6" +"validate-npm-package-license@^3.0.1": + "integrity" "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==" + "resolved" "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + "version" "3.0.4" dependencies: - "@types/unist" "^2.0.0" - "is-buffer" "^2.0.0" - "unist-util-stringify-position" "^3.0.0" - "vfile-message" "^3.0.0" + "spdx-correct" "^3.0.0" + "spdx-expression-parse" "^3.0.0" "watchpack@^2.4.0": "integrity" "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==" @@ -2430,17 +2358,17 @@ "glob-to-regexp" "^0.4.1" "graceful-fs" "^4.1.2" -"webpack-cli@^5.0.0", "webpack-cli@5.x.x": - "integrity" "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==" - "resolved" "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz" - "version" "5.0.1" +"webpack-cli@^5.1.4", "webpack-cli@5.x.x": + "integrity" "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==" + "resolved" "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" + "version" "5.1.4" dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.0.1" - "@webpack-cli/info" "^2.0.1" - "@webpack-cli/serve" "^2.0.1" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" "colorette" "^2.0.14" - "commander" "^9.4.1" + "commander" "^10.0.1" "cross-spawn" "^7.0.3" "envinfo" "^7.7.3" "fastest-levenshtein" "^1.0.12" @@ -2462,22 +2390,22 @@ "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" "version" "3.2.3" -"webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.75.0", "webpack@5.x.x": - "integrity" "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==" - "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz" - "version" "5.75.0" +"webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.86.0", "webpack@5.x.x": + "integrity" "sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg==" + "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz" + "version" "5.86.0" dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" "acorn" "^8.7.1" - "acorn-import-assertions" "^1.7.6" + "acorn-import-assertions" "^1.9.0" "browserslist" "^4.14.5" "chrome-trace-event" "^1.0.2" - "enhanced-resolve" "^5.10.0" - "es-module-lexer" "^0.9.0" + "enhanced-resolve" "^5.14.1" + "es-module-lexer" "^1.2.1" "eslint-scope" "5.1.1" "events" "^3.2.0" "glob-to-regexp" "^0.4.1" @@ -2486,12 +2414,17 @@ "loader-runner" "^4.2.0" "mime-types" "^2.1.27" "neo-async" "^2.6.2" - "schema-utils" "^3.1.0" + "schema-utils" "^3.1.2" "tapable" "^2.1.1" - "terser-webpack-plugin" "^5.1.3" + "terser-webpack-plugin" "^5.3.7" "watchpack" "^2.4.0" "webpack-sources" "^3.2.3" +"when-exit@^2.0.0": + "integrity" "sha512-H85ulNwUBU1e6PGxkWUDgxnbohSXD++ah6Xw1VHAN7CtypcbZaC4aYjQ+C2PMVaDkURDuOinNAT+Lnz3utWXxQ==" + "resolved" "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz" + "version" "2.1.0" + "which@^2.0.1": "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" @@ -2514,6 +2447,15 @@ "resolved" "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" "version" "6.2.1" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" + "wrap-ansi@^7.0.0": "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -2523,6 +2465,15 @@ "string-width" "^4.1.0" "strip-ansi" "^6.0.0" +"wrap-ansi@^8.1.0": + "integrity" "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + "version" "8.1.0" + dependencies: + "ansi-styles" "^6.1.0" + "string-width" "^5.0.1" + "strip-ansi" "^7.0.1" + "wrappy@1": "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" @@ -2571,7 +2522,7 @@ "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" "version" "0.1.0" -"zwitch@^2.0.0": - "integrity" "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" - "resolved" "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" - "version" "2.0.4" +"yocto-queue@^1.0.0": + "integrity" "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" + "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" + "version" "1.0.0" From 3141942b194aaf5b520762602e486f30067f730f Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Mon, 12 Jun 2023 23:14:23 +0200 Subject: [PATCH 02/15] Add ChatGPT retrieval plugin and xtra tooling --- .env.example | 6 + README.md | 76 + package-lock.json | 819 ++- package.json | 19 +- resources/VectorDB-architecture.webp | Bin 0 -> 28698 bytes retrieval/.dockerignore | 11 + retrieval/.github/pull_request_template.md | 40 + retrieval/.gitignore | 138 + retrieval/.well-known/ai-plugin.json | 19 + retrieval/.well-known/logo.png | Bin 0 -> 17015 bytes retrieval/.well-known/openapi.yaml | 196 + retrieval/Dockerfile | 24 + retrieval/LICENSE | 21 + retrieval/Makefile | 14 + retrieval/README.md | 595 +++ retrieval/datastore/__init__.py | 0 retrieval/datastore/datastore.py | 86 + retrieval/datastore/factory.py | 63 + retrieval/datastore/providers/__init__.py | 0 .../providers/analyticdb_datastore.py | 312 ++ .../providers/azuresearch_datastore.py | 260 + .../datastore/providers/chroma_datastore.py | 250 + .../datastore/providers/llama_datastore.py | 181 + .../datastore/providers/milvus_datastore.py | 560 ++ .../datastore/providers/pgvector_datastore.py | 181 + .../datastore/providers/pinecone_datastore.py | 262 + .../datastore/providers/postgres_datastore.py | 132 + .../datastore/providers/qdrant_datastore.py | 297 + .../datastore/providers/redis_datastore.py | 391 ++ .../datastore/providers/supabase_datastore.py | 95 + .../datastore/providers/weaviate_datastore.py | 385 ++ .../datastore/providers/zilliz_datastore.py | 65 + retrieval/docs/deployment/flyio.md | 89 + retrieval/docs/deployment/heroku.md | 105 + retrieval/docs/deployment/other-options.md | 17 + .../removing-unused-dependencies.md | 20 + .../docs/deployment/render-thumbnail.png | Bin 0 -> 260172 bytes retrieval/docs/deployment/render.md | 19 + retrieval/docs/providers/analyticdb/setup.md | 82 + retrieval/docs/providers/azuresearch/setup.md | 29 + retrieval/docs/providers/chroma/setup.md | 29 + retrieval/docs/providers/llama/setup.md | 51 + retrieval/docs/providers/milvus/setup.md | 43 + retrieval/docs/providers/pinecone/setup.md | 35 + retrieval/docs/providers/postgres/setup.md | 81 + retrieval/docs/providers/qdrant/setup.md | 58 + retrieval/docs/providers/redis/setup.md | 37 + retrieval/docs/providers/supabase/setup.md | 87 + retrieval/docs/providers/weaviate/setup.md | 79 + retrieval/docs/providers/zilliz/setup.md | 46 + .../no-auth/ai-plugin.json | 18 + .../authentication-methods/no-auth/main.py | 144 + .../oauth/ai-plugin.json | 25 + .../service-http/ai-plugin.json | 22 + .../user-http/ai-plugin.json | 19 + .../docker/milvus/docker-compose.yaml | 49 + retrieval/examples/docker/qdrant/README.md | 46 + .../docker/qdrant/docker-compose.yaml | 17 + .../examples/docker/qdrant/documents.json | 23 + retrieval/examples/docker/qdrant/queries.json | 7 + .../examples/docker/redis/docker-compose.yml | 18 + retrieval/examples/memory/README.md | 15 + retrieval/examples/memory/ai-plugin.json | 19 + retrieval/examples/memory/main.py | 183 + retrieval/examples/memory/openapi.yaml | 276 + .../providers/pinecone/semantic-search.ipynb | 809 +++ .../redis/semantic-search-and-filter.ipynb | 511 ++ .../examples/providers/supabase/.gitignore | 3 + .../examples/providers/supabase/config.toml | 72 + .../20230414142107_init_pg_vector.sql | 70 + .../examples/providers/supabase/seed.sql | 0 retrieval/local_server/ai-plugin.json | 18 + retrieval/local_server/logo.png | Bin 0 -> 17015 bytes retrieval/local_server/main.py | 148 + retrieval/local_server/openapi.yaml | 190 + retrieval/models/api.py | 34 + retrieval/models/models.py | 66 + retrieval/poetry.lock | 4753 +++++++++++++++++ retrieval/pyproject.toml | 66 + retrieval/scripts/process_json/README.md | 24 + retrieval/scripts/process_json/example.json | 25 + .../scripts/process_json/process_json.py | 147 + retrieval/scripts/process_jsonl/README.md | 24 + retrieval/scripts/process_jsonl/example.jsonl | 6 + .../scripts/process_jsonl/process_jsonl.py | 145 + retrieval/scripts/process_zip/README.md | 24 + retrieval/scripts/process_zip/example.zip | Bin 0 -> 53296 bytes retrieval/scripts/process_zip/process_zip.py | 152 + retrieval/server/main.py | 156 + retrieval/services/chunks.py | 202 + retrieval/services/date.py | 24 + retrieval/services/extract_metadata.py | 43 + retrieval/services/file.py | 117 + retrieval/services/openai.py | 77 + retrieval/services/pii_detection.py | 32 + retrieval/tests/__init__.py | 0 .../analyticdb/test_analyticdb_datastore.py | 323 ++ .../azuresearch/test_azuresearch_datastore.py | 139 + .../providers/chroma/test_chroma_datastore.py | 293 + .../providers/llama/test_llama_datastore.py | 95 + .../providers/milvus/test_milvus_datastore.py | 354 ++ .../postgres/test_postgres_datastore.py | 291 + .../providers/qdrant/test_qdrant_datastore.py | 280 + .../providers/redis/test_redis_datastore.py | 64 + .../supabase/test_supabase_datastore.py | 291 + .../providers/weaviate/docker-compose.yml | 25 + .../weaviate/test_weaviate_datastore.py | 538 ++ .../providers/zilliz/test_zilliz_datastore.py | 29 + retrieval/xtras/chat_utils.py | 80 + retrieval/xtras/database_utils.py | 97 + retrieval/xtras/main.py | 17 + retrieval/xtras/secrets.py | 2 + src/file-agent.js | 141 + src/langchain.ts | 17 + src/typescript_textsplitter.ts | 36 + yarn.lock | 248 +- 116 files changed, 18557 insertions(+), 27 deletions(-) create mode 100644 .env.example create mode 100644 resources/VectorDB-architecture.webp create mode 100644 retrieval/.dockerignore create mode 100644 retrieval/.github/pull_request_template.md create mode 100644 retrieval/.gitignore create mode 100644 retrieval/.well-known/ai-plugin.json create mode 100644 retrieval/.well-known/logo.png create mode 100644 retrieval/.well-known/openapi.yaml create mode 100644 retrieval/Dockerfile create mode 100644 retrieval/LICENSE create mode 100644 retrieval/Makefile create mode 100644 retrieval/README.md create mode 100644 retrieval/datastore/__init__.py create mode 100644 retrieval/datastore/datastore.py create mode 100644 retrieval/datastore/factory.py create mode 100644 retrieval/datastore/providers/__init__.py create mode 100644 retrieval/datastore/providers/analyticdb_datastore.py create mode 100644 retrieval/datastore/providers/azuresearch_datastore.py create mode 100644 retrieval/datastore/providers/chroma_datastore.py create mode 100644 retrieval/datastore/providers/llama_datastore.py create mode 100644 retrieval/datastore/providers/milvus_datastore.py create mode 100644 retrieval/datastore/providers/pgvector_datastore.py create mode 100644 retrieval/datastore/providers/pinecone_datastore.py create mode 100644 retrieval/datastore/providers/postgres_datastore.py create mode 100644 retrieval/datastore/providers/qdrant_datastore.py create mode 100644 retrieval/datastore/providers/redis_datastore.py create mode 100644 retrieval/datastore/providers/supabase_datastore.py create mode 100644 retrieval/datastore/providers/weaviate_datastore.py create mode 100644 retrieval/datastore/providers/zilliz_datastore.py create mode 100644 retrieval/docs/deployment/flyio.md create mode 100644 retrieval/docs/deployment/heroku.md create mode 100644 retrieval/docs/deployment/other-options.md create mode 100644 retrieval/docs/deployment/removing-unused-dependencies.md create mode 100644 retrieval/docs/deployment/render-thumbnail.png create mode 100644 retrieval/docs/deployment/render.md create mode 100644 retrieval/docs/providers/analyticdb/setup.md create mode 100644 retrieval/docs/providers/azuresearch/setup.md create mode 100644 retrieval/docs/providers/chroma/setup.md create mode 100644 retrieval/docs/providers/llama/setup.md create mode 100644 retrieval/docs/providers/milvus/setup.md create mode 100644 retrieval/docs/providers/pinecone/setup.md create mode 100644 retrieval/docs/providers/postgres/setup.md create mode 100644 retrieval/docs/providers/qdrant/setup.md create mode 100644 retrieval/docs/providers/redis/setup.md create mode 100644 retrieval/docs/providers/supabase/setup.md create mode 100644 retrieval/docs/providers/weaviate/setup.md create mode 100644 retrieval/docs/providers/zilliz/setup.md create mode 100644 retrieval/examples/authentication-methods/no-auth/ai-plugin.json create mode 100644 retrieval/examples/authentication-methods/no-auth/main.py create mode 100644 retrieval/examples/authentication-methods/oauth/ai-plugin.json create mode 100644 retrieval/examples/authentication-methods/service-http/ai-plugin.json create mode 100644 retrieval/examples/authentication-methods/user-http/ai-plugin.json create mode 100644 retrieval/examples/docker/milvus/docker-compose.yaml create mode 100644 retrieval/examples/docker/qdrant/README.md create mode 100644 retrieval/examples/docker/qdrant/docker-compose.yaml create mode 100644 retrieval/examples/docker/qdrant/documents.json create mode 100644 retrieval/examples/docker/qdrant/queries.json create mode 100644 retrieval/examples/docker/redis/docker-compose.yml create mode 100644 retrieval/examples/memory/README.md create mode 100644 retrieval/examples/memory/ai-plugin.json create mode 100644 retrieval/examples/memory/main.py create mode 100644 retrieval/examples/memory/openapi.yaml create mode 100644 retrieval/examples/providers/pinecone/semantic-search.ipynb create mode 100644 retrieval/examples/providers/redis/semantic-search-and-filter.ipynb create mode 100644 retrieval/examples/providers/supabase/.gitignore create mode 100644 retrieval/examples/providers/supabase/config.toml create mode 100644 retrieval/examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql create mode 100644 retrieval/examples/providers/supabase/seed.sql create mode 100644 retrieval/local_server/ai-plugin.json create mode 100644 retrieval/local_server/logo.png create mode 100644 retrieval/local_server/main.py create mode 100644 retrieval/local_server/openapi.yaml create mode 100644 retrieval/models/api.py create mode 100644 retrieval/models/models.py create mode 100644 retrieval/poetry.lock create mode 100644 retrieval/pyproject.toml create mode 100644 retrieval/scripts/process_json/README.md create mode 100644 retrieval/scripts/process_json/example.json create mode 100644 retrieval/scripts/process_json/process_json.py create mode 100644 retrieval/scripts/process_jsonl/README.md create mode 100644 retrieval/scripts/process_jsonl/example.jsonl create mode 100644 retrieval/scripts/process_jsonl/process_jsonl.py create mode 100644 retrieval/scripts/process_zip/README.md create mode 100644 retrieval/scripts/process_zip/example.zip create mode 100644 retrieval/scripts/process_zip/process_zip.py create mode 100644 retrieval/server/main.py create mode 100644 retrieval/services/chunks.py create mode 100644 retrieval/services/date.py create mode 100644 retrieval/services/extract_metadata.py create mode 100644 retrieval/services/file.py create mode 100644 retrieval/services/openai.py create mode 100644 retrieval/services/pii_detection.py create mode 100644 retrieval/tests/__init__.py create mode 100644 retrieval/tests/datastore/providers/analyticdb/test_analyticdb_datastore.py create mode 100644 retrieval/tests/datastore/providers/azuresearch/test_azuresearch_datastore.py create mode 100644 retrieval/tests/datastore/providers/chroma/test_chroma_datastore.py create mode 100644 retrieval/tests/datastore/providers/llama/test_llama_datastore.py create mode 100644 retrieval/tests/datastore/providers/milvus/test_milvus_datastore.py create mode 100644 retrieval/tests/datastore/providers/postgres/test_postgres_datastore.py create mode 100644 retrieval/tests/datastore/providers/qdrant/test_qdrant_datastore.py create mode 100644 retrieval/tests/datastore/providers/redis/test_redis_datastore.py create mode 100644 retrieval/tests/datastore/providers/supabase/test_supabase_datastore.py create mode 100644 retrieval/tests/datastore/providers/weaviate/docker-compose.yml create mode 100644 retrieval/tests/datastore/providers/weaviate/test_weaviate_datastore.py create mode 100644 retrieval/tests/datastore/providers/zilliz/test_zilliz_datastore.py create mode 100644 retrieval/xtras/chat_utils.py create mode 100644 retrieval/xtras/database_utils.py create mode 100644 retrieval/xtras/main.py create mode 100644 retrieval/xtras/secrets.py create mode 100644 src/file-agent.js create mode 100644 src/langchain.ts create mode 100644 src/typescript_textsplitter.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d06d750 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +export DATASTORE=pinecone +export BEARER_TOKEN= +export OPENAI_API_KEY= +export PINECONE_API_KEY= +export PINECONE_ENVIRONMENT= +export PINECONE_INDEX= diff --git a/README.md b/README.md index fd31be9..6d7a879 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,82 @@ To use this extension, you will need to authenticate with a valid API key from C Once you have obtained a API key, you can configure the extension to use it as described in the previous section. +### Run ChatGPT plugin + +``` +cd retrieval +pip install poetry +poetry env use python3.10 +poetry shell +poetry install +``` + +Set your environment variables + +``` +export DATASTORE=pinecone +export BEARER_TOKEN= +export OPENAI_API_KEY= +export PINECONE_API_KEY= +export PINECONE_ENVIRONMENT= +export PINECONE_INDEX= +``` + +### Run Database Interface Server +When The config is ready. Under the project directory, run: + +`poetry run start` + +It will start your Database Interface server. Open your browser and open `http://0.0.0.0:8000/docs#/`. + +If the page is up, you have successfully implemented the Database Interface module with Retrieval Plugin. + +## Using Vector Database to store code base + +The goal is to use an architecture similar to [chatgpt with external memory using vector database](https://betterprogramming.pub/enhancing-chatgpt-with-infinite-external-memory-using-vector-database-and-chatgpt-retrieval-plugin-b6f4ea16ab8) + +[![Vector DB architecture](/resources/VectorDB-architecture.webp) + +This will additionally use LangChain to chunk code files using a [Code splitter](https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html) specific to the language of the code file being processed. + +We will need an agent running in OS in the background to monitor file changes which upserts and deletes vectors in the DB based on OS file operations. + +It should only take into account files that are "source files" of the project. To achieve this, we can use `.gitignore` or `.npmignore` files foralong with a custom configuration. + + +### Chokidar agent with ignore files + +We can use [chokidar](https://github.com/paulmillr/chokidar) as a battle-tested agent to monitor the file system for changes using file, dir, glob, or array of files to match. + +```ts +// Initialize watcher. +const watcher = chokidar.watch('file, dir, glob, or array', { + ignored: /(^|[\/\\])\../, // ignore dotfiles + persistent: true +}); + +// Something to use when events are received. +const log = console.log.bind(console); +// Add event listeners. +watcher + .on('add', path => log(`File ${path} has been added`)) + .on('change', path => log(`File ${path} has been changed`)) + .on('unlink', path => log(`File ${path} has been removed`)); +``` + +For any `add` or `change` we call the `upsert` API of the FastAPI python API for [GPT Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin.git). + +For `unlink` we call the `delete` endpoint. + +We will however use [ignoring-watcher](https://www.npmjs.com/package/ignoring-watcher) as a convenient wrapper of Chokidar :) + +The `file_agent.js` can be found in `src` and can be run simply via node: + +`node src/file_agent.js` + +or via script: + +`npm run file-agent` ## Using the Extension diff --git a/package-lock.json b/package-lock.json index 21ba89f..537c18a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "chatgpt", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "chatgpt", - "version": "0.4.0", + "version": "0.4.1", "license": "MIT", "dependencies": { - "chatgpt": "^5.2.5" + "chatgpt": "^5.2.5", + "langchain": "^0.0.92" }, "devDependencies": { "@types/glob": "^8.1.0", @@ -31,6 +32,15 @@ "vscode": "^1.73.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.4.4.tgz", + "integrity": "sha512-Z/39nQi1sSUCeLII3lsAbL1u+0JF6cR2XmUEX9sLH0VtxmIjY6cjOUYjCkYh4oapTxOkhAFnVSAFJ6cxml2qXg==", + "dependencies": { + "@fortaine/fetch-event-source": "^3.0.6", + "cross-fetch": "^3.1.5" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -192,6 +202,14 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortaine/fetch-event-source": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz", + "integrity": "sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw==", + "engines": { + "node": ">=16.15" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -498,6 +516,11 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "node_modules/@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", @@ -1079,6 +1102,11 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/atomically": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz", @@ -1088,6 +1116,14 @@ "when-exit": "^2.0.0" } }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1117,11 +1153,15 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1364,6 +1404,17 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1423,6 +1474,14 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dependencies": { + "node-fetch": "^2.6.11" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1486,6 +1545,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -1805,6 +1872,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -1822,6 +1894,11 @@ "node": ">=14.18" } }, + "node_modules/expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1929,7 +2006,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, "bin": { "flat": "cli.js" } @@ -1953,6 +2029,25 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -1969,6 +2064,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2203,7 +2311,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 4" } @@ -2283,6 +2391,11 @@ "node": ">=10.13.0" } }, + "node_modules/is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==" + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2503,6 +2616,14 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -2532,6 +2653,231 @@ "node": ">=0.10.0" } }, + "node_modules/langchain": { + "version": "0.0.92", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.0.92.tgz", + "integrity": "sha512-77528kXJu9C391qV1a5jDzZLEjIblq/VtpH7xd4BwavgvhRmeSXR9WkrHHmih/Phpae4Ss3dtb7pcyd78rp/YA==", + "dependencies": { + "@anthropic-ai/sdk": "^0.4.3", + "ansi-styles": "^5.0.0", + "binary-extensions": "^2.2.0", + "expr-eval": "^2.0.2", + "flat": "^5.0.2", + "js-tiktoken": "^1.0.6", + "jsonpointer": "^5.0.1", + "ml-distance": "^4.0.0", + "object-hash": "^3.0.0", + "openai": "^3.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0", + "yaml": "^2.2.1", + "zod": "^3.21.4", + "zod-to-json-schema": "^3.20.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.310.0", + "@aws-sdk/client-lambda": "^3.310.0", + "@aws-sdk/client-s3": "^3.310.0", + "@aws-sdk/client-sagemaker-runtime": "^3.310.0", + "@clickhouse/client": "^0.0.14", + "@getmetal/metal-sdk": "*", + "@getzep/zep-js": "^0.3.1", + "@gomomento/sdk": "^1.23.0", + "@huggingface/inference": "^1.5.1", + "@opensearch-project/opensearch": "*", + "@pinecone-database/pinecone": "*", + "@qdrant/js-client-rest": "^1.2.0", + "@supabase/postgrest-js": "^1.1.1", + "@supabase/supabase-js": "^2.10.0", + "@tensorflow-models/universal-sentence-encoder": "*", + "@tensorflow/tfjs-converter": "*", + "@tensorflow/tfjs-core": "*", + "@tigrisdata/vector": "^1.1.0", + "@upstash/redis": "^1.20.6", + "@zilliz/milvus2-sdk-node": ">=2.2.7", + "apify-client": "^2.7.1", + "axios": "*", + "cheerio": "^1.0.0-rc.12", + "chromadb": "^1.5.2", + "cohere-ai": "^5.0.2", + "d3-dsv": "^2.0.0", + "epub2": "^3.0.1", + "faiss-node": "^0.2.1", + "google-auth-library": "^8.8.0", + "hnswlib-node": "^1.4.2", + "html-to-text": "^9.0.5", + "ignore": "^5.2.0", + "mammoth": "*", + "mongodb": "^5.2.0", + "mysql2": "^3.3.3", + "pdf-parse": "1.1.1", + "peggy": "^3.0.2", + "pg": "^8.11.0", + "pickleparser": "^0.1.0", + "playwright": "^1.32.1", + "puppeteer": "^19.7.2", + "redis": "^4.6.4", + "replicate": "^0.9.0", + "srt-parser-2": "^1.2.2", + "typeorm": "^0.3.12", + "weaviate-ts-client": "^1.0.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-dynamodb": { + "optional": true + }, + "@aws-sdk/client-lambda": { + "optional": true + }, + "@aws-sdk/client-s3": { + "optional": true + }, + "@aws-sdk/client-sagemaker-runtime": { + "optional": true + }, + "@clickhouse/client": { + "optional": true + }, + "@getmetal/metal-sdk": { + "optional": true + }, + "@getzep/zep-js": { + "optional": true + }, + "@gomomento/sdk": { + "optional": true + }, + "@huggingface/inference": { + "optional": true + }, + "@opensearch-project/opensearch": { + "optional": true + }, + "@pinecone-database/pinecone": { + "optional": true + }, + "@qdrant/js-client-rest": { + "optional": true + }, + "@supabase/postgrest-js": { + "optional": true + }, + "@supabase/supabase-js": { + "optional": true + }, + "@tensorflow-models/universal-sentence-encoder": { + "optional": true + }, + "@tensorflow/tfjs-converter": { + "optional": true + }, + "@tensorflow/tfjs-core": { + "optional": true + }, + "@tigrisdata/vector": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@zilliz/milvus2-sdk-node": { + "optional": true + }, + "apify-client": { + "optional": true + }, + "axios": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "chromadb": { + "optional": true + }, + "cohere-ai": { + "optional": true + }, + "d3-dsv": { + "optional": true + }, + "epub2": { + "optional": true + }, + "faiss-node": { + "optional": true + }, + "google-auth-library": { + "optional": true + }, + "hnswlib-node": { + "optional": true + }, + "html-to-text": { + "optional": true + }, + "ignore": { + "optional": true + }, + "mammoth": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pdf-parse": { + "optional": true + }, + "peggy": { + "optional": true + }, + "pg": { + "optional": true + }, + "pickleparser": { + "optional": true + }, + "playwright": { + "optional": true + }, + "puppeteer": { + "optional": true + }, + "redis": { + "optional": true + }, + "replicate": { + "optional": true + }, + "srt-parser-2": { + "optional": true + }, + "typeorm": { + "optional": true + }, + "weaviate-ts-client": { + "optional": true + } + } + }, + "node_modules/langchain/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2648,7 +2994,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -2657,7 +3002,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -2697,6 +3041,46 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/ml-array-mean": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ml-array-mean/-/ml-array-mean-1.1.6.tgz", + "integrity": "sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ==", + "dependencies": { + "ml-array-sum": "^1.1.6" + } + }, + "node_modules/ml-array-sum": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ml-array-sum/-/ml-array-sum-1.1.6.tgz", + "integrity": "sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw==", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-distance": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ml-distance/-/ml-distance-4.0.1.tgz", + "integrity": "sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw==", + "dependencies": { + "ml-array-mean": "^1.1.6", + "ml-distance-euclidean": "^2.0.0", + "ml-tree-similarity": "^1.0.0" + } + }, + "node_modules/ml-distance-euclidean": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz", + "integrity": "sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q==" + }, + "node_modules/ml-tree-similarity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz", + "integrity": "sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==", + "dependencies": { + "binary-search": "^1.3.5", + "num-sort": "^2.0.0" + } + }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -2847,6 +3231,25 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -2876,6 +3279,25 @@ "node": ">=0.10.0" } }, + "node_modules/num-sort": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/num-sort/-/num-sort-2.1.0.tgz", + "integrity": "sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2885,6 +3307,15 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "dependencies": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -2902,6 +3333,14 @@ "node": ">= 0.8.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2932,6 +3371,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-timeout": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz", @@ -3417,6 +3894,14 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3851,6 +4336,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-loader": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", @@ -3997,6 +4487,11 @@ "node": ">=10.13.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "node_modules/webpack": { "version": "5.86.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", @@ -4120,6 +4615,15 @@ "node": ">=10.13.0" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/when-exit": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz", @@ -4216,6 +4720,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4278,9 +4790,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.21.1.tgz", + "integrity": "sha512-y5g0MPxDq+YG/T+cHGPYH4PcBpyCqwK6wxeJ76MR563y0gk/14HKfebq8xHiItY7lkc9GDFygCnkvNDTvAhYAg==", + "peerDependencies": { + "zod": "^3.21.4" + } } }, "dependencies": { + "@anthropic-ai/sdk": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.4.4.tgz", + "integrity": "sha512-Z/39nQi1sSUCeLII3lsAbL1u+0JF6cR2XmUEX9sLH0VtxmIjY6cjOUYjCkYh4oapTxOkhAFnVSAFJ6cxml2qXg==", + "requires": { + "@fortaine/fetch-event-source": "^3.0.6", + "cross-fetch": "^3.1.5" + } + }, "@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -4399,6 +4936,11 @@ "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", "dev": true }, + "@fortaine/fetch-event-source": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz", + "integrity": "sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw==" + }, "@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -4640,6 +5182,11 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", @@ -5059,6 +5606,11 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "atomically": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz", @@ -5068,6 +5620,14 @@ "when-exit": "^2.0.0" } }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5082,8 +5642,12 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" }, "brace-expansion": { "version": "1.1.11", @@ -5253,6 +5817,14 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -5304,6 +5876,14 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "requires": { + "node-fetch": "^2.6.11" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5344,6 +5924,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -5579,6 +6164,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -5590,6 +6180,11 @@ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz", "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==" }, + "expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5677,8 +6272,7 @@ "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" }, "flat-cache": { "version": "3.0.4", @@ -5696,6 +6290,11 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -5706,6 +6305,16 @@ "signal-exit": "^4.0.1" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5880,7 +6489,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", - "dev": true + "devOptional": true }, "immediate": { "version": "3.0.6", @@ -5936,6 +6545,11 @@ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, + "is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6105,6 +6719,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, "jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -6131,6 +6750,36 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "langchain": { + "version": "0.0.92", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.0.92.tgz", + "integrity": "sha512-77528kXJu9C391qV1a5jDzZLEjIblq/VtpH7xd4BwavgvhRmeSXR9WkrHHmih/Phpae4Ss3dtb7pcyd78rp/YA==", + "requires": { + "@anthropic-ai/sdk": "^0.4.3", + "ansi-styles": "^5.0.0", + "binary-extensions": "^2.2.0", + "expr-eval": "^2.0.2", + "flat": "^5.0.2", + "js-tiktoken": "^1.0.6", + "jsonpointer": "^5.0.1", + "ml-distance": "^4.0.0", + "object-hash": "^3.0.0", + "openai": "^3.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0", + "yaml": "^2.2.1", + "zod": "^3.21.4", + "zod-to-json-schema": "^3.20.4" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6219,14 +6868,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -6251,6 +6898,46 @@ "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "dev": true }, + "ml-array-mean": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ml-array-mean/-/ml-array-mean-1.1.6.tgz", + "integrity": "sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ==", + "requires": { + "ml-array-sum": "^1.1.6" + } + }, + "ml-array-sum": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ml-array-sum/-/ml-array-sum-1.1.6.tgz", + "integrity": "sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw==", + "requires": { + "is-any-array": "^2.0.0" + } + }, + "ml-distance": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ml-distance/-/ml-distance-4.0.1.tgz", + "integrity": "sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw==", + "requires": { + "ml-array-mean": "^1.1.6", + "ml-distance-euclidean": "^2.0.0", + "ml-tree-similarity": "^1.0.0" + } + }, + "ml-distance-euclidean": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz", + "integrity": "sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q==" + }, + "ml-tree-similarity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz", + "integrity": "sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==", + "requires": { + "binary-search": "^1.3.5", + "num-sort": "^2.0.0" + } + }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -6372,6 +7059,14 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -6395,6 +7090,16 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "num-sort": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/num-sort/-/num-sort-2.1.0.tgz", + "integrity": "sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6404,6 +7109,15 @@ "wrappy": "1" } }, + "openai": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "requires": { + "axios": "^0.26.0", + "form-data": "^4.0.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -6418,6 +7132,11 @@ "word-wrap": "^1.2.3" } }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6436,6 +7155,34 @@ "p-limit": "^3.0.2" } }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "dependencies": { + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + } + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, "p-timeout": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz", @@ -6770,6 +7517,11 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7070,6 +7822,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "ts-loader": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", @@ -7165,6 +7922,11 @@ "graceful-fs": "^4.1.2" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "webpack": { "version": "5.86.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", @@ -7242,6 +8004,15 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "when-exit": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz", @@ -7313,6 +8084,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7359,6 +8135,17 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" + }, + "zod-to-json-schema": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.21.1.tgz", + "integrity": "sha512-y5g0MPxDq+YG/T+cHGPYH4PcBpyCqwK6wxeJ76MR563y0gk/14HKfebq8xHiItY7lkc9GDFygCnkvNDTvAhYAg==", + "requires": {} } } } diff --git a/package.json b/package.json index a284b82..802b0eb 100644 --- a/package.json +++ b/package.json @@ -132,11 +132,11 @@ }, "views": { "chatgpt": [ - { - "type": "webview", - "id": "chatgpt.chatView", - "name": "ChatGPT" - } + { + "type": "webview", + "id": "chatgpt.chatView", + "name": "ChatGPT" + } ] }, "configuration": { @@ -198,7 +198,8 @@ "default": true, "description": "Keep the conversation going by using the same conversation ID for all requests (allows follow-up questions)", "order": 7 - },"chatgpt.timeoutLength": { + }, + "chatgpt.timeoutLength": { "type": "number", "default": "60", "description": "How long should the request wait for a response before timing out (in seconds)", @@ -222,7 +223,8 @@ "watch-tests": "tsc -p . -w --outDir out", "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js" + "test": "node ./out/test/runTest.js", + "file-agent": "node src/file_agent.js" }, "devDependencies": { "@types/glob": "^8.1.0", @@ -241,6 +243,7 @@ "webpack-cli": "^5.1.4" }, "dependencies": { - "chatgpt": "^5.2.5" + "chatgpt": "^5.2.5", + "langchain": "^0.0.92" } } diff --git a/resources/VectorDB-architecture.webp b/resources/VectorDB-architecture.webp new file mode 100644 index 0000000000000000000000000000000000000000..d6ee92829c4d8f5d3101f8cee1d9825119ef0973 GIT binary patch literal 28698 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/- +``` + +- ``: The type of PR, such as `bugfix`, `feature`, `enhancement`, `refactor`, or `docs`. Multiple types are ok and should appear as , +- ``: A brief description of the changes made, using hyphens to separate words. +- ``: The issue number associated with the changes made (if applicable). + +Example: + +``` +feature/advanced-chunking-strategy-123 +``` \ No newline at end of file diff --git a/retrieval/.gitignore b/retrieval/.gitignore new file mode 100644 index 0000000..952eee2 --- /dev/null +++ b/retrieval/.gitignore @@ -0,0 +1,138 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# .vscode files +.vscode/* + +# Pycharm +.idea/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +myvenv/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# macOS .DS_Store files +.DS_Store \ No newline at end of file diff --git a/retrieval/.well-known/ai-plugin.json b/retrieval/.well-known/ai-plugin.json new file mode 100644 index 0000000..678e3ca --- /dev/null +++ b/retrieval/.well-known/ai-plugin.json @@ -0,0 +1,19 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth": { + "type": "user_http", + "authorization_type": "bearer" + }, + "api": { + "type": "openapi", + "url": "https://your-app-url.com/.well-known/openapi.yaml", + "has_user_authentication": false + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "http://example.com/legal-info" +} diff --git a/retrieval/.well-known/logo.png b/retrieval/.well-known/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..af562f798e9ba772e44fb42c21d3b9de398d120d GIT binary patch literal 17015 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 + +heroku-push: + docker buildx build --platform linux/amd64 -t ${HEROKU_APP} . + docker tag ${HEROKU_APP} registry.heroku.com/${HEROKU_APP}/web + docker push registry.heroku.com/${HEROKU_APP}/web + heroku container:release web -a ${HEROKU_APP} + +heroku-login: + heroku container:login diff --git a/retrieval/README.md b/retrieval/README.md new file mode 100644 index 0000000..f3b80a9 --- /dev/null +++ b/retrieval/README.md @@ -0,0 +1,595 @@ +# ChatGPT Retrieval Plugin + +> **Join the [ChatGPT plugins waitlist here](https://openai.com/waitlist/plugins)!** + +Find an example video of a Retrieval Plugin that has access to the UN Annual Reports from 2018 to 2022 [here](https://cdn.openai.com/chat-plugins/retrieval-gh-repo-readme/Retrieval-Final.mp4). + +## Introduction + +The ChatGPT Retrieval Plugin repository provides a flexible solution for semantic search and retrieval of personal or organizational documents using natural language queries. The repository is organized into several directories: + +| Directory | Description | +| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| [`datastore`](/datastore) | Contains the core logic for storing and querying document embeddings using various vector database providers. | +| [`docs`](/docs) | Includes documentation for setting up and using each vector database provider, webhooks, and removing unused dependencies. | +| [`examples`](/examples) | Provides example configurations, authentication methods, and provider-specific examples. | +| [`local_server`](/local_server) | Contains an implementation of the retrieval plugin configured for localhost testing. | +| [`models`](/models) | Contains the data models used by the plugin, such as document and metadata models. | +| [`scripts`](/scripts) | Offers scripts for processing and uploading documents from different data sources. | +| [`server`](/server) | Houses the main FastAPI server implementation. | +| [`services`](/services) | Contains utility services for tasks like chunking, metadata extraction, and PII detection. | +| [`tests`](/tests) | Includes integration tests for various vector database providers. | +| [`xtras`](/xtras) | Xtra utils to store code in a Vector DB and query this DB. | +| [`.well-known`](/.well-known) | Stores the plugin manifest file and OpenAPI schema, which define the plugin configuration and API specification. | + +This README provides detailed information on how to set up, develop, and deploy the ChatGPT Retrieval Plugin. + +## Table of Contents + +- [ChatGPT Retrieval Plugin](#chatgpt-retrieval-plugin) + - [Introduction](#introduction) + - [Table of Contents](#table-of-contents) + - [Quickstart](#quickstart) + - [Testing in ChatGPT](#testing-in-chatgpt) + - [About](#about) + - [Plugins](#plugins) + - [Retrieval Plugin](#retrieval-plugin) + - [Memory Feature](#memory-feature) + - [Security](#security) + - [API Endpoints](#api-endpoints) + - [Development](#development) + - [Setup](#setup) + - [General Environment Variables](#general-environment-variables) + - [Using the plugin with Azure OpenAI](#using-the-plugin-with-azure-openai) + - [Choosing a Vector Database](#choosing-a-vector-database) + - [Pinecone](#pinecone) + - [Weaviate](#weaviate) + - [Zilliz](#zilliz) + - [Milvus](#milvus) + - [Qdrant](#qdrant) + - [Redis](#redis) + - [LlamaIndex](#llamaindex) + - [Chroma](#chroma) + - [Azure Cognitive Search](#azure-cognitive-search) + - [Supabase](#supabase) + - [Postgres](#postgres) + - [AnalyticDB](#analyticdb) + - [Running the API locally](#running-the-api-locally) + - [Testing a Localhost Plugin in ChatGPT](#testing-a-localhost-plugin-in-chatgpt) + - [Personalization](#personalization) + - [Authentication Methods](#authentication-methods) + - [Deployment](#deployment) + - [Installing a Developer Plugin](#installing-a-developer-plugin) + - [Webhooks](#webhooks) + - [Scripts](#scripts) + - [Pull Request (PR) Checklist](#pull-request-pr-checklist) + - [Pull Request Naming Convention](#pull-request-naming-convention) + - [Limitations](#limitations) + - [Future Directions](#future-directions) + - [Contributors](#contributors) + +## Quickstart + +Follow these steps to quickly set up and run the ChatGPT Retrieval Plugin: + +1. Install Python 3.10, if not already installed. +2. Clone the repository: `git clone https://github.com/openai/chatgpt-retrieval-plugin.git` +3. Navigate to the cloned repository directory: `cd /path/to/chatgpt-retrieval-plugin` +4. Install poetry: `pip install poetry` +5. Create a new virtual environment with Python 3.10: `poetry env use python3.10` +6. Activate the virtual environment: `poetry shell` +7. Install app dependencies: `poetry install` +8. Create a [bearer token](#general-environment-variables) +9. Set the required environment variables: + + ``` + export DATASTORE= + export BEARER_TOKEN= + export OPENAI_API_KEY= + + # Optional environment variables used when running Azure OpenAI + export OPENAI_API_BASE=https://.openai.azure.com/ + export OPENAI_API_TYPE=azure + export OPENAI_EMBEDDINGMODEL_DEPLOYMENTID= + export OPENAI_METADATA_EXTRACTIONMODEL_DEPLOYMENTID= + export OPENAI_COMPLETIONMODEL_DEPLOYMENTID= + export OPENAI_EMBEDDING_BATCH_SIZE= + + # Add the environment variables for your chosen vector DB. + # Some of these are optional; read the provider's setup docs in /docs/providers for more information. + + # Pinecone + export PINECONE_API_KEY= + export PINECONE_ENVIRONMENT= + export PINECONE_INDEX= + + # Weaviate + export WEAVIATE_URL= + export WEAVIATE_API_KEY= + export WEAVIATE_CLASS= + + # Zilliz + export ZILLIZ_COLLECTION= + export ZILLIZ_URI= + export ZILLIZ_USER= + export ZILLIZ_PASSWORD= + + # Milvus + export MILVUS_COLLECTION= + export MILVUS_HOST= + export MILVUS_PORT= + export MILVUS_USER= + export MILVUS_PASSWORD= + + # Qdrant + export QDRANT_URL= + export QDRANT_PORT= + export QDRANT_GRPC_PORT= + export QDRANT_API_KEY= + export QDRANT_COLLECTION= + + # AnalyticDB + export PG_HOST= + export PG_PORT= + export PG_USER= + export PG_PASSWORD= + export PG_DATABASE= + export PG_COLLECTION= + + + # Redis + export REDIS_HOST= + export REDIS_PORT= + export REDIS_PASSWORD= + export REDIS_INDEX_NAME= + export REDIS_DOC_PREFIX= + export REDIS_DISTANCE_METRIC= + export REDIS_INDEX_TYPE= + + # Llama + export LLAMA_INDEX_TYPE= + export LLAMA_INDEX_JSON_PATH= + export LLAMA_QUERY_KWARGS_JSON_PATH= + export LLAMA_RESPONSE_MODE= + + # Chroma + export CHROMA_COLLECTION= + export CHROMA_IN_MEMORY= + export CHROMA_PERSISTENCE_DIR= + export CHROMA_HOST= + export CHROMA_PORT= + + # Azure Cognitive Search + export AZURESEARCH_SERVICE= + export AZURESEARCH_INDEX= + export AZURESEARCH_API_KEY= (optional, uses key-free managed identity if not set) + + # Supabase + export SUPABASE_URL= + export SUPABASE_ANON_KEY= + + # Postgres + export PG_HOST= + export PG_PORT= + export PG_USER= + export PG_PASSWORD= + export PG_DATABASE= + ``` + +10. Run the API locally: `poetry run start` +11. Access the API documentation at `http://0.0.0.0:8000/docs` and test the API endpoints (make sure to add your bearer token). + +### Testing in ChatGPT + +To test a locally hosted plugin in ChatGPT, follow these steps: + +1. Run the API on localhost: `poetry run dev` +2. Follow the instructions in the [Testing a Localhost Plugin in ChatGPT](#testing-a-localhost-plugin-in-chatgpt) section of the README. + +For more detailed information on setting up, developing, and deploying the ChatGPT Retrieval Plugin, refer to the full Development section below. + +## About + +### Plugins + +Plugins are chat extensions designed specifically for language models like ChatGPT, enabling them to access up-to-date information, run computations, or interact with third-party services in response to a user's request. They unlock a wide range of potential use cases and enhance the capabilities of language models. + +Developers can create a plugin by exposing an API through their website and providing a standardized manifest file that describes the API. ChatGPT consumes these files and allows the AI models to make calls to the API defined by the developer. + +A plugin consists of: + +- An API +- An API schema (OpenAPI JSON or YAML format) +- A manifest (JSON file) that defines relevant metadata for the plugin + +The Retrieval Plugin already contains all of these components. Read the Chat Plugins blogpost [here](https://openai.com/blog/chatgpt-plugins), and find the docs [here](https://platform.openai.com/docs/plugins/introduction). + +### Retrieval Plugin + +This is a plugin for ChatGPT that enables semantic search and retrieval of personal or organizational documents. It allows users to obtain the most relevant document snippets from their data sources, such as files, notes, or emails, by asking questions or expressing needs in natural language. Enterprises can make their internal documents available to their employees through ChatGPT using this plugin. + +The plugin uses OpenAI's `text-embedding-ada-002` embeddings model to generate embeddings of document chunks, and then stores and queries them using a vector database on the backend. As an open-source and self-hosted solution, developers can deploy their own Retrieval Plugin and register it with ChatGPT. The Retrieval Plugin supports several vector database providers, allowing developers to choose their preferred one from a list. + +A FastAPI server exposes the plugin's endpoints for upserting, querying, and deleting documents. Users can refine their search results by using metadata filters by source, date, author, or other criteria. The plugin can be hosted on any cloud platform that supports Docker containers, such as Fly.io, Heroku, Render, or Azure Container Apps. To keep the vector database updated with the latest documents, the plugin can process and store documents from various data sources continuously, using incoming webhooks to the upsert and delete endpoints. Tools like [Zapier](https://zapier.com) or [Make](https://www.make.com) can help configure the webhooks based on events or schedules. + +### Memory Feature + +A notable feature of the Retrieval Plugin is its capacity to provide ChatGPT with memory. By utilizing the plugin's upsert endpoint, ChatGPT can save snippets from the conversation to the vector database for later reference (only when prompted to do so by the user). This functionality contributes to a more context-aware chat experience by allowing ChatGPT to remember and retrieve information from previous conversations. Learn how to configure the Retrieval Plugin with memory [here](/examples/memory). + +### Security + +The Retrieval Plugin allows ChatGPT to search a vector database of content, and then add the best results into the ChatGPT session. This means it doesn’t have any external effects, and the main risk consideration is data authorization and privacy. Developers should only add content into their Retrieval Plugin that they have authorization for and that they are fine with appearing in users’ ChatGPT sessions. You can choose from a number of different authentication methods to secure the plugin (more information [here](#authentication-methods)). + +### API Endpoints + +The Retrieval Plugin is built using FastAPI, a web framework for building APIs with Python. FastAPI allows for easy development, validation, and documentation of API endpoints. Find the FastAPI documentation [here](https://fastapi.tiangolo.com/). + +One of the benefits of using FastAPI is the automatic generation of interactive API documentation with Swagger UI. When the API is running locally, Swagger UI at `/docs` can be used to interact with the API endpoints, test their functionality, and view the expected request and response models. + +The plugin exposes the following endpoints for upserting, querying, and deleting documents from the vector database. All requests and responses are in JSON format, and require a valid bearer token as an authorization header. + +- `/upsert`: This endpoint allows uploading one or more documents and storing their text and metadata in the vector database. The documents are split into chunks of around 200 tokens, each with a unique ID. The endpoint expects a list of documents in the request body, each with a `text` field, and optional `id` and `metadata` fields. The `metadata` field can contain the following optional subfields: `source`, `source_id`, `url`, `created_at`, and `author`. The endpoint returns a list of the IDs of the inserted documents (an ID is generated if not initially provided). + +- `/upsert-file`: This endpoint allows uploading a single file (PDF, TXT, DOCX, PPTX, or MD) and storing its text and metadata in the vector database. The file is converted to plain text and split into chunks of around 200 tokens, each with a unique ID. The endpoint returns a list containing the generated id of the inserted file. + +- `/query`: This endpoint allows querying the vector database using one or more natural language queries and optional metadata filters. The endpoint expects a list of queries in the request body, each with a `query` and optional `filter` and `top_k` fields. The `filter` field should contain a subset of the following subfields: `source`, `source_id`, `document_id`, `url`, `created_at`, and `author`. The `top_k` field specifies how many results to return for a given query, and the default value is 3. The endpoint returns a list of objects that each contain a list of the most relevant document chunks for the given query, along with their text, metadata and similarity scores. + +- `/delete`: This endpoint allows deleting one or more documents from the vector database using their IDs, a metadata filter, or a delete_all flag. The endpoint expects at least one of the following parameters in the request body: `ids`, `filter`, or `delete_all`. The `ids` parameter should be a list of document IDs to delete; all document chunks for the document with these IDS will be deleted. The `filter` parameter should contain a subset of the following subfields: `source`, `source_id`, `document_id`, `url`, `created_at`, and `author`. The `delete_all` parameter should be a boolean indicating whether to delete all documents from the vector database. The endpoint returns a boolean indicating whether the deletion was successful. + +The detailed specifications and examples of the request and response models can be found by running the app locally and navigating to http://0.0.0.0:8000/openapi.json, or in the OpenAPI schema [here](/.well-known/openapi.yaml). Note that the OpenAPI schema only contains the `/query` endpoint, because that is the only function that ChatGPT needs to access. This way, ChatGPT can use the plugin only to retrieve relevant documents based on natural language queries or needs. However, if developers want to also give ChatGPT the ability to remember things for later, they can use the `/upsert` endpoint to save snippets from the conversation to the vector database. An example of a manifest and OpenAPI schema that gives ChatGPT access to the `/upsert` endpoint can be found [here](/examples/memory). + +To include custom metadata fields, edit the `DocumentMetadata` and `DocumentMetadataFilter` data models [here](/models/models.py), and update the OpenAPI schema [here](/.well-known/openapi.yaml). You can update this easily by running the app locally, copying the JSON found at http://0.0.0.0:8000/sub/openapi.json, and converting it to YAML format with [Swagger Editor](https://editor.swagger.io/). Alternatively, you can replace the `openapi.yaml` file with an `openapi.json` file. + +## Development + +### Setup + +This app uses Python 3.10, and [poetry](https://python-poetry.org/) for dependency management. + +Install Python 3.10 on your machine if it isn't already installed. It can be downloaded from the official [Python website](https://www.python.org/downloads/) or with a package manager like `brew` or `apt`, depending on your system. + +Clone the repository from GitHub: + +``` +git clone https://github.com/openai/chatgpt-retrieval-plugin.git +``` + +Navigate to the cloned repository directory: + +``` +cd /path/to/chatgpt-retrieval-plugin +``` + +Install poetry: + +``` +pip install poetry +``` + +Create a new virtual environment that uses Python 3.10: + +``` +poetry env use python3.10 +poetry shell +``` + +Install app dependencies using poetry: + +``` +poetry install +``` + +**Note:** If adding dependencies in the `pyproject.toml`, make sure to run `poetry lock` and `poetry install`. + +#### General Environment Variables + +The API requires the following environment variables to work: + +| Name | Required | Description | +| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DATASTORE` | Yes | This specifies the vector database provider you want to use to store and query embeddings. You can choose from `chroma`, `pinecone`, `weaviate`, `zilliz`, `milvus`, `qdrant`, `redis`, `azuresearch`, `supabase`, `postgres`, `analyticdb`. | +| `BEARER_TOKEN` | Yes | This is a secret token that you need to authenticate your requests to the API. You can generate one using any tool or method you prefer, such as [jwt.io](https://jwt.io/). | +| `OPENAI_API_KEY` | Yes | This is your OpenAI API key that you need to generate embeddings using the `text-embedding-ada-002` model. You can get an API key by creating an account on [OpenAI](https://openai.com/). | + +### Using the plugin with Azure OpenAI + +The Azure Open AI uses URLs that are specific to your resource and references models not by model name but by the deployment id. As a result, you need to set additional environment variables for this case. + +In addition to the `OPENAI_API_BASE` (your specific URL) and `OPENAI_API_TYPE` (azure), you should also set `OPENAI_EMBEDDINGMODEL_DEPLOYMENTID` which specifies the model to use for getting embeddings on upsert and query. For this, we recommend deploying `text-embedding-ada-002` model and using the deployment name here. + +If you wish to use the data preparation scripts, you will also need to set `OPENAI_METADATA_EXTRACTIONMODEL_DEPLOYMENTID`, used for metadata extraction and +`OPENAI_COMPLETIONMODEL_DEPLOYMENTID`, used for PII handling. + +### Choosing a Vector Database + +The plugin supports several vector database providers, each with different features, performance, and pricing. Depending on which one you choose, you will need to use a different Dockerfile and set different environment variables. The following sections provide brief introductions to each vector database provider. + +For more detailed instructions on setting up and using each vector database provider, please refer to the respective documentation in the `/docs/providers//setup.md` file ([folders here](/docs/providers)). + +#### Pinecone + +[Pinecone](https://www.pinecone.io) is a managed vector database designed for speed, scale, and rapid deployment to production. It supports hybrid search and is currently the only datastore to natively support SPLADE sparse vectors. For detailed setup instructions, refer to [`/docs/providers/pinecone/setup.md`](/docs/providers/pinecone/setup.md). + +#### Weaviate + +[Weaviate](https://weaviate.io/) is an open-source vector search engine built to scale seamlessly into billions of data objects. It supports hybrid search out-of-the-box, making it suitable for users who require efficient keyword searches. Weaviate can be self-hosted or managed, offering flexibility in deployment. For detailed setup instructions, refer to [`/docs/providers/weaviate/setup.md`](/docs/providers/weaviate/setup.md). + +#### Zilliz + +[Zilliz](https://zilliz.com) is a managed cloud-native vector database designed for billion-scale data. It offers a wide range of features, including multiple indexing algorithms, distance metrics, scalar filtering, time travel searches, rollback with snapshots, full RBAC, 99.9% uptime, separated storage and compute, and multi-language SDKs. For detailed setup instructions, refer to [`/docs/providers/zilliz/setup.md`](/docs/providers/zilliz/setup.md). + +#### Milvus + +[Milvus](https://milvus.io/) is an open-source, cloud-native vector database that scales to billions of vectors. It is the open-source version of Zilliz and shares many of its features, such as various indexing algorithms, distance metrics, scalar filtering, time travel searches, rollback with snapshots, multi-language SDKs, storage and compute separation, and cloud scalability. For detailed setup instructions, refer to [`/docs/providers/milvus/setup.md`](/docs/providers/milvus/setup.md). + +#### Qdrant + +[Qdrant](https://qdrant.tech/) is a vector database capable of storing documents and vector embeddings. It offers both self-hosted and managed [Qdrant Cloud](https://cloud.qdrant.io/) deployment options, providing flexibility for users with different requirements. For detailed setup instructions, refer to [`/docs/providers/qdrant/setup.md`](/docs/providers/qdrant/setup.md). + +#### Redis + +[Redis](https://redis.com/solutions/use-cases/vector-database/) is a real-time data platform suitable for a variety of use cases, including everyday applications and AI/ML workloads. It can be used as a low-latency vector engine by creating a Redis database with the [Redis Stack docker container](/examples/docker/redis/docker-compose.yml). For a hosted/managed solution, [Redis Cloud](https://app.redislabs.com/#/) is available. For detailed setup instructions, refer to [`/docs/providers/redis/setup.md`](/docs/providers/redis/setup.md). + +#### LlamaIndex + +[LlamaIndex](https://github.com/jerryjliu/llama_index) is a central interface to connect your LLM's with external data. +It provides a suite of in-memory indices over your unstructured and structured data for use with ChatGPT. +Unlike standard vector databases, LlamaIndex supports a wide range of indexing strategies (e.g. tree, keyword table, knowledge graph) optimized for different use-cases. +It is light-weight, easy-to-use, and requires no additional deployment. +All you need to do is specifying a few environment variables (optionally point to an existing saved Index json file). +Note that metadata filters in queries are not yet supported. +For detailed setup instructions, refer to [`/docs/providers/llama/setup.md`](/docs/providers/llama/setup.md). + +#### Chroma + +[Chroma](https://trychroma.com) is an AI-native open-source embedding database designed to make getting started as easy as possible. Chroma runs in-memory, or in a client-server setup. It supports metadata and keyword filtering out of the box. For detailed instructions, refer to [`/docs/providers/chroma/setup.md`](/docs/providers/chroma/setup.md). + +#### Azure Cognitive Search + +[Azure Cognitive Search](https://azure.microsoft.com/products/search/) is a complete retrieval cloud service that supports vector search, text search, and hybrid (vectors + text combined to yield the best of the two approaches). It also offers an [optional L2 re-ranking step](https://learn.microsoft.com/azure/search/semantic-search-overview) to further improve results quality. For detailed setup instructions, refer to [`/docs/providers/azuresearch/setup.md`](/docs/providers/azuresearch/setup.md) + +#### Supabase + +[Supabase](https://supabase.com/blog/openai-embeddings-postgres-vector) offers an easy and efficient way to store vectors via [pgvector](https://github.com/pgvector/pgvector) extension for Postgres Database. [You can use Supabase CLI](https://github.com/supabase/cli) to set up a whole Supabase stack locally or in the cloud or you can also use docker-compose, k8s and other options available. For a hosted/managed solution, try [Supabase.com](https://supabase.com/) and unlock the full power of Postgres with built-in authentication, storage, auto APIs, and Realtime features. For detailed setup instructions, refer to [`/docs/providers/supabase/setup.md`](/docs/providers/supabase/setup.md). + +#### Postgres + +[Postgres](https://www.postgresql.org) offers an easy and efficient way to store vectors via [pgvector](https://github.com/pgvector/pgvector) extension. To use pgvector, you will need to set up a PostgreSQL database with the pgvector extension enabled. For example, you can [use docker](https://www.docker.com/blog/how-to-use-the-postgres-docker-official-image/) to run locally. For a hosted/managed solution, you can use any of the cloud vendors which support [pgvector](https://github.com/pgvector/pgvector#hosted-postgres). For detailed setup instructions, refer to [`/docs/providers/postgres/setup.md`](/docs/providers/postgres/setup.md). + +#### AnalyticDB + +[AnalyticDB](https://www.alibabacloud.com/help/en/analyticdb-for-postgresql/latest/product-introduction-overview) is a distributed cloud-native vector database designed for storing documents and vector embeddings. It is fully compatible with PostgreSQL syntax and managed by Alibaba Cloud. AnalyticDB offers a powerful vector compute engine, processing billions of data vectors and providing features such as indexing algorithms, structured and unstructured data capabilities, real-time updates, distance metrics, scalar filtering, and time travel searches. For detailed setup instructions, refer to [`/docs/providers/analyticdb/setup.md`](/docs/providers/analyticdb/setup.md). + +### Running the API locally + +To run the API locally, you first need to set the requisite environment variables with the `export` command: + +``` +export DATASTORE= +export BEARER_TOKEN= +export OPENAI_API_KEY= + +``` + +Start the API with: + +``` +poetry run start +``` + +Append `docs` to the URL shown in the terminal and open it in a browser to access the API documentation and try out the endpoints (i.e. http://0.0.0.0:8000/docs). Make sure to enter your bearer token and test the API endpoints. + +**Note:** If you add new dependencies to the pyproject.toml file, you need to run `poetry lock` and `poetry install` to update the lock file and install the new dependencies. + +### Testing a Localhost Plugin in ChatGPT + +To test a localhost plugin in ChatGPT, use the provided [`local_server/main.py`](/local_server/main.py) file, which is specifically configured for localhost testing with CORS settings, no authentication and routes for the manifest, OpenAPI schema and logo. + +Follow these steps to test your localhost plugin: + +1. Run the localhost server using the `poetry run dev` command. This starts the server at the default address (e.g. `localhost:3333`). + +2. Visit [ChatGPT](https://chat.openai.com/), select "Plugins" from the model picker, click on the plugins picker, and click on "Plugin store" at the bottom of the list. + +3. Choose "Develop your own plugin" and enter your localhost URL (e.g. `localhost:3333`) when prompted. + +4. Your localhost plugin is now enabled for your ChatGPT session. + +For more information, refer to the [OpenAI documentation](https://platform.openai.com/docs/plugins/getting-started/openapi-definition). + +### Personalization + +You can personalize the Retrieval Plugin for your own use case by doing the following: + +- **Replace the logo**: Replace the image in [logo.png](/.well-known/logo.png) with your own logo. + +- **Edit the data models**: Edit the `DocumentMetadata` and `DocumentMetadataFilter` data models in [models.py](/models/models.py) to add custom metadata fields. Update the OpenAPI schema in [openapi.yaml](/.well-known/openapi.yaml) accordingly. To update the OpenAPI schema more easily, you can run the app locally, then navigate to `http://0.0.0.0:8000/sub/openapi.json` and copy the contents of the webpage. Then go to [Swagger Editor](https://editor.swagger.io/) and paste in the JSON to convert it to a YAML format. You could also replace the [openapi.yaml](/.well-known/openapi.yaml) file with an openapi.json file in the [.well-known](/.well-known) folder. + +- **Change the plugin name, description, and usage instructions**: Update the plugin name, user-facing description, and usage instructions for the model. You can either edit the descriptions in the [main.py](/server/main.py) file or update the [openapi.yaml](/.well-known/openapi.yaml) file. Follow the same instructions as in the previous step to update the OpenAPI schema. + +- **Enable ChatGPT to save information from conversations**: See the instructions in the [memory example folder](/examples/memory). + +### Authentication Methods + +You can choose from four options for authenticating requests to your plugin: + +1. **No Authentication**: Anyone can add your plugin and use its API without any credentials. This option is suitable if you are only exposing documents that are not sensitive or already public. It provides no security for your data. If using this method, copy the contents of this [main.py](/examples/authentication-methods/no-auth/main.py) into the [actual main.py file](/server/main.py). Example manifest [here](/examples/authentication-methods/no-auth/ai-plugin.json). + +2. **HTTP Bearer**: You can use a secret token as a header to authorize requests to your plugin. There are two variants of this option: + + - **User Level** (default for this implementation): Each user who adds your plugin to ChatGPT must provide the bearer token when adding the plugin. You can generate and distribute these tokens using any tool or method you prefer, such as [jwt.io](https://jwt.io/). This method provides better security as each user has to enter the shared access token. If you require a unique access token for each user, you will need to implement this yourself in the [main.py](/server/main.py) file. Example manifest [here](/examples/authentication-methods/user-http/ai-plugin.json). + + - **Service Level**: Anyone can add your plugin and use its API without credentials, but you must add a bearer token when registering the plugin. When you install your plugin, you need to add your bearer token, and will then receive a token from ChatGPT that you must include in your hosted manifest file. Your token will be used by ChatGPT to authorize requests to your plugin on behalf of all users who add it. This method is more convenient for users, but it may be less secure as all users share the same token and do not need to add a token to install the plugin. Example manifest [here](/examples/authentication-methods/service-http/ai-plugin.json). + +3. **OAuth**: Users must go through an OAuth flow to add your plugin. You can use an OAuth provider to authenticate users who add your plugin and grant them access to your API. This method offers the highest level of security and control, as users authenticate through a trusted third-party provider. However, you will need to implement the OAuth flow yourself in the [main.py](/server/main.py) file and provide the necessary parameters in your manifest file. Example manifest [here](/examples/authentication-methods/oauth/ai-plugin.json). + +Consider the benefits and drawbacks of each authentication method before choosing the one that best suits your use case and security requirements. If you choose to use a method different to the default (User Level HTTP), make sure to update the manifest file [here](/.well-known/ai-plugin.json). + +## Deployment + +You can deploy your app to different cloud providers, depending on your preferences and requirements. However, regardless of the provider you choose, you will need to update two files in your app: [openapi.yaml](/.well-known/openapi.yaml) and [ai-plugin.json](/.well-known/ai-plugin.json). As outlined above, these files define the API specification and the AI plugin configuration for your app, respectively. You need to change the url field in both files to match the address of your deployed app. + +Render has a 1-click deploy option that automatically updates the url field in both files: + +[Deploy to Render](https://render.com/deploy?repo=https://github.com/render-examples/chatgpt-retrieval-plugin/tree/main) + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. Refer to the respective documentation in the [`/docs/deployment/removing-unused-dependencies.md`](/docs/deployment/removing-unused-dependencies.md) file for information on removing unused dependencies for each provider. + +Instructions: + +- [Deploying to Fly.io](/docs/deployment/flyio.md) +- [Deploying to Heroku](/docs/deployment/heroku.md) +- [Deploying to Render](/docs/deployment/render.md) +- [Other Deployment Options](/docs/deployment/other-options.md) (Azure Container Apps, Google Cloud Run, AWS Elastic Container Service, etc.) + +Once you have deployed your app, consider uploading an initial batch of documents using one of [these scripts](/scripts) or by calling the `/upsert` endpoint. + +## Installing a Developer Plugin + +To install a developer plugin, follow the steps below: + +- First, create your developer plugin by deploying it to your preferred hosting platform (e.g. Fly.io, Heroku, etc.) and updating the plugin URL in the manifest file and OpenAPI schema. + +- Go to [ChatGPT](https://chat.openai.com/) and select "Plugins" from the model picker. + +- From the plugins picker, scroll to the bottom and click on "Plugin store." + +- Go to "Develop your own plugin" and follow the instructions provided. You will need to enter the domain where your plugin is deployed. + +- Follow the instructions based on the authentication type you have chosen for your plugin (e.g. if your plugin uses Service Level HTTP, you will have to paste in your access token, then paste the new access token you receive from the plugin flow into your [ai-plugin.json](/.well-known/ai-plugin.json) file and redeploy your app). + +- Next, you must add your plugin. Go to the "Plugin store" again and click on "Install an unverified plugin." + +- Follow the instructions provided, which will require you to enter the domain where your plugin is deployed. + +- Follow the instructions based on the authentication type you have chosen for your plugin (e.g. if your plugin uses User Level HTTP, you will have to paste in your bearer token). + +After completing these steps, your developer plugin should be installed and ready to use in ChatGPT. + +## Webhooks + +To keep the documents stored in the vector database up-to-date, consider using tools like [Zapier](https://zapier.com) or [Make](https://www.make.com) to configure incoming webhooks to your plugin's API based on events or schedules. For example, this could allow you to sync new information as you update your notes or receive emails. You can also use a [Zapier Transfer](https://zapier.com/blog/zapier-transfer-guide/) to batch process a collection of existing documents and upload them to the vector database. + +If you need to pass custom fields from these tools to your plugin, you might want to create an additional Retrieval Plugin API endpoint that calls the datastore's upsert function, such as `upsert-email`. This custom endpoint can be designed to accept specific fields from the webhook and process them accordingly. + +To set up an incoming webhook, follow these general steps: + +- Choose a webhook tool like Zapier or Make and create an account. +- Set up a new webhook or transfer in the tool, and configure it to trigger based on events or schedules. +- Specify the target URL for the webhook, which should be the API endpoint of your retrieval plugin (e.g. `https://your-plugin-url.com/upsert`). +- Configure the webhook payload to include the necessary data fields and format them according to your retrieval plugin's API requirements. +- Test the webhook to ensure it's working correctly and sending data to your retrieval plugin as expected. + +After setting up the webhook, you may want to run a backfill to ensure that any previously missed data is included in the vector database. + +Remember that if you want to use incoming webhooks to continuously sync data, you should consider running a backfill after setting these up to avoid missing any data. + +In addition to using tools like Zapier and Make, you can also build your own custom integrations to sync data with your Retrieval Plugin. This allows you to have more control over the data flow and tailor the integration to your specific needs and requirements. + +## Scripts + +The `scripts` folder contains scripts to batch upsert or process text documents from different data sources, such as a zip file, JSON file, or JSONL file. These scripts use the plugin's upsert utility functions to upload the documents and their metadata to the vector database, after converting them to plain text and splitting them into chunks. Each script folder has a README file that explains how to use it and what parameters it requires. You can also optionally screen the documents for personally identifiable information (PII) using a language model and skip them if detected, with the [`services.pii_detection`](/services/pii_detection.py) module. This can be helpful if you want to avoid uploading sensitive or private documents to the vector database unintentionally. Additionally, you can optionally extract metadata from the document text using a language model, with the [`services.extract_metadata`](/services/extract_metadata.py) module. This can be useful if you want to enrich the document metadata. **Note:** if using incoming webhooks to continuously sync data, consider running a backfill after setting these up to avoid missing any data. + +The scripts are: + +- [`process_json`](scripts/process_json/): This script processes a file dump of documents in a JSON format and stores them in the vector database with some metadata. The format of the JSON file should be a list of JSON objects, where each object represents a document. The JSON object should have a `text` field and optionally other fields to populate the metadata. You can provide custom metadata as a JSON string and flags to screen for PII and extract metadata. +- [`process_jsonl`](scripts/process_jsonl/): This script processes a file dump of documents in a JSONL format and stores them in the vector database with some metadata. The format of the JSONL file should be a newline-delimited JSON file, where each line is a valid JSON object representing a document. The JSON object should have a `text` field and optionally other fields to populate the metadata. You can provide custom metadata as a JSON string and flags to screen for PII and extract metadata. +- [`process_zip`](scripts/process_zip/): This script processes a file dump of documents in a zip file and stores them in the vector database with some metadata. The format of the zip file should be a flat zip file folder of docx, pdf, txt, md, pptx or csv files. You can provide custom metadata as a JSON string and flags to screen for PII and extract metadata. + +## Pull Request (PR) Checklist +If you'd like to contribute, please follow the checklist below when submitting a PR. This will help us review and merge your changes faster! Thank you for contributing! + +1. **Type of PR**: Indicate the type of PR by adding a label in square brackets at the beginning of the title, such as `[Bugfix]`, `[Feature]`, `[Enhancement]`, `[Refactor]`, or `[Documentation]`. + +2. **Short Description**: Provide a brief, informative description of the PR that explains the changes made. + +3. **Issue(s) Linked**: Mention any related issue(s) by using the keyword `Fixes` or `Closes` followed by the respective issue number(s) (e.g., Fixes #123, Closes #456). + +4. **Branch**: Ensure that you have created a new branch for the changes, and it is based on the latest version of the `main` branch. + +5. **Code Changes**: Make sure the code changes are minimal, focused, and relevant to the issue or feature being addressed. + +6. **Commit Messages**: Write clear and concise commit messages that explain the purpose of each commit. + +7. **Tests**: Include unit tests and/or integration tests for any new code or changes to existing code. Make sure all tests pass before submitting the PR. + +8. **Documentation**: Update relevant documentation (e.g., README, inline comments, or external documentation) to reflect any changes made. + +9. **Review Requested**: Request a review from at least one other contributor or maintainer of the repository. + +10. **Video Submission** (For Complex/Large PRs): If your PR introduces significant changes, complexities, or a large number of lines of code, submit a brief video walkthrough along with the PR. The video should explain the purpose of the changes, the logic behind them, and how they address the issue or add the proposed feature. This will help reviewers to better understand your contribution and expedite the review process. + +## Pull Request Naming Convention + +Use the following naming convention for your PR branches: + +``` +/- +``` + +- ``: The type of PR, such as `bugfix`, `feature`, `enhancement`, `refactor`, or `docs`. Multiple types are ok and should appear as , +- ``: A brief description of the changes made, using hyphens to separate words. +- ``: The issue number associated with the changes made (if applicable). + +Example: + +``` +feature/advanced-chunking-strategy-123 +``` + +## Limitations + +While the ChatGPT Retrieval Plugin is designed to provide a flexible solution for semantic search and retrieval, it does have some limitations: + +- **Keyword search limitations**: The embeddings generated by the `text-embedding-ada-002` model may not always be effective at capturing exact keyword matches. As a result, the plugin might not return the most relevant results for queries that rely heavily on specific keywords. Some vector databases, like Pinecone, Weaviate and Azure Cognitive Search, use hybrid search and might perform better for keyword searches. +- **Sensitive data handling**: The plugin does not automatically detect or filter sensitive data. It is the responsibility of the developers to ensure that they have the necessary authorization to include content in the Retrieval Plugin and that the content complies with data privacy requirements. +- **Scalability**: The performance of the plugin may vary depending on the chosen vector database provider and the size of the dataset. Some providers may offer better scalability and performance than others. +- **Language support**: The plugin currently uses OpenAI's `text-embedding-ada-002` model, which is optimized for use in English. However, it is still robust enough to generate good results for a variety of languages. +- **Metadata extraction**: The optional metadata extraction feature relies on a language model to extract information from the document text. This process may not always be accurate, and the quality of the extracted metadata may vary depending on the document content and structure. +- **PII detection**: The optional PII detection feature is not foolproof and may not catch all instances of personally identifiable information. Use this feature with caution and verify its effectiveness for your specific use case. + +## Future Directions + +The ChatGPT Retrieval Plugin provides a flexible solution for semantic search and retrieval, but there is always potential for further development. We encourage users to contribute to the project by submitting pull requests for new features or enhancements. Notable contributions may be acknowledged with OpenAI credits. + +Some ideas for future directions include: + +- **More vector database providers**: If you are interested in integrating another vector database provider with the ChatGPT Retrieval Plugin, feel free to submit an implementation. +- **Additional scripts**: Expanding the range of scripts available for processing and uploading documents from various data sources would make the plugin even more versatile. +- **User Interface**: Developing a user interface for managing documents and interacting with the plugin could improve the user experience. +- **Hybrid search / TF-IDF option**: Enhancing the [datastore's upsert function](/datastore/datastore.py#L18) with an option to use hybrid search or TF-IDF indexing could improve the plugin's performance for keyword-based queries. +- **Advanced chunking strategies and embeddings calculations**: Implementing more sophisticated chunking strategies and embeddings calculations, such as embedding document titles and summaries, performing weighted averaging of document chunks and summaries, or calculating the average embedding for a document, could lead to better search results. +- **Custom metadata**: Allowing users to add custom metadata to document chunks, such as titles or other relevant information, might improve the retrieved results in some use cases. +- **Additional optional services**: Integrating more optional services, such as summarizing documents or pre-processing documents before embedding them, could enhance the plugin's functionality and quality of retrieved results. These services could be implemented using language models and integrated directly into the plugin, rather than just being available in the scripts. + +We welcome contributions from the community to help improve the ChatGPT Retrieval Plugin and expand its capabilities. If you have an idea or feature you'd like to contribute, please submit a pull request to the repository. + +## Contributors + +We would like to extend our gratitude to the following contributors for their code / documentation contributions, and support in integrating various vector database providers with the ChatGPT Retrieval Plugin: + +- [Pinecone](https://www.pinecone.io/) + - [acatav](https://github.com/acatav) + - [gkogan](https://github.com/gkogan) + - [jamescalam](https://github.com/jamescalam) +- [Weaviate](https://www.semi.technology/) + - [byronvoorbach](https://github.com/byronvoorbach) + - [hsm207](https://github.com/hsm207) + - [sebawita](https://github.com/sebawita) +- [Zilliz](https://zilliz.com/) + - [filip-halt](https://github.com/filip-halt) +- [Milvus](https://milvus.io/) + - [filip-halt](https://github.com/filip-halt) +- [Qdrant](https://qdrant.tech/) + - [kacperlukawski](https://github.com/kacperlukawski) +- [Redis](https://redis.io/) + - [spartee](https://github.com/spartee) + - [tylerhutcherson](https://github.com/tylerhutcherson) +- [LlamaIndex](https://github.com/jerryjliu/llama_index) + - [jerryjliu](https://github.com/jerryjliu) + - [Disiok](https://github.com/Disiok) +- [Supabase](https://supabase.com/) + - [egor-romanov](https://github.com/egor-romanov) +- [Postgres](https://www.postgresql.org/) + - [egor-romanov](https://github.com/egor-romanov) + - [mmmaia](https://github.com/mmmaia) diff --git a/retrieval/datastore/__init__.py b/retrieval/datastore/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retrieval/datastore/datastore.py b/retrieval/datastore/datastore.py new file mode 100644 index 0000000..ff0c79d --- /dev/null +++ b/retrieval/datastore/datastore.py @@ -0,0 +1,86 @@ +from abc import ABC, abstractmethod +from typing import Dict, List, Optional +import asyncio + +from models.models import ( + Document, + DocumentChunk, + DocumentMetadataFilter, + Query, + QueryResult, + QueryWithEmbedding, +) +from services.chunks import get_document_chunks +from services.openai import get_embeddings + + +class DataStore(ABC): + async def upsert( + self, documents: List[Document], chunk_token_size: Optional[int] = None + ) -> List[str]: + """ + Takes in a list of documents and inserts them into the database. + First deletes all the existing vectors with the document id (if necessary, depends on the vector db), then inserts the new ones. + Return a list of document ids. + """ + # Delete any existing vectors for documents with the input document ids + await asyncio.gather( + *[ + self.delete( + filter=DocumentMetadataFilter( + document_id=document.id, + ), + delete_all=False, + ) + for document in documents + if document.id + ] + ) + + chunks = get_document_chunks(documents, chunk_token_size) + + return await self._upsert(chunks) + + @abstractmethod + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of list of document chunks and inserts them into the database. + Return a list of document ids. + """ + + raise NotImplementedError + + async def query(self, queries: List[Query]) -> List[QueryResult]: + """ + Takes in a list of queries and filters and returns a list of query results with matching document chunks and scores. + """ + # get a list of of just the queries from the Query list + query_texts = [query.query for query in queries] + query_embeddings = get_embeddings(query_texts) + # hydrate the queries with embeddings + queries_with_embeddings = [ + QueryWithEmbedding(**query.dict(), embedding=embedding) + for query, embedding in zip(queries, query_embeddings) + ] + return await self._query(queries_with_embeddings) + + @abstractmethod + async def _query(self, queries: List[QueryWithEmbedding]) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + raise NotImplementedError + + @abstractmethod + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Multiple parameters can be used at once. + Returns whether the operation was successful. + """ + raise NotImplementedError diff --git a/retrieval/datastore/factory.py b/retrieval/datastore/factory.py new file mode 100644 index 0000000..adde49d --- /dev/null +++ b/retrieval/datastore/factory.py @@ -0,0 +1,63 @@ +from datastore.datastore import DataStore +import os + + +async def get_datastore() -> DataStore: + datastore = os.environ.get("DATASTORE") + assert datastore is not None + + match datastore: + case "chroma": + from datastore.providers.chroma_datastore import ChromaDataStore + + return ChromaDataStore() + case "llama": + from datastore.providers.llama_datastore import LlamaDataStore + + return LlamaDataStore() + + case "pinecone": + from datastore.providers.pinecone_datastore import PineconeDataStore + + return PineconeDataStore() + case "weaviate": + from datastore.providers.weaviate_datastore import WeaviateDataStore + + return WeaviateDataStore() + case "milvus": + from datastore.providers.milvus_datastore import MilvusDataStore + + return MilvusDataStore() + case "zilliz": + from datastore.providers.zilliz_datastore import ZillizDataStore + + return ZillizDataStore() + case "redis": + from datastore.providers.redis_datastore import RedisDataStore + + return await RedisDataStore.init() + case "qdrant": + from datastore.providers.qdrant_datastore import QdrantDataStore + + return QdrantDataStore() + case "azuresearch": + from datastore.providers.azuresearch_datastore import AzureSearchDataStore + + return AzureSearchDataStore() + case "supabase": + from datastore.providers.supabase_datastore import SupabaseDataStore + + return SupabaseDataStore() + case "postgres": + from datastore.providers.postgres_datastore import PostgresDataStore + + return PostgresDataStore() + case "analyticdb": + from datastore.providers.analyticdb_datastore import AnalyticDBDataStore + + return AnalyticDBDataStore() + case _: + raise ValueError( + f"Unsupported vector database: {datastore}. " + f"Try one of the following: llama, pinecone, weaviate, milvus, zilliz, redis, or qdrant" + ) \ No newline at end of file diff --git a/retrieval/datastore/providers/__init__.py b/retrieval/datastore/providers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retrieval/datastore/providers/analyticdb_datastore.py b/retrieval/datastore/providers/analyticdb_datastore.py new file mode 100644 index 0000000..ba206f2 --- /dev/null +++ b/retrieval/datastore/providers/analyticdb_datastore.py @@ -0,0 +1,312 @@ +import os +import asyncio +from typing import Dict, List, Optional, Tuple, Any +from datetime import datetime +from loguru import logger + +from psycopg2cffi import compat + +compat.register() +import psycopg2 +from psycopg2.extras import DictCursor +from psycopg2.pool import SimpleConnectionPool + +from services.date import to_unix_timestamp +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + DocumentChunkWithScore, +) + +PG_CONFIG = { + "collection": os.environ.get("PG_COLLECTION", "document_chunks"), + "database": os.environ.get("PG_DATABASE", "postgres"), + "user": os.environ.get("PG_USER", "user"), + "password": os.environ.get("PG_PASSWORD", "password"), + "host": os.environ.get("PG_HOST", "localhost"), + "port": int(os.environ.get("PG_PORT", "5432")), +} +OUTPUT_DIM = 1536 + + +class AnalyticDBDataStore(DataStore): + def __init__(self, config: Dict[str, str] = PG_CONFIG): + self.collection_name = config["collection"] + self.user = config["user"] + self.password = config["password"] + self.database = config["database"] + self.host = config["host"] + self.port = config["port"] + + self.connection_pool = SimpleConnectionPool( + minconn=1, + maxconn=100, + dbname=self.database, + user=self.user, + password=self.password, + host=self.host, + port=self.port, + ) + + self._initialize_db() + + def _initialize_db(self): + conn = self.connection_pool.getconn() + try: + with conn.cursor() as cur: + self._create_table(cur) + self._create_embedding_index(cur) + conn.commit() + finally: + self.connection_pool.putconn(conn) + + def _create_table(self, cur: psycopg2.extensions.cursor): + cur.execute( + f""" + CREATE TABLE IF NOT EXISTS {self.collection_name} ( + id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::TEXT, + source TEXT, + source_id TEXT, + content TEXT, + document_id TEXT, + author TEXT, + url TEXT, + created_at TIMESTAMPTZ DEFAULT NOW(), + embedding real[] + ); + """ + ) + + def _create_embedding_index(self, cur: psycopg2.extensions.cursor): + cur.execute( + f""" + SELECT * FROM pg_indexes WHERE tablename='{self.collection_name}'; + """ + ) + index_exists = any( + index[2] == f"{self.collection_name}_embedding_idx" + for index in cur.fetchall() + ) + if not index_exists: + cur.execute( + f""" + CREATE INDEX {self.collection_name}_embedding_idx + ON {self.collection_name} + USING ann(embedding) + WITH ( + distancemeasure=L2, + dim=OUTPUT_DIM, + pq_segments=64, + hnsw_m=100, + pq_centers=2048 + ); + """ + ) + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a dict of document_ids to list of document chunks and inserts them into the database. + Return a list of document ids. + """ + loop = asyncio.get_event_loop() + tasks = [ + loop.run_in_executor(None, self._upsert_chunk, chunk) + for document_chunks in chunks.values() + for chunk in document_chunks + ] + await asyncio.gather(*tasks) + + return list(chunks.keys()) + + def _upsert_chunk(self, chunk: DocumentChunk): + created_at = ( + datetime.fromtimestamp(to_unix_timestamp(chunk.metadata.created_at)) + if chunk.metadata.created_at + else None + ) + data = ( + chunk.id, + chunk.text, + chunk.embedding, + chunk.metadata.document_id, + chunk.metadata.source, + chunk.metadata.source_id, + chunk.metadata.url, + chunk.metadata.author, + created_at, + ) + + conn = self.connection_pool.getconn() + try: + with conn.cursor() as cur: + # Construct the SQL query and data + query = f""" + INSERT INTO {self.collection_name} (id, content, embedding, document_id, source, source_id, url, author, created_at) + VALUES (%s::text, %s::text, %s::real[], %s::text, %s::text, %s::text, %s::text, %s::text, %s::timestamp with time zone) + ON CONFLICT (id) DO UPDATE SET + content = EXCLUDED.content, + embedding = EXCLUDED.embedding, + document_id = EXCLUDED.document_id, + source = EXCLUDED.source, + source_id = EXCLUDED.source_id, + url = EXCLUDED.url, + author = EXCLUDED.author, + created_at = EXCLUDED.created_at; + """ + + # Execute the query + cur.execute(query, data) + + # Commit the transaction + conn.commit() + finally: + self.connection_pool.putconn(conn) + + async def _query(self, queries: List[QueryWithEmbedding]) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + query_results: List[QueryResult] = [] + + def generate_query(query: QueryWithEmbedding) -> Tuple[str, List[Any]]: + embedding = "[" + ", ".join(str(x) for x in query.embedding) + "]" + q = f""" + SELECT + id, + content, + source, + source_id, + document_id, + url, + created_at, + author, + embedding, + l2_distance(embedding,array{embedding}::real[]) AS similarity + FROM + {self.collection_name} + """ + where_clause, params = generate_where_clause(query.filter) + q += where_clause + q += f"ORDER BY embedding <-> array{embedding}::real[] LIMIT {query.top_k};" + return q, params + + def generate_where_clause( + query_filter: Optional[DocumentMetadataFilter], + ) -> Tuple[str, List[Any]]: + if query_filter is None: + return "", [] + + conditions = [ + ("document_id=%s", query_filter.document_id), + ("source_id=%s", query_filter.source_id), + ("source LIKE %s", query_filter.source), + ("author LIKE %s", query_filter.author), + ("created_at >= %s", query_filter.start_date), + ("created_at <= %s", query_filter.end_date), + ] + + where_clause = "WHERE " + " AND ".join( + [cond[0] for cond in conditions if cond[1] is not None] + ) + + values = [cond[1] for cond in conditions if cond[1] is not None] + + return where_clause, values + + def fetch_data(cur, q: str, params: List[Any]): + cur.execute(q, params) + return cur.fetchall() + + def create_results(data): + results = [] + for row in data: + document_chunk = DocumentChunkWithScore( + id=row["id"], + text=row["content"], + score=float(row["similarity"]), + metadata=DocumentChunkMetadata( + source=row["source"], + source_id=row["source_id"], + document_id=row["document_id"], + url=row["url"], + created_at=str(row["created_at"]), + author=row["author"], + ), + ) + results.append(document_chunk) + return results + + conn = self.connection_pool.getconn() + try: + for query in queries: + try: + cur = conn.cursor(cursor_factory=DictCursor) + for query in queries: + q, params = generate_query(query) + data = fetch_data(cur, q, params) + results = create_results(data) + query_results.append( + QueryResult(query=query.query, results=results) + ) + except Exception as e: + logger.error(e) + query_results.append(QueryResult(query=query.query, results=[])) + return query_results + finally: + self.connection_pool.putconn(conn) + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + async def execute_delete(query: str, params: Optional[List] = None) -> bool: + conn = self.connection_pool.getconn() + try: + with conn.cursor() as cur: + if params: + cur.execute(query, params) + else: + cur.execute(query) + self.conn.commit() + return True + except Exception as e: + logger.error(e) + return False + finally: + self.connection_pool.putconn(conn) + + if delete_all: + query = f"DELETE FROM {self.collection_name} WHERE document_id LIKE %s;" + return await execute_delete(query, ["%"]) + elif ids: + query = f"DELETE FROM {self.collection_name} WHERE document_id IN ({','.join(['%s'] * len(ids))});" + return await execute_delete(query, ids) + elif filter is not None: + query, params = self._generate_delete_query(filter) + return await execute_delete(query, params) + else: + return True + + def _generate_delete_query( + self, filter: DocumentMetadataFilter + ) -> Tuple[str, List]: + conditions = [ + (filter.document_id, "document_id = %s"), + (filter.source, "source = %s"), + (filter.source_id, "source_id = %s"), + (filter.author, "author = %s"), + (filter.start_date, "created_at >= %s"), + (filter.end_date, "created_at <= %s"), + ] + + where_conditions = [f for value, f in conditions if value] + where_values = [value for value, _ in conditions if value] + + query = f"DELETE FROM {self.collection_name} WHERE {' AND '.join(where_conditions)};" + return query, where_values diff --git a/retrieval/datastore/providers/azuresearch_datastore.py b/retrieval/datastore/providers/azuresearch_datastore.py new file mode 100644 index 0000000..3852258 --- /dev/null +++ b/retrieval/datastore/providers/azuresearch_datastore.py @@ -0,0 +1,260 @@ +import asyncio +import os +import re +import time +import base64 +from typing import Dict, List, Optional, Union +from datastore.datastore import DataStore +from models.models import DocumentChunk, DocumentChunkMetadata, DocumentChunkWithScore, DocumentMetadataFilter, Query, QueryResult, QueryWithEmbedding +from loguru import logger +from azure.search.documents.aio import SearchClient +from azure.search.documents.models import Vector, QueryType +from azure.search.documents.indexes import SearchIndexClient +from azure.search.documents.indexes.models import * +from azure.core.credentials import AzureKeyCredential +from azure.identity import DefaultAzureCredential as DefaultAzureCredentialSync +from azure.identity.aio import DefaultAzureCredential + +AZURESEARCH_SERVICE = os.environ.get("AZURESEARCH_SERVICE") +AZURESEARCH_INDEX = os.environ.get("AZURESEARCH_INDEX") +AZURESEARCH_API_KEY = os.environ.get("AZURESEARCH_API_KEY") +AZURESEARCH_SEMANTIC_CONFIG = os.environ.get("AZURESEARCH_SEMANTIC_CONFIG") +AZURESEARCH_LANGUAGE = os.environ.get("AZURESEARCH_LANGUAGE", "en-us") +AZURESEARCH_DISABLE_HYBRID = os.environ.get("AZURESEARCH_DISABLE_HYBRID") +AZURESEARCH_DIMENSIONS = os.environ.get("AZURESEARCH_DIMENSIONS", 1536) # Default to OpenAI's ada-002 embedding model vector size +assert AZURESEARCH_SERVICE is not None +assert AZURESEARCH_INDEX is not None + +# Allow overriding field names for Azure Search +FIELDS_ID = os.environ.get("AZURESEARCH_FIELDS_ID", "id") +FIELDS_TEXT = os.environ.get("AZURESEARCH_FIELDS_TEXT", "text") +FIELDS_EMBEDDING = os.environ.get("AZURESEARCH_FIELDS_EMBEDDING", "embedding") +FIELDS_DOCUMENT_ID = os.environ.get("AZURESEARCH_FIELDS_DOCUMENT_ID", "document_id") +FIELDS_SOURCE = os.environ.get("AZURESEARCH_FIELDS_SOURCE", "source") +FIELDS_SOURCE_ID = os.environ.get("AZURESEARCH_FIELDS_SOURCE_ID", "source_id") +FIELDS_URL = os.environ.get("AZURESEARCH_FIELDS_URL", "url") +FIELDS_CREATED_AT = os.environ.get("AZURESEARCH_FIELDS_CREATED_AT", "created_at") +FIELDS_AUTHOR = os.environ.get("AZURESEARCH_FIELDS_AUTHOR", "author") + +MAX_UPLOAD_BATCH_SIZE = 1000 +MAX_DELETE_BATCH_SIZE = 1000 + +class AzureSearchDataStore(DataStore): + def __init__(self): + self.client = SearchClient( + endpoint=f"https://{AZURESEARCH_SERVICE}.search.windows.net", + index_name=AZURESEARCH_INDEX, + credential=AzureSearchDataStore._create_credentials(True), + user_agent="retrievalplugin" + ) + + mgmt_client = SearchIndexClient( + endpoint=f"https://{AZURESEARCH_SERVICE}.search.windows.net", + credential=AzureSearchDataStore._create_credentials(False), + user_agent="retrievalplugin" + ) + if AZURESEARCH_INDEX not in [name for name in mgmt_client.list_index_names()]: + self._create_index(mgmt_client) + else: + logger.info(f"Using existing index {AZURESEARCH_INDEX} in service {AZURESEARCH_SERVICE}") + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + azdocuments: List[Dict] = [] + + async def upload(): + r = await self.client.upload_documents(documents=azdocuments) + count = sum(1 for rr in r if rr.succeeded) + logger.info(f"Upserted {count} chunks out of {len(azdocuments)}") + if count < len(azdocuments): + raise Exception(f"Failed to upload {len(azdocuments) - count} chunks") + + ids = [] + for document_id, document_chunks in chunks.items(): + ids.append(document_id) + for chunk in document_chunks: + azdocuments.append({ + # base64-encode the id string to stay within Azure Search's valid characters for keys + FIELDS_ID: base64.urlsafe_b64encode(bytes(chunk.id, "utf-8")).decode("ascii"), + FIELDS_TEXT: chunk.text, + FIELDS_EMBEDDING: chunk.embedding, + FIELDS_DOCUMENT_ID: document_id, + FIELDS_SOURCE: chunk.metadata.source, + FIELDS_SOURCE_ID: chunk.metadata.source_id, + FIELDS_URL: chunk.metadata.url, + FIELDS_CREATED_AT: chunk.metadata.created_at, + FIELDS_AUTHOR: chunk.metadata.author, + }) + + if len(azdocuments) >= MAX_UPLOAD_BATCH_SIZE: + await upload() + azdocuments = [] + + if len(azdocuments) > 0: + await upload() + + return ids + + async def delete(self, ids: Optional[List[str]] = None, filter: Optional[DocumentMetadataFilter] = None, delete_all: Optional[bool] = None) -> bool: + filter = None if delete_all else self._translate_filter(filter) + if delete_all or filter is not None: + deleted = set() + while True: + search_result = await self.client.search(None, filter=filter, top=MAX_DELETE_BATCH_SIZE, include_total_count=True, select=FIELDS_ID) + if await search_result.get_count() == 0: + break + documents = [{ FIELDS_ID: d[FIELDS_ID] } async for d in search_result if d[FIELDS_ID] not in deleted] + if len(documents) > 0: + logger.info(f"Deleting {len(documents)} chunks " + ("using a filter" if filter is not None else "using delete_all")) + del_result = await self.client.delete_documents(documents=documents) + if not all([rr.succeeded for rr in del_result]): + raise Exception("Failed to delete documents") + deleted.update([d[FIELDS_ID] for d in documents]) + else: + # All repeats, delay a bit to let the index refresh and try again + time.sleep(0.25) + + if ids is not None and len(ids) > 0: + for id in ids: + logger.info(f"Deleting chunks for document id {id}") + await self.delete(filter=DocumentMetadataFilter(document_id=id)) + + return True + + async def _query(self, queries: List[QueryWithEmbedding]) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + return await asyncio.gather(*(self._single_query(query) for query in queries)) + + async def _single_query(self, query: QueryWithEmbedding) -> QueryResult: + """ + Takes in a single query and filters and returns a query result with matching document chunks and scores. + """ + filter = self._translate_filter(query.filter) if query.filter is not None else None + try: + vector_top_k = query.top_k if filter is None else query.top_k * 2 + q = query.query if not AZURESEARCH_DISABLE_HYBRID else None + if AZURESEARCH_SEMANTIC_CONFIG != None and not AZURESEARCH_DISABLE_HYBRID: + # Ensure we're feeding a good number of candidates to the L2 reranker + vector_top_k = max(50, vector_top_k) + r = await self.client.search( + q, + filter=filter, + top=query.top_k, + vector=Vector(value=query.embedding, k=vector_top_k, fields=FIELDS_EMBEDDING), + query_type=QueryType.SEMANTIC, + query_language=AZURESEARCH_LANGUAGE, + semantic_configuration_name=AZURESEARCH_SEMANTIC_CONFIG) + else: + r = await self.client.search( + q, + filter=filter, + top=query.top_k, + vector=Vector(value=query.embedding, k=vector_top_k, fields=FIELDS_EMBEDDING)) + results: List[DocumentChunkWithScore] = [] + async for hit in r: + f = lambda field: hit.get(field) if field != "-" else None + results.append(DocumentChunkWithScore( + id=hit[FIELDS_ID], + text=hit[FIELDS_TEXT], + metadata=DocumentChunkMetadata( + document_id=f(FIELDS_DOCUMENT_ID), + source=f(FIELDS_SOURCE), + source_id=f(FIELDS_SOURCE_ID), + url=f(FIELDS_URL), + created_at=f(FIELDS_CREATED_AT), + author=f(FIELDS_AUTHOR) + ), + score=hit["@search.score"] + )) + + return QueryResult(query=query.query, results=results) + except Exception as e: + raise Exception(f"Error querying the index: {e}") + + @staticmethod + def _translate_filter(filter: DocumentMetadataFilter) -> str: + """ + Translates a DocumentMetadataFilter into an Azure Search filter string + """ + if filter is None: + return None + + escape = lambda s: s.replace("'", "''") + + # regex to validate dates are in OData format + date_re = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z") + + filter_list = [] + if filter.document_id is not None: + filter_list.append(f"{FIELDS_DOCUMENT_ID} eq '{escape(filter.document_id)}'") + if filter.source is not None: + filter_list.append(f"{FIELDS_SOURCE} eq '{escape(filter.source)}'") + if filter.source_id is not None: + filter_list.append(f"{FIELDS_SOURCE_ID} eq '{escape(filter.source_id)}'") + if filter.author is not None: + filter_list.append(f"{FIELDS_AUTHOR} eq '{escape(filter.author)}'") + if filter.start_date is not None: + if not date_re.match(filter.start_date): + raise ValueError(f"start_date must be in OData format, got {filter.start_date}") + filter_list.append(f"{FIELDS_CREATED_AT} ge {filter.start_date}") + if filter.end_date is not None: + if not date_re.match(filter.end_date): + raise ValueError(f"end_date must be in OData format, got {filter.end_date}") + filter_list.append(f"{FIELDS_CREATED_AT} le {filter.end_date}") + return " and ".join(filter_list) if len(filter_list) > 0 else None + + def _create_index(self, mgmt_client: SearchIndexClient): + """ + Creates an Azure Cognitive Search index, including a semantic search configuration if a name is specified for it + """ + logger.info( + f"Creating index {AZURESEARCH_INDEX} in service {AZURESEARCH_SERVICE}" + + (f" with semantic search configuration {AZURESEARCH_SEMANTIC_CONFIG}" if AZURESEARCH_SEMANTIC_CONFIG is not None else "") + ) + mgmt_client.create_index( + SearchIndex( + name=AZURESEARCH_INDEX, + fields=[ + SimpleField(name=FIELDS_ID, type=SearchFieldDataType.String, key=True), + SearchableField(name=FIELDS_TEXT, type=SearchFieldDataType.String, analyzer_name="standard.lucene"), + SearchField(name=FIELDS_EMBEDDING, type=SearchFieldDataType.Collection(SearchFieldDataType.Single), + hidden=False, searchable=True, filterable=False, sortable=False, facetable=False, + dimensions=AZURESEARCH_DIMENSIONS, vector_search_configuration="default"), + SimpleField(name=FIELDS_DOCUMENT_ID, type=SearchFieldDataType.String, filterable=True, sortable=True), + SimpleField(name=FIELDS_SOURCE, type=SearchFieldDataType.String, filterable=True, sortable=True), + SimpleField(name=FIELDS_SOURCE_ID, type=SearchFieldDataType.String, filterable=True, sortable=True), + SimpleField(name=FIELDS_URL, type=SearchFieldDataType.String), + SimpleField(name=FIELDS_CREATED_AT, type=SearchFieldDataType.DateTimeOffset, filterable=True, sortable=True), + SimpleField(name=FIELDS_AUTHOR, type=SearchFieldDataType.String, filterable=True, sortable=True) + ], + semantic_settings=None if AZURESEARCH_SEMANTIC_CONFIG is None else SemanticSettings( + configurations=[SemanticConfiguration( + name=AZURESEARCH_SEMANTIC_CONFIG, + prioritized_fields=PrioritizedFields( + title_field=None, prioritized_content_fields=[SemanticField(field_name=FIELDS_TEXT)] + ) + )] + ), + vector_search=VectorSearch( + algorithm_configurations=[ + VectorSearchAlgorithmConfiguration( + name="default", + kind="hnsw", + # Could change to dotproduct for OpenAI's embeddings since they normalize vectors to unit length + hnsw_parameters=HnswParameters(metric="cosine") + ) + ] + ) + ) + ) + + @staticmethod + def _create_credentials(use_async: bool) -> Union[AzureKeyCredential, DefaultAzureCredential, DefaultAzureCredentialSync]: + if AZURESEARCH_API_KEY is None: + logger.info("Using DefaultAzureCredential for Azure Search, make sure local identity or managed identity are set up appropriately") + credential = DefaultAzureCredential() if use_async else DefaultAzureCredentialSync() + else: + logger.info("Using an API key to authenticate with Azure Search") + credential = AzureKeyCredential(AZURESEARCH_API_KEY) + return credential diff --git a/retrieval/datastore/providers/chroma_datastore.py b/retrieval/datastore/providers/chroma_datastore.py new file mode 100644 index 0000000..59b802d --- /dev/null +++ b/retrieval/datastore/providers/chroma_datastore.py @@ -0,0 +1,250 @@ +""" +Chroma datastore support for the ChatGPT retrieval plugin. + +Consult the Chroma docs and GitHub repo for more information: +- https://docs.trychroma.com/usage-guide?lang=py +- https://github.com/chroma-core/chroma +- https://www.trychroma.com/ +""" + +import os +from datetime import datetime +from typing import Dict, List, Optional + +import chromadb + +from datastore.datastore import DataStore +from models.models import ( + Document, + DocumentChunk, + DocumentChunkMetadata, + DocumentChunkWithScore, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + Source, +) +from services.chunks import get_document_chunks + +CHROMA_IN_MEMORY = os.environ.get("CHROMA_IN_MEMORY", "True") +CHROMA_PERSISTENCE_DIR = os.environ.get("CHROMA_PERSISTENCE_DIR", "openai") +CHROMA_HOST = os.environ.get("CHROMA_HOST", "http://127.0.0.1") +CHROMA_PORT = os.environ.get("CHROMA_PORT", "8000") +CHROMA_COLLECTION = os.environ.get("CHROMA_COLLECTION", "openaiembeddings") + + +class ChromaDataStore(DataStore): + def __init__( + self, + in_memory: bool = CHROMA_IN_MEMORY, # type: ignore + persistence_dir: Optional[str] = CHROMA_PERSISTENCE_DIR, + collection_name: str = CHROMA_COLLECTION, + host: str = CHROMA_HOST, + port: str = CHROMA_PORT, + client: Optional[chromadb.Client] = None, + ): + if client: + self._client = client + else: + if in_memory: + settings = ( + chromadb.config.Settings( + chroma_db_impl="duckdb+parquet", + persist_directory=persistence_dir, + ) + if persistence_dir + else chromadb.config.Settings() + ) + + self._client = chromadb.Client(settings=settings) + else: + self._client = chromadb.Client( + settings=chromadb.config.Settings( + chroma_api_impl="rest", + chroma_server_host=host, + chroma_server_http_port=port, + ) + ) + self._collection = self._client.get_or_create_collection( + name=collection_name, + embedding_function=None, + ) + + async def upsert( + self, documents: List[Document], chunk_token_size: Optional[int] = None + ) -> List[str]: + """ + Takes in a list of documents and inserts them into the database. If an id already exists, the document is updated. + Return a list of document ids. + """ + + chunks = get_document_chunks(documents, chunk_token_size) + + # Chroma has a true upsert, so we don't need to delete first + return await self._upsert(chunks) + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of list of document chunks and inserts them into the database. + Return a list of document ids. + """ + + self._collection.upsert( + ids=[chunk.id for chunk_list in chunks.values() for chunk in chunk_list], + embeddings=[ + chunk.embedding + for chunk_list in chunks.values() + for chunk in chunk_list + ], + documents=[ + chunk.text for chunk_list in chunks.values() for chunk in chunk_list + ], + metadatas=[ + self._process_metadata_for_storage(chunk.metadata) + for chunk_list in chunks.values() + for chunk in chunk_list + ], + ) + return list(chunks.keys()) + + def _where_from_query_filter(self, query_filter: DocumentMetadataFilter) -> Dict: + output = { + k: v + for (k, v) in query_filter.dict().items() + if v is not None and k != "start_date" and k != "end_date" and k != "source" + } + if query_filter.source: + output["source"] = query_filter.source.value + if query_filter.start_date and query_filter.end_date: + output["$and"] = [ + { + "created_at": { + "$gte": int( + datetime.fromisoformat(query_filter.start_date).timestamp() + ) + } + }, + { + "created_at": { + "$lte": int( + datetime.fromisoformat(query_filter.end_date).timestamp() + ) + } + }, + ] + elif query_filter.start_date: + output["created_at"] = { + "$gte": int(datetime.fromisoformat(query_filter.start_date).timestamp()) + } + elif query_filter.end_date: + output["created_at"] = { + "$lte": int(datetime.fromisoformat(query_filter.end_date).timestamp()) + } + + return output + + def _process_metadata_for_storage(self, metadata: DocumentChunkMetadata) -> Dict: + stored_metadata = {} + if metadata.source: + stored_metadata["source"] = metadata.source.value + if metadata.source_id: + stored_metadata["source_id"] = metadata.source_id + if metadata.url: + stored_metadata["url"] = metadata.url + if metadata.created_at: + stored_metadata["created_at"] = int( + datetime.fromisoformat(metadata.created_at).timestamp() + ) + if metadata.author: + stored_metadata["author"] = metadata.author + if metadata.document_id: + stored_metadata["document_id"] = metadata.document_id + + return stored_metadata + + def _process_metadata_from_storage(self, metadata: Dict) -> DocumentChunkMetadata: + return DocumentChunkMetadata( + source=Source(metadata["source"]) if "source" in metadata else None, + source_id=metadata.get("source_id", None), + url=metadata.get("url", None), + created_at=datetime.fromtimestamp(metadata["created_at"]).isoformat() + if "created_at" in metadata + else None, + author=metadata.get("author", None), + document_id=metadata.get("document_id", None), + ) + + async def _query(self, queries: List[QueryWithEmbedding]) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + results = [ + self._collection.query( + query_embeddings=[query.embedding], + include=["documents", "distances", "metadatas"], # embeddings + n_results=min(query.top_k, self._collection.count()), # type: ignore + where=( + self._where_from_query_filter(query.filter) if query.filter else {} + ), + ) + for query in queries + ] + + output = [] + for query, result in zip(queries, results): + inner_results = [] + (ids,) = result["ids"] + # (embeddings,) = result["embeddings"] + (documents,) = result["documents"] + (metadatas,) = result["metadatas"] + (distances,) = result["distances"] + for id_, text, metadata, distance in zip( + ids, + documents, + metadatas, + distances, # embeddings (https://github.com/openai/chatgpt-retrieval-plugin/pull/59#discussion_r1154985153) + ): + inner_results.append( + DocumentChunkWithScore( + id=id_, + text=text, + metadata=self._process_metadata_from_storage(metadata), + # embedding=embedding, + score=distance, + ) + ) + output.append(QueryResult(query=query.query, results=inner_results)) + + return output + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Multiple parameters can be used at once. + Returns whether the operation was successful. + """ + if delete_all: + self._collection.delete() + return True + + if ids and len(ids) > 0: + if len(ids) > 1: + where_clause = {"$or": [{"document_id": id_} for id_ in ids]} + else: + (id_,) = ids + where_clause = {"document_id": id_} + + if filter: + where_clause = { + "$and": [self._where_from_query_filter(filter), where_clause] + } + elif filter: + where_clause = self._where_from_query_filter(filter) + + self._collection.delete(where=where_clause) + return True diff --git a/retrieval/datastore/providers/llama_datastore.py b/retrieval/datastore/providers/llama_datastore.py new file mode 100644 index 0000000..9a07136 --- /dev/null +++ b/retrieval/datastore/providers/llama_datastore.py @@ -0,0 +1,181 @@ +import json +import os +from typing import Dict, List, Optional, Type +from loguru import logger +from datastore.datastore import DataStore +from models.models import DocumentChunk, DocumentChunkMetadata, DocumentChunkWithScore, DocumentMetadataFilter, Query, QueryResult, QueryWithEmbedding + +from llama_index.indices.base import BaseGPTIndex +from llama_index.indices.vector_store.base import GPTVectorStoreIndex +from llama_index.indices.query.schema import QueryBundle +from llama_index.response.schema import Response +from llama_index.data_structs.node_v2 import Node, DocumentRelationship, NodeWithScore +from llama_index.indices.registry import INDEX_STRUCT_TYPE_TO_INDEX_CLASS +from llama_index.data_structs.struct_type import IndexStructType +from llama_index.indices.response.builder import ResponseMode + +INDEX_STRUCT_TYPE_STR = os.environ.get('LLAMA_INDEX_TYPE', IndexStructType.SIMPLE_DICT.value) +INDEX_JSON_PATH = os.environ.get('LLAMA_INDEX_JSON_PATH', None) +QUERY_KWARGS_JSON_PATH = os.environ.get('LLAMA_QUERY_KWARGS_JSON_PATH', None) +RESPONSE_MODE = os.environ.get('LLAMA_RESPONSE_MODE', ResponseMode.NO_TEXT.value) + +EXTERNAL_VECTOR_STORE_INDEX_STRUCT_TYPES = [ + IndexStructType.DICT, + IndexStructType.WEAVIATE, + IndexStructType.PINECONE, + IndexStructType.QDRANT, + IndexStructType.CHROMA, + IndexStructType.VECTOR_STORE, +] + +def _create_or_load_index( + index_type_str: Optional[str] = None, + index_json_path: Optional[str] = None, + index_type_to_index_cls: Optional[dict[str, Type[BaseGPTIndex]]] = None, +) -> BaseGPTIndex: + """Create or load index from json path.""" + index_json_path = index_json_path or INDEX_JSON_PATH + index_type_to_index_cls = index_type_to_index_cls or INDEX_STRUCT_TYPE_TO_INDEX_CLASS + index_type_str = index_type_str or INDEX_STRUCT_TYPE_STR + index_type = IndexStructType(index_type_str) + + if index_type not in index_type_to_index_cls: + raise ValueError(f'Unknown index type: {index_type}') + + if index_type in EXTERNAL_VECTOR_STORE_INDEX_STRUCT_TYPES: + raise ValueError('Please use vector store directly.') + + index_cls = index_type_to_index_cls[index_type] + if index_json_path is None: + return index_cls(nodes=[]) # Create empty index + else: + return index_cls.load_from_disk(index_json_path) # Load index from disk + +def _create_or_load_query_kwargs(query_kwargs_json_path: Optional[str] = None) -> Optional[dict]: + """Create or load query kwargs from json path.""" + query_kwargs_json_path= query_kwargs_json_path or QUERY_KWARGS_JSON_PATH + query_kargs: Optional[dict] = None + if query_kwargs_json_path is not None: + with open(INDEX_JSON_PATH, 'r') as f: + query_kargs = json.load(f) + return query_kargs + + +def _doc_chunk_to_node(doc_chunk: DocumentChunk, source_doc_id: str) -> Node: + """Convert document chunk to Node""" + return Node( + doc_id=doc_chunk.id, + text=doc_chunk.text, + embedding=doc_chunk.embedding, + extra_info=doc_chunk.metadata.dict(), + relationships={ + DocumentRelationship.SOURCE: source_doc_id + } + ) + +def _query_with_embedding_to_query_bundle(query: QueryWithEmbedding) -> QueryBundle: + return QueryBundle( + query_str = query.query, + embedding=query.embedding, + ) + +def _source_node_to_doc_chunk_with_score(node_with_score: NodeWithScore) -> DocumentChunkWithScore: + node = node_with_score.node + if node.extra_info is not None: + metadata = DocumentChunkMetadata(**node.extra_info) + else: + metadata = DocumentChunkMetadata() + + return DocumentChunkWithScore( + id=node.doc_id, + text=node.text, + score=node_with_score.score if node_with_score.score is not None else 1., + metadata=metadata, + ) + +def _response_to_query_result(response: Response, query: QueryWithEmbedding) -> QueryResult: + results = [_source_node_to_doc_chunk_with_score(node) for node in response.source_nodes] + return QueryResult(query=query.query, results=results,) + +class LlamaDataStore(DataStore): + def __init__(self, index: Optional[BaseGPTIndex] = None, query_kwargs: Optional[dict] = None): + self._index = index or _create_or_load_index() + self._query_kwargs = query_kwargs or _create_or_load_query_kwargs() + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of list of document chunks and inserts them into the database. + Return a list of document ids. + """ + doc_ids = [] + for doc_id, doc_chunks in chunks.items(): + logger.debug(f"Upserting {doc_id} with {len(doc_chunks)} chunks") + + nodes = [ + _doc_chunk_to_node(doc_chunk=doc_chunk, source_doc_id=doc_id) + for doc_chunk in doc_chunks + ] + + self._index.insert_nodes(nodes) + doc_ids.append(doc_id) + return doc_ids + + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and + returns a list of query results with matching document chunks and scores. + """ + query_result_all = [] + for query in queries: + if query.filter is not None: + logger.warning('Filters are not supported yet, ignoring for now.') + + query_bundle = _query_with_embedding_to_query_bundle(query) + + # Setup query kwargs + if self._query_kwargs is not None: + query_kwargs = self._query_kwargs + else: + query_kwargs = {} + # TODO: support top_k for other indices + if isinstance(self._index, GPTVectorStoreIndex): + query_kwargs['similarity_top_k'] = query.top_k + + response = await self._index.aquery(query_bundle, response_mode=RESPONSE_MODE, **query_kwargs) + + query_result = _response_to_query_result(response, query) + query_result_all.append(query_result) + + return query_result_all + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Returns whether the operation was successful. + """ + if delete_all: + logger.warning('Delete all not supported yet.') + return False + + if filter is not None: + logger.warning('Filters are not supported yet.') + return False + + if ids is not None: + for id_ in ids: + try: + self._index.delete(id_) + except NotImplementedError: + # NOTE: some indices does not support delete yet. + logger.warning(f'{type(self._index)} does not support delete yet.') + return False + + return True \ No newline at end of file diff --git a/retrieval/datastore/providers/milvus_datastore.py b/retrieval/datastore/providers/milvus_datastore.py new file mode 100644 index 0000000..d105cc4 --- /dev/null +++ b/retrieval/datastore/providers/milvus_datastore.py @@ -0,0 +1,560 @@ +import json +import os +import asyncio + +from loguru import logger +from typing import Dict, List, Optional +from pymilvus import ( + Collection, + connections, + utility, + FieldSchema, + DataType, + CollectionSchema, + MilvusException, +) +from uuid import uuid4 + + +from services.date import to_unix_timestamp +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + Source, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + DocumentChunkWithScore, +) + +MILVUS_COLLECTION = os.environ.get("MILVUS_COLLECTION") or "c" + uuid4().hex +MILVUS_HOST = os.environ.get("MILVUS_HOST") or "localhost" +MILVUS_PORT = os.environ.get("MILVUS_PORT") or 19530 +MILVUS_USER = os.environ.get("MILVUS_USER") +MILVUS_PASSWORD = os.environ.get("MILVUS_PASSWORD") +MILVUS_USE_SECURITY = False if MILVUS_PASSWORD is None else True + +MILVUS_INDEX_PARAMS = os.environ.get("MILVUS_INDEX_PARAMS") +MILVUS_SEARCH_PARAMS = os.environ.get("MILVUS_SEARCH_PARAMS") +MILVUS_CONSISTENCY_LEVEL = os.environ.get("MILVUS_CONSISTENCY_LEVEL") + +UPSERT_BATCH_SIZE = 100 +OUTPUT_DIM = 1536 +EMBEDDING_FIELD = "embedding" + + +class Required: + pass + +# The fields names that we are going to be storing within Milvus, the field declaration for schema creation, and the default value +SCHEMA_V1 = [ + ( + "pk", + FieldSchema(name="pk", dtype=DataType.INT64, is_primary=True, auto_id=True), + Required, + ), + ( + EMBEDDING_FIELD, + FieldSchema(name=EMBEDDING_FIELD, dtype=DataType.FLOAT_VECTOR, dim=OUTPUT_DIM), + Required, + ), + ( + "text", + FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535), + Required, + ), + ( + "document_id", + FieldSchema(name="document_id", dtype=DataType.VARCHAR, max_length=65535), + "", + ), + ( + "source_id", + FieldSchema(name="source_id", dtype=DataType.VARCHAR, max_length=65535), + "", + ), + ( + "id", + FieldSchema( + name="id", + dtype=DataType.VARCHAR, + max_length=65535, + ), + "", + ), + ( + "source", + FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=65535), + "", + ), + ("url", FieldSchema(name="url", dtype=DataType.VARCHAR, max_length=65535), ""), + ("created_at", FieldSchema(name="created_at", dtype=DataType.INT64), -1), + ( + "author", + FieldSchema(name="author", dtype=DataType.VARCHAR, max_length=65535), + "", + ), +] + +# V2 schema, remomve the "pk" field +SCHEMA_V2 = SCHEMA_V1[1:] +SCHEMA_V2[4][1].is_primary = True + + +class MilvusDataStore(DataStore): + def __init__( + self, + create_new: Optional[bool] = False, + consistency_level: str = "Bounded", + ): + """Create a Milvus DataStore. + + The Milvus Datastore allows for storing your indexes and metadata within a Milvus instance. + + Args: + create_new (Optional[bool], optional): Whether to overwrite if collection already exists. Defaults to True. + consistency_level(str, optional): Specify the collection consistency level. + Defaults to "Bounded" for search performance. + Set to "Strong" in test cases for result validation. + """ + # Overwrite the default consistency level by MILVUS_CONSISTENCY_LEVEL + self._consistency_level = MILVUS_CONSISTENCY_LEVEL or consistency_level + self._create_connection() + + self._create_collection(MILVUS_COLLECTION, create_new) # type: ignore + self._create_index() + + def _get_schema(self): + return SCHEMA_V1 if self._schema_ver == "V1" else SCHEMA_V2 + + def _create_connection(self): + try: + self.alias = "" + # Check if the connection already exists + for x in connections.list_connections(): + addr = connections.get_connection_addr(x[0]) + if x[1] and ('address' in addr) and (addr['address'] == "{}:{}".format(MILVUS_HOST, MILVUS_PORT)): + self.alias = x[0] + logger.info("Reuse connection to Milvus server '{}:{}' with alias '{:s}'" + .format(MILVUS_HOST, MILVUS_PORT, self.alias)) + break + + # Connect to the Milvus instance using the passed in Environment variables + if len(self.alias) == 0: + self.alias = uuid4().hex + connections.connect( + alias=self.alias, + host=MILVUS_HOST, + port=MILVUS_PORT, + user=MILVUS_USER, # type: ignore + password=MILVUS_PASSWORD, # type: ignore + secure=MILVUS_USE_SECURITY, + ) + logger.info("Create connection to Milvus server '{}:{}' with alias '{:s}'" + .format(MILVUS_HOST, MILVUS_PORT, self.alias)) + except Exception as e: + logger.error("Failed to create connection to Milvus server '{}:{}', error: {}" + .format(MILVUS_HOST, MILVUS_PORT, e)) + + def _create_collection(self, collection_name, create_new: bool) -> None: + """Create a collection based on environment and passed in variables. + + Args: + create_new (bool): Whether to overwrite if collection already exists. + """ + try: + self._schema_ver = "V1" + # If the collection exists and create_new is True, drop the existing collection + if utility.has_collection(collection_name, using=self.alias) and create_new: + utility.drop_collection(collection_name, using=self.alias) + + # Check if the collection doesnt exist + if utility.has_collection(collection_name, using=self.alias) is False: + # If it doesnt exist use the field params from init to create a new schem + schema = [field[1] for field in SCHEMA_V2] + schema = CollectionSchema(schema) + # Use the schema to create a new collection + self.col = Collection( + collection_name, + schema=schema, + using=self.alias, + consistency_level=self._consistency_level, + ) + self._schema_ver = "V2" + logger.info("Create Milvus collection '{}' with schema {} and consistency level {}" + .format(collection_name, self._schema_ver, self._consistency_level)) + else: + # If the collection exists, point to it + self.col = Collection( + collection_name, using=self.alias + ) # type: ignore + # Which sechma is used + for field in self.col.schema.fields: + if field.name == "id" and field.is_primary: + self._schema_ver = "V2" + break + logger.info("Milvus collection '{}' already exists with schema {}" + .format(collection_name, self._schema_ver)) + except Exception as e: + logger.error("Failed to create collection '{}', error: {}".format(collection_name, e)) + + def _create_index(self): + # TODO: verify index/search params passed by os.environ + self.index_params = MILVUS_INDEX_PARAMS or None + self.search_params = MILVUS_SEARCH_PARAMS or None + try: + # If no index on the collection, create one + if len(self.col.indexes) == 0: + if self.index_params is not None: + # Convert the string format to JSON format parameters passed by MILVUS_INDEX_PARAMS + self.index_params = json.loads(self.index_params) + logger.info("Create Milvus index: {}".format(self.index_params)) + # Create an index on the 'embedding' field with the index params found in init + self.col.create_index(EMBEDDING_FIELD, index_params=self.index_params) + else: + # If no index param supplied, to first create an HNSW index for Milvus + try: + i_p = { + "metric_type": "IP", + "index_type": "HNSW", + "params": {"M": 8, "efConstruction": 64}, + } + logger.info("Attempting creation of Milvus '{}' index".format(i_p["index_type"])) + self.col.create_index(EMBEDDING_FIELD, index_params=i_p) + self.index_params = i_p + logger.info("Creation of Milvus '{}' index successful".format(i_p["index_type"])) + # If create fails, most likely due to being Zilliz Cloud instance, try to create an AutoIndex + except MilvusException: + logger.info("Attempting creation of Milvus default index") + i_p = {"metric_type": "IP", "index_type": "AUTOINDEX", "params": {}} + self.col.create_index(EMBEDDING_FIELD, index_params=i_p) + self.index_params = i_p + logger.info("Creation of Milvus default index successful") + # If an index already exists, grab its params + else: + # How about if the first index is not vector index? + for index in self.col.indexes: + idx = index.to_dict() + if idx["field"] == EMBEDDING_FIELD: + logger.info("Index already exists: {}".format(idx)) + self.index_params = idx['index_param'] + break + + self.col.load() + + if self.search_params is not None: + # Convert the string format to JSON format parameters passed by MILVUS_SEARCH_PARAMS + self.search_params = json.loads(self.search_params) + else: + # The default search params + metric_type = "IP" + if "metric_type" in self.index_params: + metric_type = self.index_params["metric_type"] + default_search_params = { + "IVF_FLAT": {"metric_type": metric_type, "params": {"nprobe": 10}}, + "IVF_SQ8": {"metric_type": metric_type, "params": {"nprobe": 10}}, + "IVF_PQ": {"metric_type": metric_type, "params": {"nprobe": 10}}, + "HNSW": {"metric_type": metric_type, "params": {"ef": 10}}, + "RHNSW_FLAT": {"metric_type": metric_type, "params": {"ef": 10}}, + "RHNSW_SQ": {"metric_type": metric_type, "params": {"ef": 10}}, + "RHNSW_PQ": {"metric_type": metric_type, "params": {"ef": 10}}, + "IVF_HNSW": {"metric_type": metric_type, "params": {"nprobe": 10, "ef": 10}}, + "ANNOY": {"metric_type": metric_type, "params": {"search_k": 10}}, + "AUTOINDEX": {"metric_type": metric_type, "params": {}}, + } + # Set the search params + self.search_params = default_search_params[self.index_params["index_type"]] + logger.info("Milvus search parameters: {}".format(self.search_params)) + except Exception as e: + logger.error("Failed to create index, error: {}".format(e)) + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """Upsert chunks into the datastore. + + Args: + chunks (Dict[str, List[DocumentChunk]]): A list of DocumentChunks to insert + + Raises: + e: Error in upserting data. + + Returns: + List[str]: The document_id's that were inserted. + """ + try: + # The doc id's to return for the upsert + doc_ids: List[str] = [] + # List to collect all the insert data, skip the "pk" for schema V1 + offset = 1 if self._schema_ver == "V1" else 0 + insert_data = [[] for _ in range(len(self._get_schema()) - offset)] + + # Go through each document chunklist and grab the data + for doc_id, chunk_list in chunks.items(): + # Append the doc_id to the list we are returning + doc_ids.append(doc_id) + # Examine each chunk in the chunklist + for chunk in chunk_list: + # Extract data from the chunk + list_of_data = self._get_values(chunk) + # Check if the data is valid + if list_of_data is not None: + # Append each field to the insert_data + for x in range(len(insert_data)): + insert_data[x].append(list_of_data[x]) + # Slice up our insert data into batches + batches = [ + insert_data[i : i + UPSERT_BATCH_SIZE] + for i in range(0, len(insert_data), UPSERT_BATCH_SIZE) + ] + + # Attempt to insert each batch into our collection + # batch data can work with both V1 and V2 schema + for batch in batches: + if len(batch[0]) != 0: + try: + logger.info(f"Upserting batch of size {len(batch[0])}") + self.col.insert(batch) + logger.info(f"Upserted batch successfully") + except Exception as e: + logger.error(f"Failed to insert batch records, error: {e}") + raise e + + # This setting perfoms flushes after insert. Small insert == bad to use + # self.col.flush() + return doc_ids + except Exception as e: + logger.error("Failed to insert records, error: {}".format(e)) + return [] + + + def _get_values(self, chunk: DocumentChunk) -> List[any] | None: # type: ignore + """Convert the chunk into a list of values to insert whose indexes align with fields. + + Args: + chunk (DocumentChunk): The chunk to convert. + + Returns: + List (any): The values to insert. + """ + # Convert DocumentChunk and its sub models to dict + values = chunk.dict() + # Unpack the metadata into the same dict + meta = values.pop("metadata") + values.update(meta) + + # Convert date to int timestamp form + if values["created_at"]: + values["created_at"] = to_unix_timestamp(values["created_at"]) + + # If source exists, change from Source object to the string value it holds + if values["source"]: + values["source"] = values["source"].value + # List to collect data we will return + ret = [] + # Grab data responding to each field, excluding the hidden auto pk field for schema V1 + offset = 1 if self._schema_ver == "V1" else 0 + for key, _, default in self._get_schema()[offset:]: + # Grab the data at the key and default to our defaults set in init + x = values.get(key) or default + # If one of our required fields is missing, ignore the entire entry + if x is Required: + logger.info("Chunk " + values["id"] + " missing " + key + " skipping") + return None + # Add the corresponding value if it passes the tests + ret.append(x) + return ret + + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """Query the QueryWithEmbedding against the MilvusDocumentSearch + + Search the embedding and its filter in the collection. + + Args: + queries (List[QueryWithEmbedding]): The list of searches to perform. + + Returns: + List[QueryResult]: Results for each search. + """ + # Async to perform the query, adapted from pinecone implementation + async def _single_query(query: QueryWithEmbedding) -> QueryResult: + try: + filter = None + # Set the filter to expression that is valid for Milvus + if query.filter is not None: + # Either a valid filter or None will be returned + filter = self._get_filter(query.filter) + + # Perform our search + return_from = 2 if self._schema_ver == "V1" else 1 + res = self.col.search( + data=[query.embedding], + anns_field=EMBEDDING_FIELD, + param=self.search_params, + limit=query.top_k, + expr=filter, + output_fields=[ + field[0] for field in self._get_schema()[return_from:] + ], # Ignoring pk, embedding + ) + # Results that will hold our DocumentChunkWithScores + results = [] + # Parse every result for our search + for hit in res[0]: # type: ignore + # The distance score for the search result, falls under DocumentChunkWithScore + score = hit.score + # Our metadata info, falls under DocumentChunkMetadata + metadata = {} + # Grab the values that correspond to our fields, ignore pk and embedding. + for x in [field[0] for field in self._get_schema()[return_from:]]: + metadata[x] = hit.entity.get(x) + # If the source isn't valid, convert to None + if metadata["source"] not in Source.__members__: + metadata["source"] = None + # Text falls under the DocumentChunk + text = metadata.pop("text") + # Id falls under the DocumentChunk + ids = metadata.pop("id") + chunk = DocumentChunkWithScore( + id=ids, + score=score, + text=text, + metadata=DocumentChunkMetadata(**metadata), + ) + results.append(chunk) + + # TODO: decide on doing queries to grab the embedding itself, slows down performance as double query occurs + + return QueryResult(query=query.query, results=results) + except Exception as e: + logger.error("Failed to query, error: {}".format(e)) + return QueryResult(query=query.query, results=[]) + + results: List[QueryResult] = await asyncio.gather( + *[_single_query(query) for query in queries] + ) + return results + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """Delete the entities based either on the chunk_id of the vector, + + Args: + ids (Optional[List[str]], optional): The document_ids to delete. Defaults to None. + filter (Optional[DocumentMetadataFilter], optional): The filter to delete by. Defaults to None. + delete_all (Optional[bool], optional): Whether to drop the collection and recreate it. Defaults to None. + """ + # If deleting all, drop and create the new collection + if delete_all: + coll_name = self.col.name + logger.info("Delete the entire collection {} and create new one".format(coll_name)) + # Release the collection from memory + self.col.release() + # Drop the collection + self.col.drop() + # Recreate the new collection + self._create_collection(coll_name, True) + self._create_index() + return True + + # Keep track of how many we have deleted for later printing + delete_count = 0 + batch_size = 100 + pk_name = "pk" if self._schema_ver == "V1" else "id" + try: + # According to the api design, the ids is a list of document_id, + # document_id is not primary key, use query+delete to workaround, + # in future version we can delete by expression + if (ids is not None) and len(ids) > 0: + # Add quotation marks around the string format id + ids = ['"' + str(id) + '"' for id in ids] + # Query for the pk's of entries that match id's + ids = self.col.query(f"document_id in [{','.join(ids)}]") + # Convert to list of pks + pks = [str(entry[pk_name]) for entry in ids] # type: ignore + # for schema V2, the "id" is varchar, rewrite the expression + if self._schema_ver != "V1": + pks = ['"' + pk + '"' for pk in pks] + + # Delete by ids batch by batch(avoid too long expression) + logger.info("Apply {:d} deletions to schema {:s}".format(len(pks), self._schema_ver)) + while len(pks) > 0: + batch_pks = pks[:batch_size] + pks = pks[batch_size:] + # Delete the entries batch by batch + res = self.col.delete(f"{pk_name} in [{','.join(batch_pks)}]") + # Increment our deleted count + delete_count += int(res.delete_count) # type: ignore + except Exception as e: + logger.error("Failed to delete by ids, error: {}".format(e)) + + try: + # Check if empty filter + if filter is not None: + # Convert filter to milvus expression + filter = self._get_filter(filter) # type: ignore + # Check if there is anything to filter + if len(filter) != 0: # type: ignore + # Query for the pk's of entries that match filter + res = self.col.query(filter) # type: ignore + # Convert to list of pks + pks = [str(entry[pk_name]) for entry in res] # type: ignore + # for schema V2, the "id" is varchar, rewrite the expression + if self._schema_ver != "V1": + pks = ['"' + pk + '"' for pk in pks] + # Check to see if there are valid pk's to delete, delete batch by batch(avoid too long expression) + while len(pks) > 0: # type: ignore + batch_pks = pks[:batch_size] + pks = pks[batch_size:] + # Delete the entries batch by batch + res = self.col.delete(f"{pk_name} in [{','.join(batch_pks)}]") # type: ignore + # Increment our delete count + delete_count += int(res.delete_count) # type: ignore + except Exception as e: + logger.error("Failed to delete by filter, error: {}".format(e)) + + logger.info("{:d} records deleted".format(delete_count)) + + # This setting performs flushes after delete. Small delete == bad to use + # self.col.flush() + + return True + + def _get_filter(self, filter: DocumentMetadataFilter) -> Optional[str]: + """Converts a DocumentMetdataFilter to the expression that Milvus takes. + + Args: + filter (DocumentMetadataFilter): The Filter to convert to Milvus expression. + + Returns: + Optional[str]: The filter if valid, otherwise None. + """ + filters = [] + # Go through all the fields and their values + for field, value in filter.dict().items(): + # Check if the Value is empty + if value is not None: + # Convert start_date to int and add greater than or equal logic + if field == "start_date": + filters.append( + "(created_at >= " + str(to_unix_timestamp(value)) + ")" + ) + # Convert end_date to int and add less than or equal logic + elif field == "end_date": + filters.append( + "(created_at <= " + str(to_unix_timestamp(value)) + ")" + ) + # Convert Source to its string value and check equivalency + elif field == "source": + filters.append("(" + field + ' == "' + str(value.value) + '")') + # Check equivalency of rest of string fields + else: + filters.append("(" + field + ' == "' + str(value) + '")') + # Join all our expressions with `and`` + return " and ".join(filters) diff --git a/retrieval/datastore/providers/pgvector_datastore.py b/retrieval/datastore/providers/pgvector_datastore.py new file mode 100644 index 0000000..cd7026b --- /dev/null +++ b/retrieval/datastore/providers/pgvector_datastore.py @@ -0,0 +1,181 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict, List, Optional +from datetime import datetime +from loguru import logger + +from services.date import to_unix_timestamp +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + DocumentChunkWithScore, +) + + +# interface for Postgres client to implement pg based Datastore providers +class PGClient(ABC): + @abstractmethod + async def upsert(self, table: str, json: dict[str, Any]) -> None: + """ + Takes in a list of documents and inserts them into the table. + """ + raise NotImplementedError + + @abstractmethod + async def rpc(self, function_name: str, params: dict[str, Any]) -> Any: + """ + Calls a stored procedure in the database with the given parameters. + """ + raise NotImplementedError + + @abstractmethod + async def delete_like(self, table: str, column: str, pattern: str) -> None: + """ + Deletes rows in the table that match the pattern. + """ + raise NotImplementedError + + @abstractmethod + async def delete_in(self, table: str, column: str, ids: List[str]) -> None: + """ + Deletes rows in the table that match the ids. + """ + raise NotImplementedError + + @abstractmethod + async def delete_by_filters( + self, table: str, filter: DocumentMetadataFilter + ) -> None: + """ + Deletes rows in the table that match the filter. + """ + raise NotImplementedError + + +# abstract class for Postgres based Datastore providers that implements DataStore interface +class PgVectorDataStore(DataStore): + def __init__(self): + self.client = self.create_db_client() + + @abstractmethod + def create_db_client(self) -> PGClient: + """ + Create db client, can be accessing postgres database via different APIs. + Can be supabase client or psycopg2 based client. + Return a client for postgres DB. + """ + + raise NotImplementedError + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a dict of document_ids to list of document chunks and inserts them into the database. + Return a list of document ids. + """ + for document_id, document_chunks in chunks.items(): + for chunk in document_chunks: + json = { + "id": chunk.id, + "content": chunk.text, + "embedding": chunk.embedding, + "document_id": document_id, + "source": chunk.metadata.source, + "source_id": chunk.metadata.source_id, + "url": chunk.metadata.url, + "author": chunk.metadata.author, + } + if chunk.metadata.created_at: + json["created_at"] = ( + datetime.fromtimestamp( + to_unix_timestamp(chunk.metadata.created_at) + ), + ) + await self.client.upsert("documents", json) + + return list(chunks.keys()) + + async def _query(self, queries: List[QueryWithEmbedding]) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + query_results: List[QueryResult] = [] + for query in queries: + # get the top 3 documents with the highest cosine similarity using rpc function in the database called "match_page_sections" + params = { + "in_embedding": query.embedding, + } + if query.top_k: + params["in_match_count"] = query.top_k + if query.filter: + if query.filter.document_id: + params["in_document_id"] = query.filter.document_id + if query.filter.source: + params["in_source"] = query.filter.source.value + if query.filter.source_id: + params["in_source_id"] = query.filter.source_id + if query.filter.author: + params["in_author"] = query.filter.author + if query.filter.start_date: + params["in_start_date"] = datetime.fromtimestamp( + to_unix_timestamp(query.filter.start_date) + ) + if query.filter.end_date: + params["in_end_date"] = datetime.fromtimestamp( + to_unix_timestamp(query.filter.end_date) + ) + try: + data = await self.client.rpc("match_page_sections", params=params) + results: List[DocumentChunkWithScore] = [] + for row in data: + document_chunk = DocumentChunkWithScore( + id=row["id"], + text=row["content"], + # TODO: add embedding to the response ? + # embedding=row["embedding"], + score=float(row["similarity"]), + metadata=DocumentChunkMetadata( + source=row["source"], + source_id=row["source_id"], + document_id=row["document_id"], + url=row["url"], + created_at=row["created_at"], + author=row["author"], + ), + ) + results.append(document_chunk) + query_results.append(QueryResult(query=query.query, results=results)) + except Exception as e: + logger.error(e) + query_results.append(QueryResult(query=query.query, results=[])) + return query_results + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Multiple parameters can be used at once. + Returns whether the operation was successful. + """ + if delete_all: + try: + await self.client.delete_like("documents", "document_id", "%") + except: + return False + elif ids: + try: + await self.client.delete_in("documents", "document_id", ids) + except: + return False + elif filter: + try: + await self.client.delete_by_filters("documents", filter) + except: + return False + return True diff --git a/retrieval/datastore/providers/pinecone_datastore.py b/retrieval/datastore/providers/pinecone_datastore.py new file mode 100644 index 0000000..c10ee2b --- /dev/null +++ b/retrieval/datastore/providers/pinecone_datastore.py @@ -0,0 +1,262 @@ +import os +from typing import Any, Dict, List, Optional +import pinecone +from tenacity import retry, wait_random_exponential, stop_after_attempt +import asyncio +from loguru import logger + +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentChunkWithScore, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + Source, +) +from services.date import to_unix_timestamp + +# Read environment variables for Pinecone configuration +PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY") +PINECONE_ENVIRONMENT = os.environ.get("PINECONE_ENVIRONMENT") +PINECONE_INDEX = os.environ.get("PINECONE_INDEX") +assert PINECONE_API_KEY is not None +assert PINECONE_ENVIRONMENT is not None +assert PINECONE_INDEX is not None + +# Initialize Pinecone with the API key and environment +pinecone.init(api_key=PINECONE_API_KEY, environment=PINECONE_ENVIRONMENT) + +# Set the batch size for upserting vectors to Pinecone +UPSERT_BATCH_SIZE = 100 + + +class PineconeDataStore(DataStore): + def __init__(self): + # Check if the index name is specified and exists in Pinecone + if PINECONE_INDEX and PINECONE_INDEX not in pinecone.list_indexes(): + + # Get all fields in the metadata object in a list + fields_to_index = list(DocumentChunkMetadata.__fields__.keys()) + + # Create a new index with the specified name, dimension, and metadata configuration + try: + logger.info( + f"Creating index {PINECONE_INDEX} with metadata config {fields_to_index}" + ) + pinecone.create_index( + PINECONE_INDEX, + dimension=1536, # dimensionality of OpenAI ada v2 embeddings + metadata_config={"indexed": fields_to_index}, + ) + self.index = pinecone.Index(PINECONE_INDEX) + logger.info(f"Index {PINECONE_INDEX} created successfully") + except Exception as e: + logger.error(f"Error creating index {PINECONE_INDEX}: {e}") + raise e + elif PINECONE_INDEX and PINECONE_INDEX in pinecone.list_indexes(): + # Connect to an existing index with the specified name + try: + logger.info(f"Connecting to existing index {PINECONE_INDEX}") + self.index = pinecone.Index(PINECONE_INDEX) + logger.info(f"Connected to index {PINECONE_INDEX} successfully") + except Exception as e: + logger.error(f"Error connecting to index {PINECONE_INDEX}: {e}") + raise e + + @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(3)) + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a dict from document id to list of document chunks and inserts them into the index. + Return a list of document ids. + """ + # Initialize a list of ids to return + doc_ids: List[str] = [] + # Initialize a list of vectors to upsert + vectors = [] + # Loop through the dict items + for doc_id, chunk_list in chunks.items(): + # Append the id to the ids list + doc_ids.append(doc_id) + logger.info(f"Upserting document_id: {doc_id}") + for chunk in chunk_list: + # Create a vector tuple of (id, embedding, metadata) + # Convert the metadata object to a dict with unix timestamps for dates + pinecone_metadata = self._get_pinecone_metadata(chunk.metadata) + # Add the text and document id to the metadata dict + pinecone_metadata["text"] = chunk.text + pinecone_metadata["document_id"] = doc_id + vector = (chunk.id, chunk.embedding, pinecone_metadata) + vectors.append(vector) + + # Split the vectors list into batches of the specified size + batches = [ + vectors[i : i + UPSERT_BATCH_SIZE] + for i in range(0, len(vectors), UPSERT_BATCH_SIZE) + ] + # Upsert each batch to Pinecone + for batch in batches: + try: + logger.info(f"Upserting batch of size {len(batch)}") + self.index.upsert(vectors=batch) + logger.info(f"Upserted batch successfully") + except Exception as e: + logger.error(f"Error upserting batch: {e}") + raise e + + return doc_ids + + @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(3)) + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + + # Define a helper coroutine that performs a single query and returns a QueryResult + async def _single_query(query: QueryWithEmbedding) -> QueryResult: + logger.debug(f"Query: {query.query}") + + # Convert the metadata filter object to a dict with pinecone filter expressions + pinecone_filter = self._get_pinecone_filter(query.filter) + + try: + # Query the index with the query embedding, filter, and top_k + query_response = self.index.query( + # namespace=namespace, + top_k=query.top_k, + vector=query.embedding, + filter=pinecone_filter, + include_metadata=True, + ) + except Exception as e: + logger.error(f"Error querying index: {e}") + raise e + + query_results: List[DocumentChunkWithScore] = [] + for result in query_response.matches: + score = result.score + metadata = result.metadata + # Remove document id and text from metadata and store it in a new variable + metadata_without_text = ( + {key: value for key, value in metadata.items() if key != "text"} + if metadata + else None + ) + + # If the source is not a valid Source in the Source enum, set it to None + if ( + metadata_without_text + and "source" in metadata_without_text + and metadata_without_text["source"] not in Source.__members__ + ): + metadata_without_text["source"] = None + + # Create a document chunk with score object with the result data + result = DocumentChunkWithScore( + id=result.id, + score=score, + text=metadata["text"] if metadata and "text" in metadata else None, + metadata=metadata_without_text, + ) + query_results.append(result) + return QueryResult(query=query.query, results=query_results) + + # Use asyncio.gather to run multiple _single_query coroutines concurrently and collect their results + results: List[QueryResult] = await asyncio.gather( + *[_single_query(query) for query in queries] + ) + + return results + + @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(3)) + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything from the index. + """ + # Delete all vectors from the index if delete_all is True + if delete_all: + try: + logger.info(f"Deleting all vectors from index") + self.index.delete(delete_all=True) + logger.info(f"Deleted all vectors successfully") + return True + except Exception as e: + logger.error(f"Error deleting all vectors: {e}") + raise e + + # Convert the metadata filter object to a dict with pinecone filter expressions + pinecone_filter = self._get_pinecone_filter(filter) + # Delete vectors that match the filter from the index if the filter is not empty + if pinecone_filter != {}: + try: + logger.info(f"Deleting vectors with filter {pinecone_filter}") + self.index.delete(filter=pinecone_filter) + logger.info(f"Deleted vectors with filter successfully") + except Exception as e: + logger.error(f"Error deleting vectors with filter: {e}") + raise e + + # Delete vectors that match the document ids from the index if the ids list is not empty + if ids is not None and len(ids) > 0: + try: + logger.info(f"Deleting vectors with ids {ids}") + pinecone_filter = {"document_id": {"$in": ids}} + self.index.delete(filter=pinecone_filter) # type: ignore + logger.info(f"Deleted vectors with ids successfully") + except Exception as e: + logger.error(f"Error deleting vectors with ids: {e}") + raise e + + return True + + def _get_pinecone_filter( + self, filter: Optional[DocumentMetadataFilter] = None + ) -> Dict[str, Any]: + if filter is None: + return {} + + pinecone_filter = {} + + # For each field in the MetadataFilter, check if it has a value and add the corresponding pinecone filter expression + # For start_date and end_date, uses the $gte and $lte operators respectively + # For other fields, uses the $eq operator + for field, value in filter.dict().items(): + if value is not None: + if field == "start_date": + pinecone_filter["date"] = pinecone_filter.get("date", {}) + pinecone_filter["date"]["$gte"] = to_unix_timestamp(value) + elif field == "end_date": + pinecone_filter["date"] = pinecone_filter.get("date", {}) + pinecone_filter["date"]["$lte"] = to_unix_timestamp(value) + else: + pinecone_filter[field] = value + + return pinecone_filter + + def _get_pinecone_metadata( + self, metadata: Optional[DocumentChunkMetadata] = None + ) -> Dict[str, Any]: + if metadata is None: + return {} + + pinecone_metadata = {} + + # For each field in the Metadata, check if it has a value and add it to the pinecone metadata dict + # For fields that are dates, convert them to unix timestamps + for field, value in metadata.dict().items(): + if value is not None: + if field in ["created_at"]: + pinecone_metadata[field] = to_unix_timestamp(value) + else: + pinecone_metadata[field] = value + + return pinecone_metadata diff --git a/retrieval/datastore/providers/postgres_datastore.py b/retrieval/datastore/providers/postgres_datastore.py new file mode 100644 index 0000000..402ad1b --- /dev/null +++ b/retrieval/datastore/providers/postgres_datastore.py @@ -0,0 +1,132 @@ +import os +from typing import Any, List +from datetime import datetime +import numpy as np + +from psycopg2 import connect +from psycopg2.extras import DictCursor +from pgvector.psycopg2 import register_vector + +from services.date import to_unix_timestamp +from datastore.providers.pgvector_datastore import PGClient, PgVectorDataStore +from models.models import ( + DocumentMetadataFilter, +) + +PG_HOST = os.environ.get("PG_HOST", "localhost") +PG_PORT = int(os.environ.get("PG_PORT", 5432)) +PG_DB = os.environ.get("PG_DB", "postgres") +PG_USER = os.environ.get("PG_USER", "postgres") +PG_PASSWORD = os.environ.get("PG_PASSWORD", "postgres") + + +# class that implements the DataStore interface for Postgres Datastore provider +class PostgresDataStore(PgVectorDataStore): + def create_db_client(self): + return PostgresClient() + + +class PostgresClient(PGClient): + def __init__(self) -> None: + super().__init__() + self.client = connect( + dbname=PG_DB, user=PG_USER, password=PG_PASSWORD, host=PG_HOST, port=PG_PORT + ) + register_vector(self.client) + + def __del__(self): + # close the connection when the client is destroyed + self.client.close() + + async def upsert(self, table: str, json: dict[str, Any]): + """ + Takes in a list of documents and inserts them into the table. + """ + with self.client.cursor() as cur: + if not json.get("created_at"): + json["created_at"] = datetime.now() + json["embedding"] = np.array(json["embedding"]) + cur.execute( + f"INSERT INTO {table} (id, content, embedding, document_id, source, source_id, url, author, created_at) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) ON CONFLICT (id) DO UPDATE SET content = %s, embedding = %s, document_id = %s, source = %s, source_id = %s, url = %s, author = %s, created_at = %s", + ( + json["id"], + json["content"], + json["embedding"], + json["document_id"], + json["source"], + json["source_id"], + json["url"], + json["author"], + json["created_at"], + json["content"], + json["embedding"], + json["document_id"], + json["source"], + json["source_id"], + json["url"], + json["author"], + json["created_at"], + ), + ) + self.client.commit() + + async def rpc(self, function_name: str, params: dict[str, Any]): + """ + Calls a stored procedure in the database with the given parameters. + """ + data = [] + params["in_embedding"] = np.array(params["in_embedding"]) + with self.client.cursor(cursor_factory=DictCursor) as cur: + cur.callproc(function_name, params) + rows = cur.fetchall() + self.client.commit() + for row in rows: + row["created_at"] = to_unix_timestamp(row["created_at"]) + data.append(dict(row)) + return data + + async def delete_like(self, table: str, column: str, pattern: str): + """ + Deletes rows in the table that match the pattern. + """ + with self.client.cursor() as cur: + cur.execute( + f"DELETE FROM {table} WHERE {column} LIKE %s", + (f"%{pattern}%",), + ) + self.client.commit() + + async def delete_in(self, table: str, column: str, ids: List[str]): + """ + Deletes rows in the table that match the ids. + """ + with self.client.cursor() as cur: + cur.execute( + f"DELETE FROM {table} WHERE {column} IN %s", + (tuple(ids),), + ) + self.client.commit() + + async def delete_by_filters(self, table: str, filter: DocumentMetadataFilter): + """ + Deletes rows in the table that match the filter. + """ + + filters = "WHERE" + if filter.document_id: + filters += f" document_id = '{filter.document_id}' AND" + if filter.source: + filters += f" source = '{filter.source}' AND" + if filter.source_id: + filters += f" source_id = '{filter.source_id}' AND" + if filter.author: + filters += f" author = '{filter.author}' AND" + if filter.start_date: + filters += f" created_at >= '{filter.start_date}' AND" + if filter.end_date: + filters += f" created_at <= '{filter.end_date}' AND" + filters = filters[:-4] + + with self.client.cursor() as cur: + cur.execute(f"DELETE FROM {table} {filters}") + self.client.commit() diff --git a/retrieval/datastore/providers/qdrant_datastore.py b/retrieval/datastore/providers/qdrant_datastore.py new file mode 100644 index 0000000..ffdfdfb --- /dev/null +++ b/retrieval/datastore/providers/qdrant_datastore.py @@ -0,0 +1,297 @@ +import os +import uuid +from typing import Dict, List, Optional + +from grpc._channel import _InactiveRpcError +from qdrant_client.http.exceptions import UnexpectedResponse +from qdrant_client.http.models import PayloadSchemaType + +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + DocumentChunkWithScore, +) +from qdrant_client.http import models as rest + +import qdrant_client + +from services.date import to_unix_timestamp + +QDRANT_URL = os.environ.get("QDRANT_URL", "http://localhost") +QDRANT_PORT = os.environ.get("QDRANT_PORT", "6333") +QDRANT_GRPC_PORT = os.environ.get("QDRANT_GRPC_PORT", "6334") +QDRANT_API_KEY = os.environ.get("QDRANT_API_KEY") +QDRANT_COLLECTION = os.environ.get("QDRANT_COLLECTION", "document_chunks") + + +class QdrantDataStore(DataStore): + UUID_NAMESPACE = uuid.UUID("3896d314-1e95-4a3a-b45a-945f9f0b541d") + + def __init__( + self, + collection_name: Optional[str] = None, + vector_size: int = 1536, + distance: str = "Cosine", + recreate_collection: bool = False, + ): + """ + Args: + collection_name: Name of the collection to be used + vector_size: Size of the embedding stored in a collection + distance: + Any of "Cosine" / "Euclid" / "Dot". Distance function to measure + similarity + """ + self.client = qdrant_client.QdrantClient( + url=QDRANT_URL, + port=int(QDRANT_PORT), + grpc_port=int(QDRANT_GRPC_PORT), + api_key=QDRANT_API_KEY, + prefer_grpc=True, + timeout=10, + ) + self.collection_name = collection_name or QDRANT_COLLECTION + + # Set up the collection so the points might be inserted or queried + self._set_up_collection(vector_size, distance, recreate_collection) + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of document chunks and inserts them into the database. + Return a list of document ids. + """ + points = [ + self._convert_document_chunk_to_point(chunk) + for _, chunks in chunks.items() + for chunk in chunks + ] + self.client.upsert( + collection_name=self.collection_name, + points=points, # type: ignore + wait=True, + ) + return list(chunks.keys()) + + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + search_requests = [ + self._convert_query_to_search_request(query) for query in queries + ] + results = self.client.search_batch( + collection_name=self.collection_name, + requests=search_requests, + ) + return [ + QueryResult( + query=query.query, + results=[ + self._convert_scored_point_to_document_chunk_with_score(point) + for point in result + ], + ) + for query, result in zip(queries, results) + ] + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Returns whether the operation was successful. + """ + if ids is None and filter is None and delete_all is None: + raise ValueError( + "Please provide one of the parameters: ids, filter or delete_all." + ) + + if delete_all: + points_selector = rest.Filter() + else: + points_selector = self._convert_metadata_filter_to_qdrant_filter( + filter, ids + ) + + response = self.client.delete( + collection_name=self.collection_name, + points_selector=points_selector, # type: ignore + ) + return "COMPLETED" == response.status + + def _convert_document_chunk_to_point( + self, document_chunk: DocumentChunk + ) -> rest.PointStruct: + created_at = ( + to_unix_timestamp(document_chunk.metadata.created_at) + if document_chunk.metadata.created_at is not None + else None + ) + return rest.PointStruct( + id=self._create_document_chunk_id(document_chunk.id), + vector=document_chunk.embedding, # type: ignore + payload={ + "id": document_chunk.id, + "text": document_chunk.text, + "metadata": document_chunk.metadata.dict(), + "created_at": created_at, + }, + ) + + def _create_document_chunk_id(self, external_id: Optional[str]) -> str: + if external_id is None: + return uuid.uuid4().hex + return uuid.uuid5(self.UUID_NAMESPACE, external_id).hex + + def _convert_query_to_search_request( + self, query: QueryWithEmbedding + ) -> rest.SearchRequest: + return rest.SearchRequest( + vector=query.embedding, + filter=self._convert_metadata_filter_to_qdrant_filter(query.filter), + limit=query.top_k, # type: ignore + with_payload=True, + with_vector=False, + ) + + def _convert_metadata_filter_to_qdrant_filter( + self, + metadata_filter: Optional[DocumentMetadataFilter] = None, + ids: Optional[List[str]] = None, + ) -> Optional[rest.Filter]: + if metadata_filter is None and ids is None: + return None + + must_conditions, should_conditions = [], [] + + # Filtering by document ids + if ids and len(ids) > 0: + for document_id in ids: + should_conditions.append( + rest.FieldCondition( + key="metadata.document_id", + match=rest.MatchValue(value=document_id), + ) + ) + + # Equality filters for the payload attributes + if metadata_filter: + meta_attributes_keys = { + "document_id": "metadata.document_id", + "source": "metadata.source", + "source_id": "metadata.source_id", + "author": "metadata.author", + } + + for meta_attr_name, payload_key in meta_attributes_keys.items(): + attr_value = getattr(metadata_filter, meta_attr_name) + if attr_value is None: + continue + + must_conditions.append( + rest.FieldCondition( + key=payload_key, match=rest.MatchValue(value=attr_value) + ) + ) + + # Date filters use range filtering + start_date = metadata_filter.start_date + end_date = metadata_filter.end_date + if start_date or end_date: + gte_filter = ( + to_unix_timestamp(start_date) if start_date is not None else None + ) + lte_filter = ( + to_unix_timestamp(end_date) if end_date is not None else None + ) + must_conditions.append( + rest.FieldCondition( + key="created_at", + range=rest.Range( + gte=gte_filter, + lte=lte_filter, + ), + ) + ) + + if 0 == len(must_conditions) and 0 == len(should_conditions): + return None + + return rest.Filter(must=must_conditions, should=should_conditions) + + def _convert_scored_point_to_document_chunk_with_score( + self, scored_point: rest.ScoredPoint + ) -> DocumentChunkWithScore: + payload = scored_point.payload or {} + return DocumentChunkWithScore( + id=payload.get("id"), + text=scored_point.payload.get("text"), # type: ignore + metadata=scored_point.payload.get("metadata"), # type: ignore + embedding=scored_point.vector, # type: ignore + score=scored_point.score, + ) + + def _set_up_collection( + self, vector_size: int, distance: str, recreate_collection: bool + ): + distance = rest.Distance[distance.upper()] + + if recreate_collection: + self._recreate_collection(distance, vector_size) + + try: + collection_info = self.client.get_collection(self.collection_name) + current_distance = collection_info.config.params.vectors.distance # type: ignore + current_vector_size = collection_info.config.params.vectors.size # type: ignore + + if current_distance != distance: + raise ValueError( + f"Collection '{self.collection_name}' already exists in Qdrant, " + f"but it is configured with a similarity '{current_distance.name}'. " + f"If you want to use that collection, but with a different " + f"similarity, please set `recreate_collection=True` argument." + ) + + if current_vector_size != vector_size: + raise ValueError( + f"Collection '{self.collection_name}' already exists in Qdrant, " + f"but it is configured with a vector size '{current_vector_size}'. " + f"If you want to use that collection, but with a different " + f"vector size, please set `recreate_collection=True` argument." + ) + except (UnexpectedResponse, _InactiveRpcError): + self._recreate_collection(distance, vector_size) + + def _recreate_collection(self, distance: rest.Distance, vector_size: int): + self.client.recreate_collection( + self.collection_name, + vectors_config=rest.VectorParams( + size=vector_size, + distance=distance, + ), + ) + + # Create the payload index for the document_id metadata attribute, as it is + # used to delete the document related entries + self.client.create_payload_index( + self.collection_name, + field_name="metadata.document_id", + field_type=PayloadSchemaType.KEYWORD, + ) + + # Create the payload index for the created_at attribute, to make the lookup + # by range filters faster + self.client.create_payload_index( + self.collection_name, + field_name="created_at", + field_schema=PayloadSchemaType.INTEGER, + ) diff --git a/retrieval/datastore/providers/redis_datastore.py b/retrieval/datastore/providers/redis_datastore.py new file mode 100644 index 0000000..da13348 --- /dev/null +++ b/retrieval/datastore/providers/redis_datastore.py @@ -0,0 +1,391 @@ +import asyncio +import os +import re +import json +import redis.asyncio as redis +import numpy as np + +from redis.commands.search.query import Query as RediSearchQuery +from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.field import ( + TagField, + TextField, + NumericField, + VectorField, +) +from loguru import logger +from typing import Dict, List, Optional +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentMetadataFilter, + DocumentChunkWithScore, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, +) +from services.date import to_unix_timestamp + +# Read environment variables for Redis +REDIS_HOST = os.environ.get("REDIS_HOST", "localhost") +REDIS_PORT = int(os.environ.get("REDIS_PORT", 6379)) +REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD") +REDIS_INDEX_NAME = os.environ.get("REDIS_INDEX_NAME", "index") +REDIS_DOC_PREFIX = os.environ.get("REDIS_DOC_PREFIX", "doc") +REDIS_DISTANCE_METRIC = os.environ.get("REDIS_DISTANCE_METRIC", "COSINE") +REDIS_INDEX_TYPE = os.environ.get("REDIS_INDEX_TYPE", "FLAT") +assert REDIS_INDEX_TYPE in ("FLAT", "HNSW") + +# OpenAI Ada Embeddings Dimension +VECTOR_DIMENSION = 1536 + +# RediSearch constants +REDIS_REQUIRED_MODULES = [ + {"name": "search", "ver": 20600}, + {"name": "ReJSON", "ver": 20404} +] + +REDIS_DEFAULT_ESCAPED_CHARS = re.compile(r"[,.<>{}\[\]\\\"\':;!@#$%^&()\-+=~\/ ]") + +# Helper functions +def unpack_schema(d: dict): + for v in d.values(): + if isinstance(v, dict): + yield from unpack_schema(v) + else: + yield v + +async def _check_redis_module_exist(client: redis.Redis, modules: List[dict]): + installed_modules = (await client.info()).get("modules", []) + installed_modules = {module["name"]: module for module in installed_modules} + for module in modules: + if module["name"] not in installed_modules or int(installed_modules[module["name"]]["ver"]) < int(module["ver"]): + error_message = "You must add the RediSearch (>= 2.6) and ReJSON (>= 2.4) modules from Redis Stack. " \ + "Please refer to Redis Stack docs: https://redis.io/docs/stack/" + logger.error(error_message) + raise AttributeError(error_message) + + +class RedisDataStore(DataStore): + def __init__(self, client: redis.Redis, redisearch_schema: dict): + self.client = client + self._schema = redisearch_schema + # Init default metadata with sentinel values in case the document written has no metadata + self._default_metadata = { + field: (0 if field == "created_at" else "_null_") for field in redisearch_schema["metadata"] + } + + ### Redis Helper Methods ### + + @classmethod + async def init(cls, **kwargs): + """ + Setup the index if it does not exist. + """ + try: + # Connect to the Redis Client + logger.info("Connecting to Redis") + client = redis.Redis( + host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD + ) + except Exception as e: + logger.error(f"Error setting up Redis: {e}") + raise e + + await _check_redis_module_exist(client, modules=REDIS_REQUIRED_MODULES) + + dim = kwargs.get("dim", VECTOR_DIMENSION) + redisearch_schema = { + "metadata": { + "document_id": TagField("$.metadata.document_id", as_name="document_id"), + "source_id": TagField("$.metadata.source_id", as_name="source_id"), + "source": TagField("$.metadata.source", as_name="source"), + "author": TextField("$.metadata.author", as_name="author"), + "created_at": NumericField("$.metadata.created_at", as_name="created_at"), + }, + "embedding": VectorField( + "$.embedding", + REDIS_INDEX_TYPE, + { + "TYPE": "FLOAT64", + "DIM": dim, + "DISTANCE_METRIC": REDIS_DISTANCE_METRIC, + }, + as_name="embedding", + ), + } + try: + # Check for existence of RediSearch Index + await client.ft(REDIS_INDEX_NAME).info() + logger.info(f"RediSearch index {REDIS_INDEX_NAME} already exists") + except: + # Create the RediSearch Index + logger.info(f"Creating new RediSearch index {REDIS_INDEX_NAME}") + definition = IndexDefinition( + prefix=[REDIS_DOC_PREFIX], index_type=IndexType.JSON + ) + fields = list(unpack_schema(redisearch_schema)) + logger.info(f"Creating index with fields: {fields}") + await client.ft(REDIS_INDEX_NAME).create_index( + fields=fields, definition=definition + ) + return cls(client, redisearch_schema) + + @staticmethod + def _redis_key(document_id: str, chunk_id: str) -> str: + """ + Create the JSON key for document chunks in Redis. + + Args: + document_id (str): Document Identifier + chunk_id (str): Chunk Identifier + + Returns: + str: JSON key string. + """ + return f"doc:{document_id}:chunk:{chunk_id}" + + @staticmethod + def _escape(value: str) -> str: + """ + Escape filter value. + + Args: + value (str): Value to escape. + + Returns: + str: Escaped filter value for RediSearch. + """ + + def escape_symbol(match) -> str: + value = match.group(0) + return f"\\{value}" + + return REDIS_DEFAULT_ESCAPED_CHARS.sub(escape_symbol, value) + + def _get_redis_chunk(self, chunk: DocumentChunk) -> dict: + """ + Convert DocumentChunk into a JSON object for storage + in Redis. + + Args: + chunk (DocumentChunk): Chunk of a Document. + + Returns: + dict: JSON object for storage in Redis. + """ + # Convert chunk -> dict + data = chunk.__dict__ + metadata = chunk.metadata.__dict__ + data["chunk_id"] = data.pop("id") + + # Prep Redis Metadata + redis_metadata = dict(self._default_metadata) + if metadata: + for field, value in metadata.items(): + if value: + if field == "created_at": + redis_metadata[field] = to_unix_timestamp(value) # type: ignore + else: + redis_metadata[field] = value + data["metadata"] = redis_metadata + return data + + def _get_redis_query(self, query: QueryWithEmbedding) -> RediSearchQuery: + """ + Convert a QueryWithEmbedding into a RediSearchQuery. + + Args: + query (QueryWithEmbedding): Search query. + + Returns: + RediSearchQuery: Query for RediSearch. + """ + filter_str: str = "" + + # RediSearch field type to query string + def _typ_to_str(typ, field, value) -> str: # type: ignore + if isinstance(typ, TagField): + return f"@{field}:{{{self._escape(value)}}} " + elif isinstance(typ, TextField): + return f"@{field}:{value} " + elif isinstance(typ, NumericField): + num = to_unix_timestamp(value) + match field: + case "start_date": + return f"@{field}:[{num} +inf] " + case "end_date": + return f"@{field}:[-inf {num}] " + + # Build filter + if query.filter: + redisearch_schema = self._schema + for field, value in query.filter.__dict__.items(): + if not value: + continue + if field in redisearch_schema: + filter_str += _typ_to_str(redisearch_schema[field], field, value) + elif field in redisearch_schema["metadata"]: + if field == "source": # handle the enum + value = value.value + filter_str += _typ_to_str( + redisearch_schema["metadata"][field], field, value + ) + elif field in ["start_date", "end_date"]: + filter_str += _typ_to_str( + redisearch_schema["metadata"]["created_at"], field, value + ) + + # Postprocess filter string + filter_str = filter_str.strip() + filter_str = filter_str if filter_str else "*" + + # Prepare query string + query_str = ( + f"({filter_str})=>[KNN {query.top_k} @embedding $embedding as score]" + ) + return ( + RediSearchQuery(query_str) + .sort_by("score") + .paging(0, query.top_k) + .dialect(2) + ) + + async def _redis_delete(self, keys: List[str]): + """ + Delete a list of keys from Redis. + + Args: + keys (List[str]): List of keys to delete. + """ + # Delete the keys + await asyncio.gather(*[self.client.delete(key) for key in keys]) + + ####### + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of list of document chunks and inserts them into the database. + Return a list of document ids. + """ + # Initialize a list of ids to return + doc_ids: List[str] = [] + + # Loop through the dict items + for doc_id, chunk_list in chunks.items(): + + # Append the id to the ids list + doc_ids.append(doc_id) + + # Write chunks in a pipelines + async with self.client.pipeline(transaction=False) as pipe: + for chunk in chunk_list: + key = self._redis_key(doc_id, chunk.id) + data = self._get_redis_chunk(chunk) + await pipe.json().set(key, "$", data) + await pipe.execute() + + return doc_ids + + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and + returns a list of query results with matching document chunks and scores. + """ + # Prepare query responses and results object + results: List[QueryResult] = [] + + # Gather query results in a pipeline + logger.info(f"Gathering {len(queries)} query results") + for query in queries: + + logger.debug(f"Query: {query.query}") + query_results: List[DocumentChunkWithScore] = [] + + # Extract Redis query + redis_query: RediSearchQuery = self._get_redis_query(query) + embedding = np.array(query.embedding, dtype=np.float64).tobytes() + + # Perform vector search + query_response = await self.client.ft(REDIS_INDEX_NAME).search( + redis_query, {"embedding": embedding} + ) + + # Iterate through the most similar documents + for doc in query_response.docs: + # Load JSON data + doc_json = json.loads(doc.json) + # Create document chunk object with score + result = DocumentChunkWithScore( + id=doc_json["metadata"]["document_id"], + score=doc.score, + text=doc_json["text"], + metadata=doc_json["metadata"] + ) + query_results.append(result) + + # Add to overall results + results.append(QueryResult(query=query.query, results=query_results)) + + return results + + async def _find_keys(self, pattern: str) -> List[str]: + return [key async for key in self.client.scan_iter(pattern)] + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + """ + Removes vectors by ids, filter, or everything in the datastore. + Returns whether the operation was successful. + """ + # Delete all vectors from the index if delete_all is True + if delete_all: + try: + logger.info(f"Deleting all documents from index") + await self.client.ft(REDIS_INDEX_NAME).dropindex(True) + logger.info(f"Deleted all documents successfully") + return True + except Exception as e: + logger.error(f"Error deleting all documents: {e}") + raise e + + # Delete by filter + if filter: + # TODO - extend this to work with other metadata filters? + if filter.document_id: + try: + keys = await self._find_keys( + f"{REDIS_DOC_PREFIX}:{filter.document_id}:*" + ) + await self._redis_delete(keys) + logger.info(f"Deleted document {filter.document_id} successfully") + except Exception as e: + logger.error(f"Error deleting document {filter.document_id}: {e}") + raise e + + # Delete by explicit ids (Redis keys) + if ids: + try: + logger.info(f"Deleting document ids {ids}") + keys = [] + # find all keys associated with the document ids + for document_id in ids: + doc_keys = await self._find_keys( + pattern=f"{REDIS_DOC_PREFIX}:{document_id}:*" + ) + keys.extend(doc_keys) + # delete all keys + logger.info(f"Deleting {len(keys)} keys from Redis") + await self._redis_delete(keys) + except Exception as e: + logger.error(f"Error deleting ids: {e}") + raise e + + return True diff --git a/retrieval/datastore/providers/supabase_datastore.py b/retrieval/datastore/providers/supabase_datastore.py new file mode 100644 index 0000000..ec7395e --- /dev/null +++ b/retrieval/datastore/providers/supabase_datastore.py @@ -0,0 +1,95 @@ +import os +from typing import Any, List +from datetime import datetime + +from supabase import Client + +from datastore.providers.pgvector_datastore import PGClient, PgVectorDataStore +from models.models import ( + DocumentMetadataFilter, +) + +SUPABASE_URL = os.environ.get("SUPABASE_URL") +assert SUPABASE_URL is not None, "SUPABASE_URL is not set" +SUPABASE_ANON_KEY = os.environ.get("SUPABASE_ANON_KEY") +# use service role key if you want this app to be able to bypass your Row Level Security policies +SUPABASE_SERVICE_ROLE_KEY = os.environ.get("SUPABASE_SERVICE_ROLE_KEY") +assert ( + SUPABASE_ANON_KEY is not None or SUPABASE_SERVICE_ROLE_KEY is not None +), "SUPABASE_ANON_KEY or SUPABASE_SERVICE_ROLE_KEY must be set" + + +# class that implements the DataStore interface for Supabase Datastore provider +class SupabaseDataStore(PgVectorDataStore): + def create_db_client(self): + return SupabaseClient() + + +class SupabaseClient(PGClient): + def __init__(self) -> None: + super().__init__() + if not SUPABASE_SERVICE_ROLE_KEY: + self.client = Client(SUPABASE_URL, SUPABASE_ANON_KEY) + else: + self.client = Client(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY) + + async def upsert(self, table: str, json: dict[str, Any]): + """ + Takes in a list of documents and inserts them into the table. + """ + if "created_at" in json: + json["created_at"] = json["created_at"][0].isoformat() + + self.client.table(table).upsert(json).execute() + + async def rpc(self, function_name: str, params: dict[str, Any]): + """ + Calls a stored procedure in the database with the given parameters. + """ + if "in_start_date" in params: + params["in_start_date"] = params["in_start_date"].isoformat() + if "in_end_date" in params: + params["in_end_date"] = params["in_end_date"].isoformat() + + response = self.client.rpc(function_name, params=params).execute() + return response.data + + async def delete_like(self, table: str, column: str, pattern: str): + """ + Deletes rows in the table that match the pattern. + """ + self.client.table(table).delete().like(column, pattern).execute() + + async def delete_in(self, table: str, column: str, ids: List[str]): + """ + Deletes rows in the table that match the ids. + """ + self.client.table(table).delete().in_(column, ids).execute() + + async def delete_by_filters(self, table: str, filter: DocumentMetadataFilter): + """ + Deletes rows in the table that match the filter. + """ + builder = self.client.table(table).delete() + if filter.document_id: + builder = builder.eq( + "document_id", + filter.document_id, + ) + if filter.source: + builder = builder.eq("source", filter.source) + if filter.source_id: + builder = builder.eq("source_id", filter.source_id) + if filter.author: + builder = builder.eq("author", filter.author) + if filter.start_date: + builder = builder.gte( + "created_at", + filter.start_date[0].isoformat(), + ) + if filter.end_date: + builder = builder.lte( + "created_at", + filter.end_date[0].isoformat(), + ) + builder.execute() diff --git a/retrieval/datastore/providers/weaviate_datastore.py b/retrieval/datastore/providers/weaviate_datastore.py new file mode 100644 index 0000000..fe3ae3b --- /dev/null +++ b/retrieval/datastore/providers/weaviate_datastore.py @@ -0,0 +1,385 @@ +import asyncio +import os +import re +import uuid +from typing import Dict, List, Optional + +import weaviate +from loguru import logger +from weaviate import Client +from weaviate.util import generate_uuid5 + +from datastore.datastore import DataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentChunkWithScore, + DocumentMetadataFilter, + QueryResult, + QueryWithEmbedding, + Source, +) + +WEAVIATE_URL_DEFAULT = "http://localhost:8080" +WEAVIATE_CLASS = os.environ.get("WEAVIATE_CLASS", "OpenAIDocument") + +WEAVIATE_BATCH_SIZE = int(os.environ.get("WEAVIATE_BATCH_SIZE", 20)) +WEAVIATE_BATCH_DYNAMIC = os.environ.get("WEAVIATE_BATCH_DYNAMIC", False) +WEAVIATE_BATCH_TIMEOUT_RETRIES = int(os.environ.get("WEAVIATE_TIMEOUT_RETRIES", 3)) +WEAVIATE_BATCH_NUM_WORKERS = int(os.environ.get("WEAVIATE_BATCH_NUM_WORKERS", 1)) + +SCHEMA = { + "class": WEAVIATE_CLASS, + "description": "The main class", + "properties": [ + { + "name": "chunk_id", + "dataType": ["string"], + "description": "The chunk id", + }, + { + "name": "document_id", + "dataType": ["string"], + "description": "The document id", + }, + { + "name": "text", + "dataType": ["text"], + "description": "The chunk's text", + }, + { + "name": "source", + "dataType": ["string"], + "description": "The source of the data", + }, + { + "name": "source_id", + "dataType": ["string"], + "description": "The source id", + }, + { + "name": "url", + "dataType": ["string"], + "description": "The source url", + }, + { + "name": "created_at", + "dataType": ["date"], + "description": "Creation date of document", + }, + { + "name": "author", + "dataType": ["string"], + "description": "Document author", + }, + ], +} + + +def extract_schema_properties(schema): + properties = schema["properties"] + + return {property["name"] for property in properties} + + +class WeaviateDataStore(DataStore): + def handle_errors(self, results: Optional[List[dict]]) -> List[str]: + if not self or not results: + return [] + + error_messages = [] + for result in results: + if ( + "result" not in result + or "errors" not in result["result"] + or "error" not in result["result"]["errors"] + ): + continue + for message in result["result"]["errors"]["error"]: + error_messages.append(message["message"]) + logger.error(message["message"]) + + return error_messages + + def __init__(self): + auth_credentials = self._build_auth_credentials() + + url = os.environ.get("WEAVIATE_URL", WEAVIATE_URL_DEFAULT) + + logger.debug( + f"Connecting to weaviate instance at {url} with credential type {type(auth_credentials).__name__}" + ) + self.client = Client(url, auth_client_secret=auth_credentials) + self.client.batch.configure( + batch_size=WEAVIATE_BATCH_SIZE, + dynamic=WEAVIATE_BATCH_DYNAMIC, # type: ignore + callback=self.handle_errors, # type: ignore + timeout_retries=WEAVIATE_BATCH_TIMEOUT_RETRIES, + num_workers=WEAVIATE_BATCH_NUM_WORKERS, + ) + + if self.client.schema.contains(SCHEMA): + current_schema = self.client.schema.get(WEAVIATE_CLASS) + current_schema_properties = extract_schema_properties(current_schema) + + logger.debug( + f"Found index {WEAVIATE_CLASS} with properties {current_schema_properties}" + ) + logger.debug("Will reuse this schema") + else: + new_schema_properties = extract_schema_properties(SCHEMA) + logger.debug( + f"Creating collection {WEAVIATE_CLASS} with properties {new_schema_properties}" + ) + self.client.schema.create_class(SCHEMA) + + @staticmethod + def _build_auth_credentials(): + url = os.environ.get("WEAVIATE_URL", WEAVIATE_URL_DEFAULT) + + if WeaviateDataStore._is_wcs_domain(url): + api_key = os.environ.get("WEAVIATE_API_KEY") + if api_key is not None: + return weaviate.auth.AuthApiKey(api_key=api_key) + else: + raise ValueError("WEAVIATE_API_KEY environment variable is not set") + else: + return None + + async def _upsert(self, chunks: Dict[str, List[DocumentChunk]]) -> List[str]: + """ + Takes in a list of list of document chunks and inserts them into the database. + Return a list of document ids. + """ + doc_ids = [] + + with self.client.batch as batch: + for doc_id, doc_chunks in chunks.items(): + logger.debug(f"Upserting {doc_id} with {len(doc_chunks)} chunks") + for doc_chunk in doc_chunks: + # we generate a uuid regardless of the format of the document_id because + # weaviate needs a uuid to store each document chunk and + # a document chunk cannot share the same uuid + doc_uuid = generate_uuid5(doc_chunk, WEAVIATE_CLASS) + metadata = doc_chunk.metadata + doc_chunk_dict = doc_chunk.dict() + doc_chunk_dict.pop("metadata") + for key, value in metadata.dict().items(): + doc_chunk_dict[key] = value + doc_chunk_dict["chunk_id"] = doc_chunk_dict.pop("id") + doc_chunk_dict["source"] = ( + doc_chunk_dict.pop("source").value + if doc_chunk_dict["source"] + else None + ) + embedding = doc_chunk_dict.pop("embedding") + + batch.add_data_object( + uuid=doc_uuid, + data_object=doc_chunk_dict, + class_name=WEAVIATE_CLASS, + vector=embedding, + ) + + doc_ids.append(doc_id) + batch.flush() + return doc_ids + + async def _query( + self, + queries: List[QueryWithEmbedding], + ) -> List[QueryResult]: + """ + Takes in a list of queries with embeddings and filters and returns a list of query results with matching document chunks and scores. + """ + + async def _single_query(query: QueryWithEmbedding) -> QueryResult: + logger.debug(f"Query: {query.query}") + if not hasattr(query, "filter") or not query.filter: + result = ( + self.client.query.get( + WEAVIATE_CLASS, + [ + "chunk_id", + "document_id", + "text", + "source", + "source_id", + "url", + "created_at", + "author", + ], + ) + .with_hybrid(query=query.query, alpha=0.5, vector=query.embedding) + .with_limit(query.top_k) # type: ignore + .with_additional(["score", "vector"]) + .do() + ) + else: + filters_ = self.build_filters(query.filter) + result = ( + self.client.query.get( + WEAVIATE_CLASS, + [ + "chunk_id", + "document_id", + "text", + "source", + "source_id", + "url", + "created_at", + "author", + ], + ) + .with_hybrid(query=query.query, alpha=0.5, vector=query.embedding) + .with_where(filters_) + .with_limit(query.top_k) # type: ignore + .with_additional(["score", "vector"]) + .do() + ) + + query_results: List[DocumentChunkWithScore] = [] + response = result["data"]["Get"][WEAVIATE_CLASS] + + for resp in response: + result = DocumentChunkWithScore( + id=resp["chunk_id"], + text=resp["text"], + # embedding=resp["_additional"]["vector"], + score=resp["_additional"]["score"], + metadata=DocumentChunkMetadata( + document_id=resp["document_id"] if resp["document_id"] else "", + source=Source(resp["source"]) if resp["source"] else None, + source_id=resp["source_id"], + url=resp["url"], + created_at=resp["created_at"], + author=resp["author"], + ), + ) + query_results.append(result) + return QueryResult(query=query.query, results=query_results) + + return await asyncio.gather(*[_single_query(query) for query in queries]) + + async def delete( + self, + ids: Optional[List[str]] = None, + filter: Optional[DocumentMetadataFilter] = None, + delete_all: Optional[bool] = None, + ) -> bool: + # TODO + """ + Removes vectors by ids, filter, or everything in the datastore. + Returns whether the operation was successful. + """ + if delete_all: + logger.debug(f"Deleting all vectors in index {WEAVIATE_CLASS}") + self.client.schema.delete_all() + return True + + if ids: + operands = [ + {"path": ["document_id"], "operator": "Equal", "valueString": id} + for id in ids + ] + + where_clause = {"operator": "Or", "operands": operands} + + logger.debug(f"Deleting vectors from index {WEAVIATE_CLASS} with ids {ids}") + result = self.client.batch.delete_objects( + class_name=WEAVIATE_CLASS, where=where_clause, output="verbose" + ) + + if not bool(result["results"]["successful"]): + logger.debug( + f"Failed to delete the following objects: {result['results']['objects']}" + ) + + if filter: + where_clause = self.build_filters(filter) + + logger.debug( + f"Deleting vectors from index {WEAVIATE_CLASS} with filter {where_clause}" + ) + result = self.client.batch.delete_objects( + class_name=WEAVIATE_CLASS, where=where_clause + ) + + if not bool(result["results"]["successful"]): + logger.debug( + f"Failed to delete the following objects: {result['results']['objects']}" + ) + + return True + + @staticmethod + def build_filters(filter): + if filter.source: + filter.source = filter.source.value + + operands = [] + filter_conditions = { + "source": { + "operator": "Equal", + "value": "query.filter.source.value", + "value_key": "valueString", + }, + "start_date": {"operator": "GreaterThanEqual", "value_key": "valueDate"}, + "end_date": {"operator": "LessThanEqual", "value_key": "valueDate"}, + "default": {"operator": "Equal", "value_key": "valueString"}, + } + + for attr, value in filter.__dict__.items(): + if value is not None: + filter_condition = filter_conditions.get( + attr, filter_conditions["default"] + ) + value_key = filter_condition["value_key"] + + operand = { + "path": [ + attr + if not (attr == "start_date" or attr == "end_date") + else "created_at" + ], + "operator": filter_condition["operator"], + value_key: value, + } + + operands.append(operand) + + return {"operator": "And", "operands": operands} + + @staticmethod + def _is_valid_weaviate_id(candidate_id: str) -> bool: + """ + Check if candidate_id is a valid UUID for weaviate's use + + Weaviate supports UUIDs of version 3, 4 and 5. This function checks if the candidate_id is a valid UUID of one of these versions. + See https://weaviate.io/developers/weaviate/more-resources/faq#q-are-there-restrictions-on-uuid-formatting-do-i-have-to-adhere-to-any-standards + for more information. + """ + acceptable_version = [3, 4, 5] + + try: + result = uuid.UUID(candidate_id) + if result.version not in acceptable_version: + return False + else: + return True + except ValueError: + return False + + @staticmethod + def _is_wcs_domain(url: str) -> bool: + """ + Check if the given URL ends with ".weaviate.network" or ".weaviate.network/". + + Args: + url (str): The URL to check. + + Returns: + bool: True if the URL ends with the specified strings, False otherwise. + """ + pattern = r"\.(weaviate\.cloud|weaviate\.network)(/)?$" + return bool(re.search(pattern, url)) diff --git a/retrieval/datastore/providers/zilliz_datastore.py b/retrieval/datastore/providers/zilliz_datastore.py new file mode 100644 index 0000000..81f151c --- /dev/null +++ b/retrieval/datastore/providers/zilliz_datastore.py @@ -0,0 +1,65 @@ +import os + +from loguru import logger +from typing import Optional +from pymilvus import ( + connections, +) +from uuid import uuid4 + +from datastore.providers.milvus_datastore import ( + MilvusDataStore, +) + + +ZILLIZ_COLLECTION = os.environ.get("ZILLIZ_COLLECTION") or "c" + uuid4().hex +ZILLIZ_URI = os.environ.get("ZILLIZ_URI") +ZILLIZ_USER = os.environ.get("ZILLIZ_USER") +ZILLIZ_PASSWORD = os.environ.get("ZILLIZ_PASSWORD") +ZILLIZ_USE_SECURITY = False if ZILLIZ_PASSWORD is None else True + +ZILLIZ_CONSISTENCY_LEVEL = os.environ.get("ZILLIZ_CONSISTENCY_LEVEL") + +class ZillizDataStore(MilvusDataStore): + def __init__(self, create_new: Optional[bool] = False): + """Create a Zilliz DataStore. + + The Zilliz Datastore allows for storing your indexes and metadata within a Zilliz Cloud instance. + + Args: + create_new (Optional[bool], optional): Whether to overwrite if collection already exists. Defaults to True. + """ + # Overwrite the default consistency level by MILVUS_CONSISTENCY_LEVEL + self._consistency_level = ZILLIZ_CONSISTENCY_LEVEL or "Bounded" + self._create_connection() + + self._create_collection(ZILLIZ_COLLECTION, create_new) # type: ignore + self._create_index() + + def _create_connection(self): + # Check if the connection already exists + try: + i = [ + connections.get_connection_addr(x[0]) + for x in connections.list_connections() + ].index({"address": ZILLIZ_URI, "user": ZILLIZ_USER}) + self.alias = connections.list_connections()[i][0] + except ValueError: + # Connect to the Zilliz instance using the passed in Environment variables + self.alias = uuid4().hex + connections.connect(alias=self.alias, uri=ZILLIZ_URI, user=ZILLIZ_USER, password=ZILLIZ_PASSWORD, secure=ZILLIZ_USE_SECURITY) # type: ignore + logger.info("Connect to zilliz cloud server") + + def _create_index(self): + try: + # If no index on the collection, create one + if len(self.col.indexes) == 0: + self.index_params = {"metric_type": "IP", "index_type": "AUTOINDEX", "params": {}} + self.col.create_index("embedding", index_params=self.index_params) + + self.col.load() + self.search_params = {"metric_type": "IP", "params": {}} + except Exception as e: + logger.error("Failed to create index, error: {}".format(e)) + + diff --git a/retrieval/docs/deployment/flyio.md b/retrieval/docs/deployment/flyio.md new file mode 100644 index 0000000..17cf55b --- /dev/null +++ b/retrieval/docs/deployment/flyio.md @@ -0,0 +1,89 @@ +# Deploying to Fly.io + +## Removing Unused Dependencies + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. + +Find the packages you can remove for each vector database provider [here](removing-unused-dependencies.md). + +After removing the unnecessary packages from the `pyproject.toml` file, you don't need to run `poetry lock` and `poetry install` manually. The provided Dockerfile takes care of installing the required dependencies using the `requirements.txt` file generated by the `poetry export` command. + +## Deployment + +To deploy the Docker container from this repository to Fly.io, follow +these steps: + +[Install Docker](https://docs.docker.com/engine/install/) on your local machine if it is not already installed. + +Install the [Fly.io CLI](https://fly.io/docs/getting-started/installing-flyctl/) on your local machine. + +Clone the repository from GitHub: + +``` +git clone https://github.com/openai/chatgpt-retrieval-plugin.git +``` + +Navigate to the cloned repository directory: + +``` +cd path/to/chatgpt-retrieval-plugin +``` + +Log in to the Fly.io CLI: + +``` +flyctl auth login +``` + +Create and launch your Fly.io app: + +``` +flyctl launch +``` + +Follow the instructions in your terminal: + +- Choose your app name +- Choose your app region +- Don't add any databases +- Don't deploy yet (if you do, the first deploy might fail as the environment variables are not yet set) + +Set the required environment variables: + +``` +flyctl secrets set DATASTORE=your_datastore \ +OPENAI_API_KEY=your_openai_api_key \ +BEARER_TOKEN=your_bearer_token \ + +``` + +Alternatively, you could set environment variables in the [Fly.io Console](https://fly.io/dashboard). + +At this point, you can change the plugin url in your plugin manifest file [here](/.well-known/ai-plugin.json), and in your OpenAPI schema [here](/.well-known/openapi.yaml) to the url for your Fly.io app, which will be `https://your-app-name.fly.dev`. + +Deploy your app with: + +``` +flyctl deploy +``` + +After completing these steps, your Docker container should be deployed to Fly.io and running with the necessary environment variables set. You can view your app by running: + +``` +flyctl open +``` + +which will open your app url. You should be able to find the OpenAPI schema at `/.well-known/openapi.yaml` and the manifest at `/.well-known/ai-plugin.json`. + +To view your app logs: + +``` +flyctl logs +``` + +Now, make sure you have changed the plugin url in your plugin manifest file [here](/.well-known/ai-plugin.json), and in your OpenAPI schema [here](/.well-known/openapi.yaml), and redeploy with `flyctl deploy`. This url will be `https://.fly.dev`. + +**Debugging tips:** +Fly.io uses port 8080 by default. + +If your app fails to deploy, check if the environment variables are set correctly, and then check if your port is configured correctly. You could also try using the [`-e` flag](https://fly.io/docs/flyctl/launch/) with the `flyctl launch` command to set the environment variables at launch. diff --git a/retrieval/docs/deployment/heroku.md b/retrieval/docs/deployment/heroku.md new file mode 100644 index 0000000..1323bf4 --- /dev/null +++ b/retrieval/docs/deployment/heroku.md @@ -0,0 +1,105 @@ +# Deploying to Heroku + +## Removing Unused Dependencies + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. + +Find the packages you can remove for each vector database provider [here](removing-unused-dependencies.md). + +After removing the unnecessary packages from the `pyproject.toml` file, you don't need to run `poetry lock` and `poetry install` manually. The provided Dockerfile takes care of installing the required dependencies using the `requirements.txt` file generated by the `poetry export` command. + +## Deployment + +To deploy the Docker container from this repository to Heroku and set the required environment variables, follow these steps: + +[Install Docker](https://docs.docker.com/engine/install/) on your local machine if it is not already installed. + +Install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) on your local machine. + +Clone the repository from GitHub: + +``` +git clone https://github.com/openai/chatgpt-retrieval-plugin.git +``` + +Navigate to the cloned repository directory: + +``` +cd path/to/chatgpt-retrieval-plugin +``` + +Log in to the Heroku CLI: + +``` +heroku login +``` + +Create a Heroku app: + +``` +heroku create [app-name] +``` + +Log in to the Heroku Container Registry: + +``` +heroku container:login +``` + +Alternatively, you can use a command from the Makefile to log in to the Heroku Container Registry by running: + +``` +make heroku-login +``` + +Build the Docker image using the Dockerfile: + +``` +docker buildx build --platform linux/amd64 -t [image-name] . +``` + +(Replace `[image-name]` with the name you want to give your Docker image) + +Push the Docker image to the Heroku Container Registry, and release the newly pushed image to your Heroku app. + +``` +docker tag [image-name] registry.heroku.com/[app-name]/web +docker push registry.heroku.com/[app-name]/web +heroku container:release web -a [app-name] +``` + +Alternatively, you can use a command from the to push the Docker image to the Heroku Container Registry by running: + +``` +make heroku-push +``` + +**Note:** You will need to edit the Makefile and replace `` with your actual app name. + +Set the required environment variables for your Heroku app: + +``` +heroku config:set DATASTORE=your_datastore \ +OPENAI_API_KEY=your_openai_api_key \ +BEARER_TOKEN=your_bearer_token \ + \ +-a [app-name] +``` + +You could also set environment variables in the [Heroku Console](https://dashboard.heroku.com/apps). + +After completing these steps, your Docker container should be deployed to Heroku and running with the necessary environment variables set. You can view your app by running: + +``` +heroku open -a [app-name] +``` + +which will open your app url. You should be able to find the OpenAPI schema at `/.well-known/openapi.yaml` and the manifest at `/.well-known/ai-plugin.json`. + +To view your app logs: + +``` +heroku logs --tail -a [app-name] +``` + +Now make sure to change the plugin url in your plugin manifest file [here](/.well-known/ai-plugin.json), and in your OpenAPI schema [here](/.well-known/openapi.yaml), and redeploy with `make heroku-push`. This url will be `https://your-app-name.herokuapp.com`. diff --git a/retrieval/docs/deployment/other-options.md b/retrieval/docs/deployment/other-options.md new file mode 100644 index 0000000..6f4a697 --- /dev/null +++ b/retrieval/docs/deployment/other-options.md @@ -0,0 +1,17 @@ +# Other Deployment Options + +Some possible other options for deploying the app are: + +- **Azure Container Apps**: This is a cloud platform that allows you to deploy and manage web apps using Docker containers. You can use the Azure CLI or the Azure Portal to create and configure your app service, and then push your Docker image to a container registry and deploy it to your app service. You can also set environment variables and scale your app using the Azure Portal. Learn more [here](https://learn.microsoft.com/en-us/azure/container-apps/get-started-existing-container-image-portal?pivots=container-apps-private-registry). +- **Google Cloud Run**: This is a serverless platform that allows you to run stateless web apps using Docker containers. You can use the Google Cloud Console or the gcloud command-line tool to create and deploy your Cloud Run service, and then push your Docker image to the Google Container Registry and deploy it to your service. You can also set environment variables and scale your app using the Google Cloud Console. Learn more [here](https://cloud.google.com/run/docs/quickstarts/build-and-deploy). +- **AWS Elastic Container Service**: This is a cloud platform that allows you to run and manage web apps using Docker containers. You can use the AWS CLI or the AWS Management Console to create and configure your ECS cluster, and then push your Docker image to the Amazon Elastic Container Registry and deploy it to your cluster. You can also set environment variables and scale your app using the AWS Management Console. Learn more [here](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html). + +After you create your app, make sure to change the plugin url in your plugin manifest file [here](/.well-known/ai-plugin.json), and in your OpenAPI schema [here](/.well-known/openapi.yaml), and redeploy. + +## Removing Unused Dependencies + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. + +Find the packages you can remove for each vector database provider [here](removing_unused_dependencies.md). + +After removing the unnecessary packages from the `pyproject.toml` file, you don't need to run `poetry lock` and `poetry install` manually. The provided Dockerfile takes care of installing the required dependencies using the `requirements.txt` file generated by the `poetry export` command. diff --git a/retrieval/docs/deployment/removing-unused-dependencies.md b/retrieval/docs/deployment/removing-unused-dependencies.md new file mode 100644 index 0000000..44a56c6 --- /dev/null +++ b/retrieval/docs/deployment/removing-unused-dependencies.md @@ -0,0 +1,20 @@ +# Removing Unused Dependencies + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. + +Here are the packages you can remove for each vector database provider: + +- **Pinecone:** Remove `weaviate-client`, `pymilvus`, `qdrant-client`, `redis`, `chromadb`, `llama-index`, `azure-identity`, `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Weaviate:** Remove `pinecone-client`, `pymilvus`, `qdrant-client`, `redis`, `chromadb`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, `psycopg2cffi`. +- **Zilliz:** Remove `pinecone-client`, `weaviate-client`, `qdrant-client`, `redis`, `chromadb`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Milvus:** Remove `pinecone-client`, `weaviate-client`, `qdrant-client`, `redis`, `chromadb`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Qdrant:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `redis`, `chromadb`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Redis:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `chromadb`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **LlamaIndex:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `chromadb`, `redis`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Chroma:**: Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `llama-index`, `redis`, `azure-identity` and `azure-search-documents`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Azure Cognitive Search**: Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `llama-index`, `redis` and `chromadb`, `supabase`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Supabase:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `redis`, `llama-index`, `azure-identity` and `azure-search-documents`, `psycopg2`+`pgvector`, and `psycopg2cffi`. +- **Postgres:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `redis`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, and `psycopg2cffi`. +- **AnalyticDB:** Remove `pinecone-client`, `weaviate-client`, `pymilvus`, `qdrant-client`, `redis`, `llama-index`, `azure-identity` and `azure-search-documents`, `supabase`, and `psycopg2`+`pgvector`. + +After removing the unnecessary packages from the `pyproject.toml` file, you don't need to run `poetry lock` and `poetry install` manually. The provided Dockerfile takes care of installing the required dependencies using the `requirements.txt` file generated by the `poetry export` command. diff --git a/retrieval/docs/deployment/render-thumbnail.png b/retrieval/docs/deployment/render-thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..4bd725aaeffa399af7d2302f0529e7420889380e GIT binary patch literal 260172 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%0D?ZJZ{0&sVuHLo}e}(gVdg0h6b=f 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

KY`tV^3x4}jA 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 kw@BkC`QkR*H$bf&!O6s@rZxc~qF literal 0 HcmV?d00001 diff --git a/retrieval/docs/deployment/render.md b/retrieval/docs/deployment/render.md new file mode 100644 index 0000000..741f555 --- /dev/null +++ b/retrieval/docs/deployment/render.md @@ -0,0 +1,19 @@ +# Deploying to Render + +## Removing Unused Dependencies + +Before deploying your app, you might want to remove unused dependencies from your [pyproject.toml](/pyproject.toml) file to reduce the size of your app and improve its performance. Depending on the vector database provider you choose, you can remove the packages that are not needed for your specific provider. + +Find the packages you can remove for each vector database provider [here](removing-unused-dependencies.md). + +After removing the unnecessary packages from the `pyproject.toml` file, you don't need to run `poetry lock` and `poetry install` manually. The provided Dockerfile takes care of installing the required dependencies using the `requirements.txt` file generated by the `poetry export` command. + +## Deployment + +Render maintains a [fork](https://github.com/render-examples/chatgpt-retrieval-plugin/) of this repository with a few small changes that facilitate easy deployment. The source code is unchanged. To deploy both the Docker container from this repository and a self-hosted Weaviate database to back it, just click the button below. Enter your OpenAI API key when prompted. + +[Deploy to Render](https://render.com/deploy?repo=https://github.com/render-examples/chatgpt-retrieval-plugin/tree/main) + +The bearer token will be randomly generated for you. You can view it in in the "Environment" tab on the [Render dashboard](https://dashboard.render.com) page for your server. For more guidance, consult the [README in Render's fork](https://github.com/render-examples/chatgpt-retrieval-plugin/blob/main/README.md), [Render's documentation](https://render.com/docs), or the screen recording linked below. + +[![Deploy to Render screen recording](render-thumbnail.png)](https://vimeo.com/823610578) diff --git a/retrieval/docs/providers/analyticdb/setup.md b/retrieval/docs/providers/analyticdb/setup.md new file mode 100644 index 0000000..8aed01f --- /dev/null +++ b/retrieval/docs/providers/analyticdb/setup.md @@ -0,0 +1,82 @@ +# AnalyticDB + +[AnalyticDB](https://www.alibabacloud.com/help/en/analyticdb-for-postgresql/latest/product-introduction-overview) is a distributed cloud-native vector database designed for storing documents and vector embeddings. It is a high-performance vector database that is fully compatible with PostgreSQL syntax, making it easy to use. Managed by Alibaba Cloud, AnalyticDB offers a powerful vector compute engine, processing billions of data vectors and providing a wide range of features, including indexing algorithms, structured and unstructured data capabilities, real-time updates, distance metrics, scalar filtering, and time travel searches. Additionally, it offers full OLAP database functionality and an SLA commitment for production use. + +## Install Requirements + +Run the following command to install the required packages, including the `psycopg2cffi` package: + +``` +poetry install --extras "postgresql" +``` + +If you encounter the `Error: pg_config executable not found.` issue, you need to install the PostgreSQL development package on your system. Follow the instructions for your specific Linux distribution: + +1. Debian-based systems (e.g., Ubuntu): + +```bash +sudo apt-get update +sudo apt-get install libpq-dev +``` + +2. RHEL-based systems (e.g., CentOS, Fedora): + +```bash +sudo yum install postgresql-devel +``` + +3. Arch-based systems (e.g., Manjaro, Arch Linux): + +```bash +sudo pacman -S postgresql-libs +``` + +4. macOS: + +```bash +brew install postgresql +``` + +After installing the required package, try to install `psycopg2cffi` again. If the `pg_config` executable is still not found, add its location to your system's `PATH` variable. You can typically find the `pg_config` executable in the `bin` directory of your PostgreSQL installation, for example `/usr/pgsql-13/bin/pg_config`. To add it to your `PATH` variable, use the following command (replace the path with the correct one for your system): + +```bash +export PATH=$PATH:/usr/pgsql-13/bin +``` + +Now, try installing `psycopg2cffi` again using Poetry. + +**Environment Variables:** + +| Name | Required | Description | Default | +| ---------------- | -------- | ----------------------------------- | ----------------- | +| `DATASTORE` | Yes | Datastore name, set to `analyticdb` | | +| `BEARER_TOKEN` | Yes | Secret token | | +| `OPENAI_API_KEY` | Yes | OpenAI API key | | +| `PG_HOST` | Yes | AnalyticDB instance URL | `localhost` | +| `PG_USER` | Yes | Database user | `user` | +| `PG_PASSWORD` | Yes | Database password | `password` | +| `PG_PORT` | Optional | Port for AnalyticDB communication | `5432` | +| `PG_DATABASE` | Optional | Database name | `postgres` | +| `PG_COLLECTION` | Optional | AnalyticDB relation name | `document_chunks` | + +## AnalyticDB Cloud + +For a hosted [AnalyticDB Cloud](https://cloud.qdrant.io/) version, provide the AnalyticDB instance URL: + +**Example:** + +```bash +PG_HOST="https://YOUR-CLUSTER-URL.gpdb.rds.aliyuncs.com" +PG_USER="YOUR-USER-NAME" +PG_PASSWORD="YOUR-PASSWORD" +``` + +The other parameters are optional and can be changed if needed. + +## Running AnalyticDB Integration Tests + +A suite of integration tests verifies the AnalyticDB integration. Launch the test suite with this command: + +```bash +pytest ./tests/datastore/providers/analyticdb/test_analyticdb_datastore.py +``` diff --git a/retrieval/docs/providers/azuresearch/setup.md b/retrieval/docs/providers/azuresearch/setup.md new file mode 100644 index 0000000..e184c26 --- /dev/null +++ b/retrieval/docs/providers/azuresearch/setup.md @@ -0,0 +1,29 @@ +# Azure Cognitive Search + +[Azure Cognitive Search](https://azure.microsoft.com/products/search/) is a complete retrieval cloud service that supports vector search, text search, and hybrid (vectors + text combined to yield the best of the two approaches). Azure Cognitive Search also offers an [optional L2 re-ranking step](https://learn.microsoft.com/azure/search/semantic-search-overview) to further improve results quality. + +You can find the Azure Cognitive Search documentation [here](https://learn.microsoft.com/azure/search/search-what-is-azure-search). If you don't have an Azure account, you can start setting one up [here](https://azure.microsoft.com/). + +## Environment variables + +| Name | Required | Description | Default | +| ---------------------------- | -------- | ------------------------------------------------------------------------------------- | ------------------- | +| `DATASTORE` | Yes | Datastore name, set to `azuresearch` | | +| `BEARER_TOKEN` | Yes | Secret token | | +| `OPENAI_API_KEY` | Yes | OpenAI API key | | +| `AZURESEARCH_SERVICE` | Yes | Name of your search service | | +| `AZURESEARCH_INDEX` | Yes | Name of your search index | | +| `AZURESEARCH_API_KEY` | No | Your API key, if using key-based auth instead of Azure managed identity |Uses managed identity| +| `AZURESEARCH_DISABLE_HYBRID` | No | Disable hybrid search and only use vector similarity |Use hybrid search | +| `AZURESEARCH_SEMANTIC_CONFIG`| No | Enable L2 re-ranking with this configuration name [see re-ranking below](#re-ranking) |L2 not enabled | +| `AZURESEARCH_LANGUAGE` | No | If using L2 re-ranking, language for queries/documents (valid values [listed here](https://learn.microsoft.com/rest/api/searchservice/preview-api/search-documents#queryLanguage)) |`en-us` | +| `AZURESEARCH_DIMENSIONS` | No | Vector size for embeddings |1536 (OpenAI's Ada002)| + +## Authentication Options + +* API key: this is enabled by default; you can obtain the key in the Azure Portal or using the Azure CLI. +* Managed identity: If the plugin is running in Azure, you can enable managed identity for the host and give that identity access to the service, without having to manage keys (avoiding secret storage, rotation, etc.). More details [here](https://learn.microsoft.com/azure/search/search-security-rbac). + +## Re-ranking + +Azure Cognitive Search offers the option to enable a second (L2) ranking step after retrieval to further improve results quality. This only applies when using text or hybrid search. Since it has latency and cost implications, if you want to try this option you need to explicitly [enable "semantic search"](https://learn.microsoft.com/azure/search/semantic-search-overview#enable-semantic-search) in your Cognitive Search service, and [create a semantic search configuration](https://learn.microsoft.com/azure/search/semantic-how-to-query-request#2---create-a-semantic-configuration) for your index. \ No newline at end of file diff --git a/retrieval/docs/providers/chroma/setup.md b/retrieval/docs/providers/chroma/setup.md new file mode 100644 index 0000000..2822b04 --- /dev/null +++ b/retrieval/docs/providers/chroma/setup.md @@ -0,0 +1,29 @@ +[Chroma](https://trychroma.com) is an AI-native open-source embedding database designed to make it easy to work with embeddings. Chroma runs in-memory, or in a client-server setup. + +Install Chroma by running `pip install chromadb`. Once installed, the core API consists of four essential commands for creating collections, adding embeddings, documents, and metadata, and querying embeddings to find similar documents. Get started with Chroma by visiting the [Getting Started](https://docs.trychroma.com) page on their documentation website, or explore the open-source code on their [GitHub repository](https://github.com/chroma-core/chroma). + +**Chroma Environment Variables** + +To set up Chroma and start using it as your vector database provider, you need to define some environment variables to connect to your Chroma instance. + +**Chroma Datastore Environment Variables** + +Chroma runs _in-memory_ by default, with local persistence. It can also run in [self-hosted](https://docs.trychroma.com/usage-guide#running-chroma-in-clientserver-mode) client-server mode, with a fully managed hosted version coming soon. + +| Name | Required | Description | Default | +| ------------------------ | -------- | -------------------------------------------------------------------------------------------------- | ---------------- | +| `DATASTORE` | Yes | Datastore name. Set this to `chroma` | | +| `BEARER_TOKEN` | Yes | Your secret token for authenticating requests to the API | | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key for generating embeddings | | +| `CHROMA_COLLECTION` | Optional | Your chosen Chroma collection name to store your embeddings | openaiembeddings | +| `CHROMA_IN_MEMORY` | Optional | If set to `True`, ignore `CHROMA_HOST` and `CHROMA_PORT` and just use an in-memory Chroma instance | `True` | +| `CHROMA_PERSISTENCE_DIR` | Optional | If set, and `CHROMA_IN_MEMORY` is set, persist to and load from this directory. | `openai` | + +To run Chroma in self-hosted client-server mode, st the following variables: + +| Name | Required | Description | Default | +| ------------- | -------- | --------------------------------------------------- | ------------------ | +| `CHROMA_HOST` | Optional | Your Chroma instance host address (see notes below) | `http://127.0.0.1` | +| `CHROMA_PORT` | Optional | Your Chroma port number | `8000` | + +> For **self-hosted instances**, if your instance is not at 127.0.0.1:8000, set `CHROMA_HOST` and `CHROMA_PORT` accordingly. For example: `CHROMA_HOST=http://localhost/` and `CHROMA_PORT=8080`. diff --git a/retrieval/docs/providers/llama/setup.md b/retrieval/docs/providers/llama/setup.md new file mode 100644 index 0000000..2378700 --- /dev/null +++ b/retrieval/docs/providers/llama/setup.md @@ -0,0 +1,51 @@ + +# LlamaIndex + +[LlamaIndex](https://github.com/jerryjliu/llama_index) is a central interface to connect your LLM's with external data. +It provides a suite of in-memory indices over your unstructured and structured data for use with ChatGPT. +Unlike standard vector databases, LlamaIndex supports a wide range of indexing strategies (e.g. tree, keyword table, knowledge graph) optimized for different use-cases. +It is light-weight, easy-to-use, and requires no additional deployment. +All you need to do is specifying a few environment variables (optionally point to an existing saved Index json file). +Note that metadata filters in queries are not yet supported. + +## Setup +Currently, LlamaIndex requires no additional deployment +and runs as a part of the Retrieval Plugin. +It is super easy to setup and great for quick prototyping +with ChatGPT and your external data. + +**Retrieval App Environment Variables** + +| Name | Required | Description | +|------------------|----------|-------------------------------------| +| `DATASTORE` | Yes | Datastore name. Set this to `llama` | +| `BEARER_TOKEN` | Yes | Your secret token | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | + +**Llama Datastore Environment Variables** + +| Name | Required | Description | Default | +|--------------------------------|----------|--------------------------------------|---------------| +| `LLAMA_INDEX_TYPE` | Optional | Index type (see below for details) | `simple_dict` | +| `LLAMA_INDEX_JSON_PATH` | Optional | Path to saved Index json file | None | +| `LLAMA_QUERY_KWARGS_JSON_PATH` | Optional | Path to saved query kwargs json file | None | +| `LLAMA_RESPONSE_MODE` | Optional | Response mode for query | `no_text` | + + +**Different Index Types** +By default, we use a `GPTSimpleVectorIndex` to store document chunks in memory, +and retrieve top-k nodes by embedding similarity. +Different index types are optimized for different data and query use-cases. +See this guide on [How Each Index Works](https://gpt-index.readthedocs.io/en/latest/guides/primer/index_guide.html) to learn more. +You can configure the index type via the `LLAMA_INDEX_TYPE`, see [here](https://gpt-index.readthedocs.io/en/latest/reference/indices/composability_query.html#gpt_index.data_structs.struct_type.IndexStructType) for the full list of accepted index type identifiers. + + +Read more details on [readthedocs](https://gpt-index.readthedocs.io/en/latest/), +and engage with the community on [discord](https://discord.com/invite/dGcwcsnxhU). + +## Running Tests +You can launch the test suite with this command: + +```bash +pytest ./tests/datastore/providers/llama/test_llama_datastore.py +``` \ No newline at end of file diff --git a/retrieval/docs/providers/milvus/setup.md b/retrieval/docs/providers/milvus/setup.md new file mode 100644 index 0000000..eb0ca4b --- /dev/null +++ b/retrieval/docs/providers/milvus/setup.md @@ -0,0 +1,43 @@ +# Milvus + +[Milvus](https://milvus.io/) is the open-source, cloud-native vector database that scales to billions of vectors. It's the open-source version of Zilliz. It supports: + +- Various indexing algorithms and distance metrics +- Scalar filtering and time travel searches +- Rollback and snapshots +- Multi-language SDKs +- Storage and compute separation +- Cloud scalability +- A developer-first community with multi-language support + +Visit the [Github](https://github.com/milvus-io/milvus) to learn more. + +## Deploying the Database + +You can deploy and manage Milvus using Docker Compose, Helm, K8's Operator, or Ansible. Follow the instructions [here](https://milvus.io/docs) to get started. + +**Environment Variables:** + +| Name | Required | Description | +|----------------------------| -------- |----------------------------------------------------------------------------------------------------------------------------------------------| +| `DATASTORE` | Yes | Datastore name, set to `milvus` | +| `BEARER_TOKEN` | Yes | Your bearer token | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | +| `MILVUS_COLLECTION` | Optional | Milvus collection name, defaults to a random UUID | +| `MILVUS_HOST` | Optional | Milvus host IP, defaults to `localhost` | +| `MILVUS_PORT` | Optional | Milvus port, defaults to `19530` | +| `MILVUS_USER` | Optional | Milvus username if RBAC is enabled, defaults to `None` | +| `MILVUS_PASSWORD` | Optional | Milvus password if required, defaults to `None` | +| `MILVUS_INDEX_PARAMS` | Optional | Custom index options for the collection, defaults to `{"metric_type": "IP", "index_type": "HNSW", "params": {"M": 8, "efConstruction": 64}}` | +| `MILVUS_SEARCH_PARAMS` | Optional | Custom search options for the collection, defaults to `{"metric_type": "IP", "params": {"ef": 10}}` | +| `MILVUS_CONSISTENCY_LEVEL` | Optional | Data consistency level for the collection, defaults to `Bounded` | + +## Running Milvus Integration Tests + +A suite of integration tests is available to verify the Milvus integration. To run the tests, run the milvus docker compose found in the examples folder. + +Then, launch the test suite with this command: + +```bash +pytest ./tests/datastore/providers/milvus/test_milvus_datastore.py +``` diff --git a/retrieval/docs/providers/pinecone/setup.md b/retrieval/docs/providers/pinecone/setup.md new file mode 100644 index 0000000..37230d8 --- /dev/null +++ b/retrieval/docs/providers/pinecone/setup.md @@ -0,0 +1,35 @@ +# Pinecone + +[Pinecone](https://www.pinecone.io) is a managed vector database built for speed, scale, and shipping to production sooner. To use Pinecone as your vector database provider, first get an API key by [signing up for an account](https://app.pinecone.io/). You can access your API key from the "API Keys" section in the sidebar of your dashboard. Pinecone also supports hybrid search and at the time of writing is the only datastore to support SPLADE sparse vectors natively. + +A full Jupyter notebook walkthrough for the Pinecone flavor of the retrieval plugin can be found [here](https://github.com/openai/chatgpt-retrieval-plugin/blob/main/examples/providers/pinecone/semantic-search.ipynb). There is also a [video walkthrough here](https://youtu.be/hpePPqKxNq8). + +The app will create a Pinecone index for you automatically when you run it for the first time. Just pick a name for your index and set it as an environment variable. + +**Environment Variables:** + +| Name | Required | Description | +| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `DATASTORE` | Yes | Datastore name, set this to `pinecone` | +| `BEARER_TOKEN` | Yes | Your secret token for authenticating requests to the API | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key for generating embeddings with the `text-embedding-ada-002` model | +| `PINECONE_API_KEY` | Yes | Your Pinecone API key, found in the [Pinecone console](https://app.pinecone.io/) | +| `PINECONE_ENVIRONMENT` | Yes | Your Pinecone environment, found in the [Pinecone console](https://app.pinecone.io/), e.g. `us-west1-gcp`, `us-east-1-aws`, etc. | +| `PINECONE_INDEX` | Yes | Your chosen Pinecone index name. **Note:** Index name must consist of lower case alphanumeric characters or '-' | + +If you want to create your own index with custom configurations, you can do so using the Pinecone SDK, API, or web interface ([see docs](https://docs.pinecone.io/docs/manage-indexes)). Make sure to use a dimensionality of 1536 for the embeddings and avoid indexing on the text field in the metadata, as this will reduce the performance significantly. + +```python +# Creating index with Pinecone SDK - use only if you wish to create the index manually. + +import os, pinecone + +pinecone.init(api_key=os.environ['PINECONE_API_KEY'], + environment=os.environ['PINECONE_ENVIRONMENT']) + +pinecone.create_index(name=os.environ['PINECONE_INDEX'], + dimension=1536, + metric='cosine', + metadata_config={ + "indexed": ['source', 'source_id', 'url', 'created_at', 'author', 'document_id']}) +``` diff --git a/retrieval/docs/providers/postgres/setup.md b/retrieval/docs/providers/postgres/setup.md new file mode 100644 index 0000000..25c64e4 --- /dev/null +++ b/retrieval/docs/providers/postgres/setup.md @@ -0,0 +1,81 @@ +# Postgres + +Postgres Database offers an easy and efficient way to store vectors via [pgvector](https://github.com/pgvector/pgvector) extension. To use pgvector, you will need to set up a PostgreSQL database with the pgvector extension enabled or use a managed solution that provides pgvector. For a hosted/managed solution, you can use any of the cloud vendors which support [pgvector](https://github.com/pgvector/pgvector#hosted-postgres). + +- The database needs the `pgvector` extension. +- To apply required migrations you may use any tool you are more familiar with like [pgAdmin](https://www.pgadmin.org/), [DBeaver](https://dbeaver.io/), [DataGrip](https://www.jetbrains.com/datagrip/), or `psql` cli. + +**Retrieval App Environment Variables** + +| Name | Required | Description | +| ---------------- | -------- | -------------------------------------- | +| `DATASTORE` | Yes | Datastore name. Set this to `postgres` | +| `BEARER_TOKEN` | Yes | Your secret token | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | + +**Postgres Datastore Environment Variables** + +| Name | Required | Description | Default | +| ------------- | -------- | ----------------- | ---------- | +| `PG_HOST` | Optional | Postgres host | localhost | +| `PG_PORT` | Optional | Postgres port | `5432` | +| `PG_PASSWORD` | Optional | Postgres password | `postgres` | +| `PG_USER` | Optional | Postgres username | `postgres` | +| `PG_DB` | Optional | Postgres database | `postgres` | + +## Postgres Datastore local development & testing + +In order to test your changes to the Postgres Datastore, you can run the following: + +1. You can run local or self-hosted instance of PostgreSQL with `pgvector` enabled using Docker. + +```bash +docker pull ankane/pgvector +``` + +```bash +docker run --name pgvector -e POSTGRES_PASSWORD=mysecretpassword -d postgres +``` + +Check PostgreSQL [official docker image](https://github.com/docker-library/docs/blob/master/postgres/README.md) for more options. + +2. Apply migrations using any tool you like most [pgAdmin](https://www.pgadmin.org/), [DBeaver](https://dbeaver.io/), [DataGrip](https://www.jetbrains.com/datagrip/), or `psql` cli. + +```bash +# apply migrations using psql cli +psql -h localhost -p 5432 -U postgres -d postgres -f examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql +``` + +3. Export environment variables required for the Postgres Datastore + +```bash +export PG_HOST=localhost +export PG_PORT=54322 +export PG_PASSWORD=mysecretpassword +``` + +4. Run the Postgres datastore tests from the project's root directory + +```bash +# Run the Postgres datastore tests +# go to project's root directory and run +poetry run pytest -s ./tests/datastore/providers/postgres/test_postgres_datastore.py +``` + +5. When going to prod don't forget to set the password for the `postgres` user to something more secure and apply migrations. + +6. You may want to remove RLS (Row Level Security) from the `documents` table. If you are not using RLS, it is not required in this setup. But it may be useful if you want to separate documents by user or group of users, or if you want to give permissions to insert or query documents to different users. And RLS is especially important if you are willing to use PostgREST. To do so you can just remove the following statement from the `20230414142107_init_pg_vector.sql` migration file: `alter table documents enable row level security;`. + +## Indexes for Postgres + +By default, pgvector performs exact nearest neighbor search. To speed up the vector comparison, you may want to create indexes for the `embedding` column in the `documents` table. You should do this **only** after a few thousand records are inserted. + +As datasotre is using inner product for similarity search, you can add index as follows: + +```sql +create index on documents using ivfflat (embedding vector_ip_ops) with (lists = 100); +``` + +To choose `lists` constant - a good place to start is records / 1000 for up to 1M records and sqrt(records) for over 1M records + +For more information about indexes, see [pgvector docs](https://github.com/pgvector/pgvector#indexing). diff --git a/retrieval/docs/providers/qdrant/setup.md b/retrieval/docs/providers/qdrant/setup.md new file mode 100644 index 0000000..3fb0ef6 --- /dev/null +++ b/retrieval/docs/providers/qdrant/setup.md @@ -0,0 +1,58 @@ +# Qdrant + +[Qdrant](https://qdrant.tech/) is a vector database that can store documents and vector embeddings. It can run as a self-hosted version or a managed [Qdrant Cloud](https://cloud.qdrant.io/) +solution. The configuration is almost identical for both options, except for the API key that [Qdrant Cloud](https://cloud.qdrant.io/) provides. + +**Environment Variables:** + +| Name | Required | Description | Default | +| ------------------- | -------- | ----------------------------------------------------------- | ------------------ | +| `DATASTORE` | Yes | Datastore name, set to `qdrant` | | +| `BEARER_TOKEN` | Yes | Secret token | | +| `OPENAI_API_KEY` | Yes | OpenAI API key | | +| `QDRANT_URL` | Yes | Qdrant instance URL | `http://localhost` | +| `QDRANT_PORT` | Optional | TCP port for Qdrant HTTP communication | `6333` | +| `QDRANT_GRPC_PORT` | Optional | TCP port for Qdrant GRPC communication | `6334` | +| `QDRANT_API_KEY` | Optional | Qdrant API key for [Qdrant Cloud](https://cloud.qdrant.io/) | | +| `QDRANT_COLLECTION` | Optional | Qdrant collection name | `document_chunks` | + +## Qdrant Cloud + +For a hosted [Qdrant Cloud](https://cloud.qdrant.io/) version, provide the Qdrant instance +URL and the API key from the [Qdrant Cloud UI](https://cloud.qdrant.io/). + +**Example:** + +```bash +QDRANT_URL="https://YOUR-CLUSTER-URL.aws.cloud.qdrant.io" +QDRANT_API_KEY="" +``` + +The other parameters are optional and can be changed if needed. + +## Self-hosted Qdrant Instance + +For a self-hosted version, use Docker containers or the official Helm chart for deployment. The only +required parameter is the `QDRANT_URL` that points to the Qdrant server URL. + +**Example:** + +```bash +QDRANT_URL="http://YOUR_HOST.example.com:6333" +``` + +The other parameters are optional and can be changed if needed. + +## Running Qdrant Integration Tests + +A suite of integration tests verifies the Qdrant integration. To run it, start a local Qdrant instance in a Docker container. + +```bash +docker run -p "6333:6333" -p "6334:6334" qdrant/qdrant:v1.0.3 +``` + +Then, launch the test suite with this command: + +```bash +pytest ./tests/datastore/providers/qdrant/test_qdrant_datastore.py +``` diff --git a/retrieval/docs/providers/redis/setup.md b/retrieval/docs/providers/redis/setup.md new file mode 100644 index 0000000..37f9939 --- /dev/null +++ b/retrieval/docs/providers/redis/setup.md @@ -0,0 +1,37 @@ +# Redis + +[Redis](https://redis.com/solutions/use-cases/vector-database/) is a real-time data platform that supports a variety of use cases for everyday applications as well as AI/ML workloads. Use Redis as a low-latency vector engine by creating a Redis database with the [Redis Stack docker container](/examples/docker/redis/docker-compose.yml). For a hosted/managed solution, try [Redis Cloud](https://app.redislabs.com/#/). See more helpful examples of Redis as a vector database [here](https://github.com/RedisVentures/redis-ai-resources). + +- The database **needs the RediSearch module (>=v2.6) and RedisJSON**, which are included in the self-hosted docker compose above. +- Run the App with the Redis docker image: `docker compose up -d` in [this dir](/examples/docker/redis/). +- The app automatically creates a Redis vector search index on the first run. Optionally, create a custom index with a specific name and set it as an environment variable (see below). +- To enable more hybrid searching capabilities, adjust the document schema [here](/datastore/providers/redis_datastore.py). + +**Environment Variables:** + +| Name | Required | Description | Default | +| ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | ----------- | +| `DATASTORE` | Yes | Datastore name, set to `redis` | | +| `BEARER_TOKEN` | Yes | Secret token | | +| `OPENAI_API_KEY` | Yes | OpenAI API key | | +| `REDIS_HOST` | Optional | Redis host url | `localhost` | +| `REDIS_PORT` | Optional | Redis port | `6379` | +| `REDIS_PASSWORD` | Optional | Redis password | none | +| `REDIS_INDEX_NAME` | Optional | Redis vector index name | `index` | +| `REDIS_DOC_PREFIX` | Optional | Redis key prefix for the index | `doc` | +| `REDIS_DISTANCE_METRIC` | Optional | Vector similarity distance metric | `COSINE` | +| `REDIS_INDEX_TYPE` | Optional | [Vector index algorithm type](https://redis.io/docs/stack/search/reference/vectors/#creation-attributes-per-algorithm) | `FLAT` | + + +## Redis Datastore development & testing +In order to test your changes to the Redis Datastore, you can run the following commands: + +```bash +# Run the Redis stack docker image +docker run -it --rm -p 6379:6379 redis/redis-stack-server:latest +``` + +```bash +# Run the Redis datastore tests +poetry run pytest -s ./tests/datastore/providers/redis/test_redis_datastore.py +``` \ No newline at end of file diff --git a/retrieval/docs/providers/supabase/setup.md b/retrieval/docs/providers/supabase/setup.md new file mode 100644 index 0000000..8d2f05a --- /dev/null +++ b/retrieval/docs/providers/supabase/setup.md @@ -0,0 +1,87 @@ +# Supabase + +[Supabase](https://supabase.com/blog/openai-embeddings-postgres-vector) offers an easy and efficient way to store vectors via [pgvector](https://github.com/pgvector/pgvector) extension for Postgres Database. [You can use Supabase CLI](https://github.com/supabase/cli) to set up a whole Supabase stack locally or in the cloud or you can also use docker-compose, k8s and other options available. For a hosted/managed solution, try [Supabase.com](https://supabase.com/) and unlock the full power of Postgres with built-in authentication, storage, auto APIs, and Realtime features. See more helpful examples of Supabase & pgvector as a vector database [here](https://github.com/supabase-community/nextjs-openai-doc-search). + +- The database needs the `pgvector` extension, which is included in [Supabase distribution of Postgres](https://github.com/supabase/postgres). +- It is possible to provide a Postgres connection string and an app will add `documents` table, query Postgres function, and `pgvector` extension automatically. +- But it is recommended to separate the migration process from an app. And execute the migration script in a different pipeline by using SQL statements from `_init_db()` function in [Supabase datastore provider](/datastore/providers/supabase_datastore.py). + +**Retrieval App Environment Variables** + +| Name | Required | Description | +| ---------------- | -------- | -------------------------------------- | +| `DATASTORE` | Yes | Datastore name. Set this to `supabase` | +| `BEARER_TOKEN` | Yes | Your secret token | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | + +**Supabase Datastore Environment Variables** + +| Name | Required | Description | Default | +| --------------------------- | -------- | ------------------------------------------------------------------------------ | ------- | +| `SUPABASE_URL` | Yes | Supabase Project URL | | +| `SUPABASE_ANON_KEY` | Optional | Supabase Project API anon key | | +| `SUPABASE_SERVICE_ROLE_KEY` | Optional | Supabase Project API service key, will be used if provided instead of anon key | | + +## Supabase Datastore local development & testing + +In order to test your changes to the Supabase Datastore, you can run the following commands: + +1. Install [Supabase CLI](https://github.com/supabase/cli) and [Docker](https://docs.docker.com/get-docker/) + +2. Run the Supabase `start` command from `examples/providers` directory. Config for Supabase local setup is available in `examples/providers/supabase` directory with required migrations. + +```bash +# Run the Supabase stack using cli in docker +# go to examples/providers and run supabase start +cd examples/providers +supabase start +``` + +3. Supabase `start` will download docker images and launch Supabase stack locally. You will see similar output: + +```bash +Applying migration 20230414142107_init_pg_vector.sql... +Seeding data supabase/seed.sql... +Started supabase local development setup. + + API URL: http://localhost:54321 + DB URL: postgresql://postgres:postgres@localhost:54322/postgres + Studio URL: http://localhost:54323 + Inbucket URL: http://localhost:54324 + JWT secret: super-secret-jwt-token-with-at-least-32-characters-long + anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 +service_role key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU +``` + +4. Export environment variables required for the Supabase Datastore + +```bash +export SUPABASE_URL=http://localhost:54321 +export SUPABASE_SERVICE_ROLE_KEY='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU' +``` + +5. Run the Supabase datastore tests from the project's root directory + +```bash +# Run the Supabase datastore tests +# go to project's root directory and run +poetry run pytest -s ./tests/datastore/providers/supabase/test_supabase_datastore.py +``` + +6. When you go to prod (if cloud hosted) it is recommended to link your supabase project with the local setup from `examples/providers/supabase`. All migrations will be synced with the cloud project after you run `supabase db push`. Or you can manually apply migrations from `examples/providers/supabase/migrations` directory. + +7. You might want to add RLS policies to the `documents` table. Or you can just continue using it on the server side only with the service role key. But you should not use service role key on the client side in any case. + +## Indexes for Postgres + +By default, pgvector performs exact nearest neighbor search. To speed up the vector comparison, you may want to create indexes for the `embedding` column in the `documents` table. You should do this **only** after a few thousand records are inserted. + +As datasotre is using inner product for similarity search, you can add index as follows: + +```sql +create index on documents using ivfflat (embedding vector_ip_ops) with (lists = 100); +``` + +To choose `lists` constant - a good place to start is records / 1000 for up to 1M records and sqrt(records) for over 1M records + +For more information about indexes, see [pgvector docs](https://github.com/pgvector/pgvector#indexing). diff --git a/retrieval/docs/providers/weaviate/setup.md b/retrieval/docs/providers/weaviate/setup.md new file mode 100644 index 0000000..3191632 --- /dev/null +++ b/retrieval/docs/providers/weaviate/setup.md @@ -0,0 +1,79 @@ +# Weaviate + +## Set up a Weaviate Instance + +[Weaviate](https://weaviate.io/) is an open-source vector search engine designed to scale seamlessly into billions of data objects. This implementation supports hybrid search out-of-the-box (meaning it will perform better for keyword searches). + +You can run Weaviate in 4 ways: + +- **SaaS** – with [Weaviate Cloud Services (WCS)](https://weaviate.io/pricing). + + WCS is a fully managed service that takes care of hosting, scaling, and updating your Weaviate instance. You can try it out for free with a sandbox that lasts for 30 days. + + To set up a SaaS Weaviate instance with WCS: + + 1. Navigate to [Weaviate Cloud Console](https://console.weaviate.io/). + 2. Register or sign in to your WCS account. + 3. Create a new cluster with the following settings: + - `Name` – a unique name for your cluster. The name will become part of the URL used to access this instance. + - `Subscription Tier` – Sandbox for a free trial, or contact [hello@weaviate.io](mailto:hello@weaviate.io) for other options. + - `Weaviate Version` - The latest version by default. + - `OIDC Authentication` – Enabled by default. This requires a username and password to access your instance. + 4. Wait for a few minutes until your cluster is ready. You will see a green tick ✔️ when it's done. Copy your cluster URL. + +- **Hybrid SaaS** + + > If you need to keep your data on-premise for security or compliance reasons, Weaviate also offers a Hybrid SaaS option: Weaviate runs within your cloud instances, but the cluster is managed remotely by Weaviate. This gives you the benefits of a managed service without sending data to an external party. + + The Weaviate Hybrid SaaS is a custom solution. If you are interested in this option, please reach out to [hello@weaviate.io](mailto:hello@weaviate.io). + +- **Self-hosted** – with a Docker container + + To set up a Weaviate instance with Docker: + + 1. [Install Docker](https://docs.docker.com/engine/install/) on your local machine if it is not already installed. + 2. [Install the Docker Compose Plugin](https://docs.docker.com/compose/install/) + 3. Download a `docker-compose.yml` file with this `curl` command: + + ``` + curl -o docker-compose.yml "https://configuration.weaviate.io/v2/docker-compose/docker-compose.yml?modules=standalone&runtime=docker-compose&weaviate_version=v1.18.0" + ``` + + Alternatively, you can use Weaviate's docker compose [configuration tool](https://weaviate.io/developers/weaviate/installation/docker-compose) to generate your own `docker-compose.yml` file. + + 4. Run `docker compose up -d` to spin up a Weaviate instance. + + > To shut it down, run `docker compose down`. + +- **Self-hosted** – with a Kubernetes cluster + + To configure a self-hosted instance with Kubernetes, follow Weaviate's [documentation](https://weaviate.io/developers/weaviate/installation/kubernetes). + +## Configure Weaviate Environment Variables + +You need to set some environment variables to connect to your Weaviate instance. + +**Retrieval App Environment Variables** + +| Name | Required | Description | +| ---------------- | -------- |--------------------------------------------------------------------------------------| +| `DATASTORE` | Yes | Datastore name. Set this to `weaviate` | +| `BEARER_TOKEN` | Yes | Your [secret token](/README.md#general-environment-variables) (not the Weaviate one) | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | + +**Weaviate Datastore Environment Variables** + +| Name | Required | Description | Default | +|------------------| -------- | ------------------------------------------------------------------ | ------------------ | +| `WEAVIATE_URL` | Optional | Your weaviate instance's url/WCS endpoint | `http://localhost:8080` | | +| `WEAVIATE_CLASS` | Optional | Your chosen Weaviate class/collection name to store your documents | OpenAIDocument | + +**Weaviate Auth Environment Variables** + +If using WCS instances, set the following environment variables: + +| Name | Required | Description | +| ------------------- | -------- | ------------------------------ | +| `WEAVIATE_API_KEY` | Yes | Your API key WCS | + +Learn more about accessing your [WCS API key](https://weaviate.io/developers/wcs/guides/authentication#access-api-keys). \ No newline at end of file diff --git a/retrieval/docs/providers/zilliz/setup.md b/retrieval/docs/providers/zilliz/setup.md new file mode 100644 index 0000000..fddde3a --- /dev/null +++ b/retrieval/docs/providers/zilliz/setup.md @@ -0,0 +1,46 @@ +# Zilliz + +[Zilliz](https://zilliz.com) is a managed cloud-native vector database designed for the billion scale. Zilliz offers many key features, such as: + +- Multiple indexing algorithms +- Multiple distance metrics +- Scalar filtering +- Time travel searches +- Rollback and with snapshots +- Full RBAC +- 99.9% uptime +- Separated storage and compute +- Multi-language SDK's + +Find more information [here](https://zilliz.com). + +**Self Hosted vs SaaS** + +Zilliz is a SaaS database, but offers an open-source solution, Milvus. Both options offer fast searches at the billion scale, but Zilliz handles data management for you. It automatically scales compute and storage resources and creates optimal indexes for your data. See the comparison [here](https://zilliz.com/doc/about_zilliz_cloud). + +## Deploying the Database + +Zilliz Cloud is deployable in a few simple steps. First, create an account [here](https://cloud.zilliz.com/signup). Once you have an account set up, follow the guide [here](https://zilliz.com/doc/quick_start) to set up a database and get the parameters needed for this application. + +Environment Variables: + +| Name | Required | Description | +|----------------------------| -------- |------------------------------------------------------------------| +| `DATASTORE` | Yes | Datastore name, set to `zilliz` | +| `BEARER_TOKEN` | Yes | Your secret token | +| `OPENAI_API_KEY` | Yes | Your OpenAI API key | +| `ZILLIZ_COLLECTION` | Optional | Zilliz collection name. Defaults to a random UUID | +| `ZILLIZ_URI` | Yes | URI for the Zilliz instance | +| `ZILLIZ_USER` | Yes | Zilliz username | +| `ZILLIZ_PASSWORD` | Yes | Zilliz password | +| `ZILLIZ_CONSISTENCY_LEVEL` | Optional | Data consistency level for the collection, defaults to `Bounded` | + +## Running Zilliz Integration Tests + +A suite of integration tests is available to verify the Zilliz integration. To run the tests, create a Zilliz database and update the environment variables. + +Then, launch the test suite with this command: + +```bash +pytest ./tests/datastore/providers/zilliz/test_zilliz_datastore.py +``` diff --git a/retrieval/examples/authentication-methods/no-auth/ai-plugin.json b/retrieval/examples/authentication-methods/no-auth/ai-plugin.json new file mode 100644 index 0000000..0248919 --- /dev/null +++ b/retrieval/examples/authentication-methods/no-auth/ai-plugin.json @@ -0,0 +1,18 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth": { + "type": "none" + }, + "api": { + "type": "openapi", + "url": "https://your-app-url.com/.well-known/openapi.yaml" + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" +} + diff --git a/retrieval/examples/authentication-methods/no-auth/main.py b/retrieval/examples/authentication-methods/no-auth/main.py new file mode 100644 index 0000000..961c725 --- /dev/null +++ b/retrieval/examples/authentication-methods/no-auth/main.py @@ -0,0 +1,144 @@ +# This is a version of the main.py file found in ../../../server/main.py without authentication. +# Copy and paste this into the main file at ../../../server/main.py if you choose to use no authentication for your retrieval plugin. +from typing import Optional +import uvicorn +from fastapi import FastAPI, File, Form, HTTPException, Body, UploadFile +from fastapi.staticfiles import StaticFiles +from loguru import logger + +from models.api import ( + DeleteRequest, + DeleteResponse, + QueryRequest, + QueryResponse, + UpsertRequest, + UpsertResponse, +) +from datastore.factory import get_datastore +from services.file import get_document_from_file + +from models.models import DocumentMetadata, Source + + +app = FastAPI() +app.mount("/.well-known", StaticFiles(directory=".well-known"), name="static") + +# Create a sub-application, in order to access just the query endpoints in the OpenAPI schema, found at http://0.0.0.0:8000/sub/openapi.json when the app is running locally +sub_app = FastAPI( + title="Retrieval Plugin API", + description="A retrieval API for querying and filtering documents based on natural language queries and metadata", + version="1.0.0", + servers=[{"url": "https://your-app-url.com"}], +) +app.mount("/sub", sub_app) + + +@app.post( + "/upsert-file", + response_model=UpsertResponse, +) +async def upsert_file( + file: UploadFile = File(...), + metadata: Optional[str] = Form(None), +): + try: + metadata_obj = ( + DocumentMetadata.parse_raw(metadata) + if metadata + else DocumentMetadata(source=Source.file) + ) + except: + metadata_obj = DocumentMetadata(source=Source.file) + + document = await get_document_from_file(file, metadata_obj) + + try: + ids = await datastore.upsert([document]) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail=f"str({e})") + + +@app.post( + "/upsert", + response_model=UpsertResponse, +) +async def upsert( + request: UpsertRequest = Body(...), +): + try: + ids = await datastore.upsert(request.documents) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.post( + "/query", + response_model=QueryResponse, +) +async def query_main( + request: QueryRequest = Body(...), +): + try: + results = await datastore.query( + request.queries, + ) + return QueryResponse(results=results) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@sub_app.post( + "/query", + response_model=QueryResponse, + description="Accepts search query objects with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs.", +) +async def query( + request: QueryRequest = Body(...), +): + try: + results = await datastore.query( + request.queries, + ) + return QueryResponse(results=results) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.delete( + "/delete", + response_model=DeleteResponse, +) +async def delete( + request: DeleteRequest = Body(...), +): + if not (request.ids or request.filter or request.delete_all): + raise HTTPException( + status_code=400, + detail="One of ids, filter, or delete_all is required", + ) + try: + success = await datastore.delete( + ids=request.ids, + filter=request.filter, + delete_all=request.delete_all, + ) + return DeleteResponse(success=success) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.on_event("startup") +async def startup(): + global datastore + datastore = await get_datastore() + + +def start(): + uvicorn.run("server.main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/retrieval/examples/authentication-methods/oauth/ai-plugin.json b/retrieval/examples/authentication-methods/oauth/ai-plugin.json new file mode 100644 index 0000000..e98e7aa --- /dev/null +++ b/retrieval/examples/authentication-methods/oauth/ai-plugin.json @@ -0,0 +1,25 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth" : { + "type":"oauth", + "client_url":"e.g. https:///oauth/v2/authorize", + "authorization_url":"e.g. https:///api/oauth.v2.access", + "scope":"search:read", + "authorization_content_type":"application/x-www-form-urlencoded", + "verification_tokens":{ + "openai":"" + } + }, + "api":{ + "url": "https://your-app-url.com/.well-known/openapi.yaml", + "has_user_authentication":true, + "type":"openapi" + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" +} diff --git a/retrieval/examples/authentication-methods/service-http/ai-plugin.json b/retrieval/examples/authentication-methods/service-http/ai-plugin.json new file mode 100644 index 0000000..4bf89ba --- /dev/null +++ b/retrieval/examples/authentication-methods/service-http/ai-plugin.json @@ -0,0 +1,22 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth":{ + "type":"service_http", + "authorization_type":"bearer", + "verification_tokens":{ + "openai":"" + } + }, + "api":{ + "url": "https://your-app-url.com/.well-known/openapi.yaml", + "has_user_authentication":false, + "type":"openapi" + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" +} diff --git a/retrieval/examples/authentication-methods/user-http/ai-plugin.json b/retrieval/examples/authentication-methods/user-http/ai-plugin.json new file mode 100644 index 0000000..1c4e501 --- /dev/null +++ b/retrieval/examples/authentication-methods/user-http/ai-plugin.json @@ -0,0 +1,19 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth": { + "type": "user_http", + "authorization_type": "bearer" + }, + "api": { + "type": "openapi", + "url": "https://your-app-url.com/.well-known/openapi.yaml", + "has_user_authentication": false + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" +} \ No newline at end of file diff --git a/retrieval/examples/docker/milvus/docker-compose.yaml b/retrieval/examples/docker/milvus/docker-compose.yaml new file mode 100644 index 0000000..655069c --- /dev/null +++ b/retrieval/examples/docker/milvus/docker-compose.yaml @@ -0,0 +1,49 @@ +version: '3.5' + +services: + etcd: + container_name: milvus-etcd + image: quay.io/coreos/etcd:v3.5.0 + environment: + - ETCD_AUTO_COMPACTION_MODE=revision + - ETCD_AUTO_COMPACTION_RETENTION=1000 + - ETCD_QUOTA_BACKEND_BYTES=4294967296 + - ETCD_SNAPSHOT_COUNT=50000 + volumes: + - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd + command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd + + minio: + container_name: milvus-minio + image: minio/minio:RELEASE.2023-03-20T20-16-18Z + environment: + MINIO_ACCESS_KEY: minioadmin + MINIO_SECRET_KEY: minioadmin + volumes: + - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data + command: minio server /minio_data + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + standalone: + container_name: milvus-standalone + image: milvusdb/milvus:v2.2.5 + command: ["milvus", "run", "standalone"] + environment: + ETCD_ENDPOINTS: etcd:2379 + MINIO_ADDRESS: minio:9000 + volumes: + - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus + ports: + - "19530:19530" + - "9091:9091" + depends_on: + - "etcd" + - "minio" + +networks: + default: + name: milvus \ No newline at end of file diff --git a/retrieval/examples/docker/qdrant/README.md b/retrieval/examples/docker/qdrant/README.md new file mode 100644 index 0000000..6d00b25 --- /dev/null +++ b/retrieval/examples/docker/qdrant/README.md @@ -0,0 +1,46 @@ +# Running the Retrieval Plugin with Qdrant in Docker Containers + +To set up the ChatGPT retrieval plugin with a single instance of a Qdrant vector database, follow these steps: + +## Set Environment Variables + +Set the following environment variables: + +```bash +# Provide your own OpenAI API key in order to start. +export OPENAI_API_KEY="" +# This is an example of a minimal token generated by https://jwt.io/ +export BEARER_TOKEN="" +``` + +## Run Qdrant and the Retrieval Plugin in Docker Containers + +Both Docker containers might be launched with docker-compose: + +```bash +docker-compose up -d +``` + +## Store the Documents + +Store an initial batch of documents by calling the `/upsert` endpoint: + +```bash +curl -X POST \ + -H "Content-type: application/json" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + --data-binary '@documents.json' \ + "http://localhost:80/upsert" +``` + +## Send a Test Query + +You can query Qdrant to find relevant document chunks by calling the `/query` endpoint: + +```bash +curl -X POST \ + -H "Content-type: application/json" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + --data-binary '@queries.json' \ + "http://localhost:80/query" +``` diff --git a/retrieval/examples/docker/qdrant/docker-compose.yaml b/retrieval/examples/docker/qdrant/docker-compose.yaml new file mode 100644 index 0000000..ead9fd6 --- /dev/null +++ b/retrieval/examples/docker/qdrant/docker-compose.yaml @@ -0,0 +1,17 @@ +services: + retrieval-app: + build: + context: ../../../ + dockerfile: Dockerfile + image: openai/chatgpt-retrieval-plugin + ports: + - "80:80" + depends_on: + - qdrant + environment: + DATASTORE: "qdrant" + QDRANT_URL: "http://qdrant" + BEARER_TOKEN: "${BEARER_TOKEN}" + OPENAI_API_KEY: "${OPENAI_API_KEY}" + qdrant: + image: qdrant/qdrant:v1.0.3 \ No newline at end of file diff --git a/retrieval/examples/docker/qdrant/documents.json b/retrieval/examples/docker/qdrant/documents.json new file mode 100644 index 0000000..7dc6572 --- /dev/null +++ b/retrieval/examples/docker/qdrant/documents.json @@ -0,0 +1,23 @@ +{ + "documents": [ + { + "id": "openai", + "text": "OpenAI is an AI research and deployment company. Our mission is to ensure that artificial general intelligence benefits all of humanity.", + "metadata": { + "created_at": "2023-03-14" + } + }, + { + "id": "chatgpt", + "text": "ChatGPT is a sibling model to InstructGPT, which is trained to follow an instruction in a prompt and provide a detailed response. The dialogue format makes it possible for ChatGPT to answer followup questions, admit its mistakes, challenge incorrect premises, and reject inappropriate requests." + }, + { + "id": "qdrant", + "text": "Qdrant is a vector similarity engine & vector database. It deploys as an API service providing search for the nearest high-dimensional vectors. With Qdrant, embeddings or neural network encoders can be turned into full-fledged applications for matching, searching, recommending, and much more!", + "metadata": { + "created_at": "2023-03-14", + "author": "Kacper Łukawski" + } + } + ] +} \ No newline at end of file diff --git a/retrieval/examples/docker/qdrant/queries.json b/retrieval/examples/docker/qdrant/queries.json new file mode 100644 index 0000000..6d967e2 --- /dev/null +++ b/retrieval/examples/docker/qdrant/queries.json @@ -0,0 +1,7 @@ +{ + "queries": [ + { + "query": "What vector database should I use?" + } + ] +} \ No newline at end of file diff --git a/retrieval/examples/docker/redis/docker-compose.yml b/retrieval/examples/docker/redis/docker-compose.yml new file mode 100644 index 0000000..b3c197e --- /dev/null +++ b/retrieval/examples/docker/redis/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.9" + +services: + redis: + image: redis/redis-stack-server:latest + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-h", "localhost", "-p", "6379", "ping"] + interval: 2s + timeout: 1m30s + retries: 5 + start_period: 5s + +volumes: + redis_data: \ No newline at end of file diff --git a/retrieval/examples/memory/README.md b/retrieval/examples/memory/README.md new file mode 100644 index 0000000..ccf2e03 --- /dev/null +++ b/retrieval/examples/memory/README.md @@ -0,0 +1,15 @@ +# ChatGPT Retrieval Plugin with Memory + +This example demonstrates how to give ChatGPT the ability to remember information from conversations and store it in the retrieval plugin for later use. By allowing the model to access the `/upsert` endpoint, it can save snippets from the conversation to the vector database and retrieve them when needed. + +## Setup + +To enable ChatGPT to save information from conversations, follow these steps: + +- Copy the contents of [openapi.yaml](openapi.yaml) into the main [openapi.yaml](../../.well-known/openapi.yaml) file. + +- Copy the contents of [ai-plugin.json](ai-plugin.json) into the main [ai-plugin.json](../../.well-known/ai-plugin.json) file. + +**Optional:** If you make any changes to the plugin instructions or metadata models, you can also copy the contents of [main.py](main.py) into the main [main.py](../../server/main.py) file. This will allow you to access the openapi.json at `http://0.0.0.0:8000/sub/openapi.json` when you run the app locally. You can convert from JSON to YAML format with [Swagger Editor](https://editor.swagger.io/). Alternatively, you can replace the openapi.yaml file with an openapi.json file. + +After completing these steps, ChatGPT will be able to access your plugin's `/upsert` endpoint and save snippets from the conversation to the vector database. This enables the model to remember information from previous conversations and retrieve it when needed. diff --git a/retrieval/examples/memory/ai-plugin.json b/retrieval/examples/memory/ai-plugin.json new file mode 100644 index 0000000..953fff5 --- /dev/null +++ b/retrieval/examples/memory/ai-plugin.json @@ -0,0 +1,19 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information, or asks you to save information for later.", + "description_for_human": "Search through your documents.", + "auth": { + "type": "user_http", + "authorization_type": "bearer" + }, + "api": { + "type": "openapi", + "url": "https://your-app-url.com/.well-known/openapi.yaml", + "has_user_authentication": false + }, + "logo_url": "https://your-app-url.com/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" + } \ No newline at end of file diff --git a/retrieval/examples/memory/main.py b/retrieval/examples/memory/main.py new file mode 100644 index 0000000..c94d3f9 --- /dev/null +++ b/retrieval/examples/memory/main.py @@ -0,0 +1,183 @@ +# This is a version of the main.py file found in ../../server/main.py that also gives ChatGPT access to the upsert endpoint +# (allowing it to save information from the chat back to the vector) database. +# Copy and paste this into the main file at ../../server/main.py if you choose to give the model access to the upsert endpoint +# and want to access the openapi.json when you run the app locally at http://0.0.0.0:8000/sub/openapi.json. +import os +from typing import Optional +import uvicorn +from fastapi import FastAPI, File, Form, HTTPException, Depends, Body, UploadFile +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi.staticfiles import StaticFiles +from loguru import logger + +from models.api import ( + DeleteRequest, + DeleteResponse, + QueryRequest, + QueryResponse, + UpsertRequest, + UpsertResponse, +) +from datastore.factory import get_datastore +from services.file import get_document_from_file + +from models.models import DocumentMetadata, Source + + +bearer_scheme = HTTPBearer() +BEARER_TOKEN = os.environ.get("BEARER_TOKEN") +assert BEARER_TOKEN is not None + + +def validate_token(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)): + if credentials.scheme != "Bearer" or credentials.credentials != BEARER_TOKEN: + raise HTTPException(status_code=401, detail="Invalid or missing token") + return credentials + + +app = FastAPI() +app.mount("/.well-known", StaticFiles(directory=".well-known"), name="static") + +# Create a sub-application, in order to access just the upsert and query endpoints in the OpenAPI schema, found at http://0.0.0.0:8000/sub/openapi.json when the app is running locally +sub_app = FastAPI( + title="Retrieval Plugin API", + description="A retrieval API for querying and filtering documents based on natural language queries and metadata", + version="1.0.0", + servers=[{"url": "https://your-app-url.com"}], + dependencies=[Depends(validate_token)], +) +app.mount("/sub", sub_app) + + +@app.post( + "/upsert-file", + response_model=UpsertResponse, +) +async def upsert_file( + file: UploadFile = File(...), + metadata: Optional[str] = Form(None), +): + try: + metadata_obj = ( + DocumentMetadata.parse_raw(metadata) + if metadata + else DocumentMetadata(source=Source.file) + ) + except: + metadata_obj = DocumentMetadata(source=Source.file) + + document = await get_document_from_file(file, metadata_obj) + + try: + ids = await datastore.upsert([document]) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail=f"str({e})") + + +@app.post( + "/upsert", + response_model=UpsertResponse, +) +async def upsert_main( + request: UpsertRequest = Body(...), + token: HTTPAuthorizationCredentials = Depends(validate_token), +): + try: + ids = await datastore.upsert(request.documents) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@sub_app.post( + "/upsert", + response_model=UpsertResponse, + # NOTE: We are describing the shape of the API endpoint input due to a current limitation in parsing arrays of objects from OpenAPI schemas. This will not be necessary in the future. + description="Save chat information. Accepts an array of documents with text (potential questions + conversation text), metadata (source 'chat' and timestamp, no ID as this will be generated). Confirm with the user before saving, ask for more details/context.", +) +async def upsert( + request: UpsertRequest = Body(...), + token: HTTPAuthorizationCredentials = Depends(validate_token), +): + try: + ids = await datastore.upsert(request.documents) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.post( + "/query", + response_model=QueryResponse, +) +async def query_main( + request: QueryRequest = Body(...), + token: HTTPAuthorizationCredentials = Depends(validate_token), +): + try: + results = await datastore.query( + request.queries, + ) + return QueryResponse(results=results) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@sub_app.post( + "/query", + response_model=QueryResponse, + # NOTE: We are describing the shape of the API endpoint input due to a current limitation in parsing arrays of objects from OpenAPI schemas. This will not be necessary in the future. + description="Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs.", +) +async def query( + request: QueryRequest = Body(...), + token: HTTPAuthorizationCredentials = Depends(validate_token), +): + try: + results = await datastore.query( + request.queries, + ) + return QueryResponse(results=results) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.delete( + "/delete", + response_model=DeleteResponse, +) +async def delete( + request: DeleteRequest = Body(...), + token: HTTPAuthorizationCredentials = Depends(validate_token), +): + if not (request.ids or request.filter or request.delete_all): + raise HTTPException( + status_code=400, + detail="One of ids, filter, or delete_all is required", + ) + try: + success = await datastore.delete( + ids=request.ids, + filter=request.filter, + delete_all=request.delete_all, + ) + return DeleteResponse(success=success) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail="Internal Service Error") + + +@app.on_event("startup") +async def startup(): + global datastore + datastore = await get_datastore() + + +def start(): + uvicorn.run("server.main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/retrieval/examples/memory/openapi.yaml b/retrieval/examples/memory/openapi.yaml new file mode 100644 index 0000000..43b3db9 --- /dev/null +++ b/retrieval/examples/memory/openapi.yaml @@ -0,0 +1,276 @@ +openapi: 3.0.2 +info: + title: Retrieval Plugin API + description: A retrieval API for querying and filtering documents based on natural language queries and metadata + version: 1.0.0 +servers: + - url: https://your-app-url.com +paths: + /upsert: + post: + summary: Upsert + description: Save chat information. Accepts an array of documents with text (potential questions + conversation text), metadata (source 'chat' and timestamp, no ID as this will be generated). Confirm with the user before saving, ask for more details/context. + operationId: upsert_upsert_post + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpsertRequest" + required: true + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/UpsertResponse" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/HTTPValidationError" + security: + - HTTPBearer: [] + /query: + post: + summary: Query + description: Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs. + operationId: query_query_post + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/QueryRequest" + required: true + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/QueryResponse" + "422": + description: Validation Error + content: + application/json: + schema: + $ref: "#/components/schemas/HTTPValidationError" + security: + - HTTPBearer: [] +components: + schemas: + Document: + title: Document + required: + - text + type: object + properties: + id: + title: Id + type: string + text: + title: Text + type: string + metadata: + $ref: "#/components/schemas/DocumentMetadata" + DocumentChunkMetadata: + title: DocumentChunkMetadata + type: object + properties: + source: + $ref: "#/components/schemas/Source" + source_id: + title: Source Id + type: string + url: + title: Url + type: string + created_at: + title: Created At + type: string + author: + title: Author + type: string + document_id: + title: Document Id + type: string + DocumentChunkWithScore: + title: DocumentChunkWithScore + required: + - text + - metadata + - score + type: object + properties: + id: + title: Id + type: string + text: + title: Text + type: string + metadata: + $ref: "#/components/schemas/DocumentChunkMetadata" + embedding: + title: Embedding + type: array + items: + type: number + score: + title: Score + type: number + DocumentMetadata: + title: DocumentMetadata + type: object + properties: + source: + $ref: "#/components/schemas/Source" + source_id: + title: Source Id + type: string + url: + title: Url + type: string + created_at: + title: Created At + type: string + author: + title: Author + type: string + DocumentMetadataFilter: + title: DocumentMetadataFilter + type: object + properties: + document_id: + title: Document Id + type: string + source: + $ref: "#/components/schemas/Source" + source_id: + title: Source Id + type: string + author: + title: Author + type: string + start_date: + title: Start Date + type: string + end_date: + title: End Date + type: string + HTTPValidationError: + title: HTTPValidationError + type: object + properties: + detail: + title: Detail + type: array + items: + $ref: "#/components/schemas/ValidationError" + Query: + title: Query + required: + - query + type: object + properties: + query: + title: Query + type: string + filter: + $ref: "#/components/schemas/DocumentMetadataFilter" + top_k: + title: Top K + type: integer + default: 3 + QueryRequest: + title: QueryRequest + required: + - queries + type: object + properties: + queries: + title: Queries + type: array + items: + $ref: "#/components/schemas/Query" + QueryResponse: + title: QueryResponse + required: + - results + type: object + properties: + results: + title: Results + type: array + items: + $ref: "#/components/schemas/QueryResult" + QueryResult: + title: QueryResult + required: + - query + - results + type: object + properties: + query: + title: Query + type: string + results: + title: Results + type: array + items: + $ref: "#/components/schemas/DocumentChunkWithScore" + Source: + title: Source + enum: + - email + - file + - chat + type: string + description: An enumeration. + UpsertRequest: + title: UpsertRequest + required: + - documents + type: object + properties: + documents: + title: Documents + type: array + items: + $ref: "#/components/schemas/Document" + UpsertResponse: + title: UpsertResponse + required: + - ids + type: object + properties: + ids: + title: Ids + type: array + items: + type: string + ValidationError: + title: ValidationError + required: + - loc + - msg + - type + type: object + properties: + loc: + title: Location + type: array + items: + anyOf: + - type: string + - type: integer + msg: + title: Message + type: string + type: + title: Error Type + type: string + securitySchemes: + HTTPBearer: + type: http + scheme: bearer diff --git a/retrieval/examples/providers/pinecone/semantic-search.ipynb b/retrieval/examples/providers/pinecone/semantic-search.ipynb new file mode 100644 index 0000000..de54be1 --- /dev/null +++ b/retrieval/examples/providers/pinecone/semantic-search.ipynb @@ -0,0 +1,809 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the Pinecone Retrieval App\n", + "\n", + "In this walkthrough we will see how to use the retrieval API with a Pinecone datastore for *semantic search / question-answering*.\n", + "\n", + "Before running this notebook you should have already initialized the retrieval API and have it running locally or elsewhere. The full instructions for doing this are found in the [project README]().\n", + "\n", + "We will summarize the instructions (specific to the Pinecone datastore) before moving on to the walkthrough." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## App Quickstart" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. Install Python 3.10 if not already installed.\n", + "\n", + "2. Clone the `retrieval-app` repository:\n", + "\n", + "```\n", + "git clone git@github.com:openai/retrieval-app.git\n", + "```\n", + "\n", + "3. Navigate to the app directory:\n", + "\n", + "```\n", + "cd /path/to/retrieval-app\n", + "```\n", + "\n", + "4. Install `poetry`:\n", + "\n", + "```\n", + "pip install poetry\n", + "```\n", + "\n", + "5. Create a new virtual environment:\n", + "\n", + "```\n", + "poetry env use python3.10\n", + "```\n", + "\n", + "6. Install the `retrieval-app` dependencies:\n", + "\n", + "```\n", + "poetry install\n", + "```\n", + "\n", + "7. Set app environment variables:\n", + "\n", + "* `BEARER_TOKEN`: Secret token used by the app to authorize incoming requests. We will later include this in the request `headers`. The token can be generated however you prefer, such as using [jwt.io](https://jwt.io/).\n", + "\n", + "* `OPENAI_API_KEY`: The OpenAI API key used for generating embeddings with the `text-embedding-ada-002` model. [Get an API key here](https://platform.openai.com/account/api-keys)!\n", + "\n", + "8. Set Pinecone-specific environment variables:\n", + "\n", + "* `DATASTORE`: set to `pinecone`.\n", + "\n", + "* `PINECONE_API_KEY`: Set to your Pinecone API key. This requires a free Pinecone account and can be [found in the Pinecone console](https://app.pinecone.io/).\n", + "\n", + "* `PINECONE_ENVIRONMENT`: Set to your Pinecone environment, looks like `us-east1-gcp`, `us-west1-aws`, and can be found next to your API key in the [Pinecone console](https://app.pinecone.io/).\n", + "\n", + "* `PINECONE_INDEX`: Set this to your chosen index name. The name you choose is your choice, we just recommend setting it to something descriptive like `\"openai-retrieval-app\"`. *Note that index names are restricted to alphanumeric characters, `\"-\"`, and can contain a maximum of 45 characters.*\n", + "\n", + "8. Run the app with:\n", + "\n", + "```\n", + "poetry run start\n", + "```\n", + "\n", + "If running the app locally you should see something like:\n", + "\n", + "```\n", + "INFO: Uvicorn running on http://0.0.0.0:8000\n", + "INFO: Application startup complete.\n", + "```\n", + "\n", + "In that case, the app is automatically connected to our index (specified by `PINECONE_INDEX`), if no index with that name existed beforehand, the app creates one for us.\n", + "\n", + "Now we're ready to move on to populating our index with some data." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Required Libraries" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are a few Python libraries we must `pip install` for this notebook to run, those are:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install -qU datasets pandas tqdm" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preparing Data" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, we will use the **S**tanford **Qu**estion **A**nswering **D**ataset (SQuAD), which we download from Hugging Face Datasets." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Found cached dataset squad (/Users/jamesbriggs/.cache/huggingface/datasets/squad/plain_text/1.0.0/d6ec3ceb99ca480ce37cdd35555d6cb2511d223b9150cce08a837ef62ffea453)\n" + ] + }, + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['id', 'title', 'context', 'question', 'answers'],\n", + " num_rows: 87599\n", + "})" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "data = load_dataset(\"squad\", split=\"train\")\n", + "data" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Convert to Pandas dataframe for easier preprocessing steps." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtitlecontextquestionanswers
05733be284776f41900661182University_of_Notre_DameArchitecturally, the school has a Catholic cha...To whom did the Virgin Mary allegedly appear i...{'text': ['Saint Bernadette Soubirous'], 'answ...
15733be284776f4190066117fUniversity_of_Notre_DameArchitecturally, the school has a Catholic cha...What is in front of the Notre Dame Main Building?{'text': ['a copper statue of Christ'], 'answe...
25733be284776f41900661180University_of_Notre_DameArchitecturally, the school has a Catholic cha...The Basilica of the Sacred heart at Notre Dame...{'text': ['the Main Building'], 'answer_start'...
35733be284776f41900661181University_of_Notre_DameArchitecturally, the school has a Catholic cha...What is the Grotto at Notre Dame?{'text': ['a Marian place of prayer and reflec...
45733be284776f4190066117eUniversity_of_Notre_DameArchitecturally, the school has a Catholic cha...What sits on top of the Main Building at Notre...{'text': ['a golden statue of the Virgin Mary'...
\n", + "
" + ], + "text/plain": [ + " id title \\\n", + "0 5733be284776f41900661182 University_of_Notre_Dame \n", + "1 5733be284776f4190066117f University_of_Notre_Dame \n", + "2 5733be284776f41900661180 University_of_Notre_Dame \n", + "3 5733be284776f41900661181 University_of_Notre_Dame \n", + "4 5733be284776f4190066117e University_of_Notre_Dame \n", + "\n", + " context \\\n", + "0 Architecturally, the school has a Catholic cha... \n", + "1 Architecturally, the school has a Catholic cha... \n", + "2 Architecturally, the school has a Catholic cha... \n", + "3 Architecturally, the school has a Catholic cha... \n", + "4 Architecturally, the school has a Catholic cha... \n", + "\n", + " question \\\n", + "0 To whom did the Virgin Mary allegedly appear i... \n", + "1 What is in front of the Notre Dame Main Building? \n", + "2 The Basilica of the Sacred heart at Notre Dame... \n", + "3 What is the Grotto at Notre Dame? \n", + "4 What sits on top of the Main Building at Notre... \n", + "\n", + " answers \n", + "0 {'text': ['Saint Bernadette Soubirous'], 'answ... \n", + "1 {'text': ['a copper statue of Christ'], 'answe... \n", + "2 {'text': ['the Main Building'], 'answer_start'... \n", + "3 {'text': ['a Marian place of prayer and reflec... \n", + "4 {'text': ['a golden statue of the Virgin Mary'... " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = data.to_pandas()\n", + "data.head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset contains a lot of duplicate `context` paragraphs, this is because each `context` can have many relevant questions. We don't want these duplicates so we remove like so:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "18891\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtitlecontextquestionanswers
05733be284776f41900661182University_of_Notre_DameArchitecturally, the school has a Catholic cha...To whom did the Virgin Mary allegedly appear i...{'text': ['Saint Bernadette Soubirous'], 'answ...
55733bf84d058e614000b61beUniversity_of_Notre_DameAs at most other universities, Notre Dame's st...When did the Scholastic Magazine of Notre dame...{'text': ['September 1876'], 'answer_start': [...
105733bed24776f41900661188University_of_Notre_DameThe university is the major seat of the Congre...Where is the headquarters of the Congregation ...{'text': ['Rome'], 'answer_start': [119]}
155733a6424776f41900660f51University_of_Notre_DameThe College of Engineering was established in ...How many BS level degrees are offered in the C...{'text': ['eight'], 'answer_start': [487]}
205733a70c4776f41900660f64University_of_Notre_DameAll of Notre Dame's undergraduate students are...What entity provides help with the management ...{'text': ['Learning Resource Center'], 'answer...
\n", + "
" + ], + "text/plain": [ + " id title \\\n", + "0 5733be284776f41900661182 University_of_Notre_Dame \n", + "5 5733bf84d058e614000b61be University_of_Notre_Dame \n", + "10 5733bed24776f41900661188 University_of_Notre_Dame \n", + "15 5733a6424776f41900660f51 University_of_Notre_Dame \n", + "20 5733a70c4776f41900660f64 University_of_Notre_Dame \n", + "\n", + " context \\\n", + "0 Architecturally, the school has a Catholic cha... \n", + "5 As at most other universities, Notre Dame's st... \n", + "10 The university is the major seat of the Congre... \n", + "15 The College of Engineering was established in ... \n", + "20 All of Notre Dame's undergraduate students are... \n", + "\n", + " question \\\n", + "0 To whom did the Virgin Mary allegedly appear i... \n", + "5 When did the Scholastic Magazine of Notre dame... \n", + "10 Where is the headquarters of the Congregation ... \n", + "15 How many BS level degrees are offered in the C... \n", + "20 What entity provides help with the management ... \n", + "\n", + " answers \n", + "0 {'text': ['Saint Bernadette Soubirous'], 'answ... \n", + "5 {'text': ['September 1876'], 'answer_start': [... \n", + "10 {'text': ['Rome'], 'answer_start': [119]} \n", + "15 {'text': ['eight'], 'answer_start': [487]} \n", + "20 {'text': ['Learning Resource Center'], 'answer... " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = data.drop_duplicates(subset=[\"context\"])\n", + "print(len(data))\n", + "data.head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The format required by the apps `upsert` function is a list of documents like:\n", + "\n", + "```json\n", + "[\n", + " {\n", + " \"id\": \"abc\",\n", + " \"text\": \"some important document text\",\n", + " \"metadata\": {\n", + " \"field1\": \"optional metadata goes here\",\n", + " \"field2\": 54\n", + " }\n", + " },\n", + " {\n", + " \"id\": \"123\",\n", + " \"text\": \"some other important text\",\n", + " \"metadata\": {\n", + " \"field1\": \"another metadata\",\n", + " \"field2\": 71,\n", + " \"field3\": \"not all metadatas need the same structure\"\n", + " }\n", + " }\n", + " ...\n", + "]\n", + "```\n", + "\n", + "Every document *must* have a `\"text\"` field. The `\"id\"` and `\"metadata\"` fields are optional.\n", + "\n", + "To create this format for our SQuAD data we do:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '5733be284776f41900661182',\n", + " 'text': 'Architecturally, the school has a Catholic character. Atop the Main Building\\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \"Venite Ad Me Omnes\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.',\n", + " 'metadata': {'title': 'University_of_Notre_Dame'}},\n", + " {'id': '5733bf84d058e614000b61be',\n", + " 'text': \"As at most other universities, Notre Dame's students run a number of news media outlets. The nine student-run outlets include three newspapers, both a radio and television station, and several magazines and journals. Begun as a one-page journal in September 1876, the Scholastic magazine is issued twice monthly and claims to be the oldest continuous collegiate publication in the United States. The other magazine, The Juggler, is released twice a year and focuses on student literature and artwork. The Dome yearbook is published annually. The newspapers have varying publication interests, with The Observer published daily and mainly reporting university and other news, and staffed by students from both Notre Dame and Saint Mary's College. Unlike Scholastic and The Dome, The Observer is an independent publication and does not have a faculty advisor or any editorial oversight from the University. In 1987, when some students believed that The Observer began to show a conservative bias, a liberal newspaper, Common Sense was published. Likewise, in 2003, when other students believed that the paper showed a liberal bias, the conservative paper Irish Rover went into production. Neither paper is published as often as The Observer; however, all three are distributed to all students. Finally, in Spring 2008 an undergraduate journal for political science research, Beyond Politics, made its debut.\",\n", + " 'metadata': {'title': 'University_of_Notre_Dame'}},\n", + " {'id': '5733bed24776f41900661188',\n", + " 'text': 'The university is the major seat of the Congregation of Holy Cross (albeit not its official headquarters, which are in Rome). Its main seminary, Moreau Seminary, is located on the campus across St. Joseph lake from the Main Building. Old College, the oldest building on campus and located near the shore of St. Mary lake, houses undergraduate seminarians. Retired priests and brothers reside in Fatima House (a former retreat center), Holy Cross House, as well as Columba Hall near the Grotto. The university through the Moreau Seminary has ties to theologian Frederick Buechner. While not Catholic, Buechner has praised writers from Notre Dame and Moreau Seminary created a Buechner Prize for Preaching.',\n", + " 'metadata': {'title': 'University_of_Notre_Dame'}}]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "documents = [\n", + " {\n", + " 'id': r['id'],\n", + " 'text': r['context'],\n", + " 'metadata': {\n", + " 'title': r['title']\n", + " }\n", + " } for r in data.to_dict(orient='records')\n", + "]\n", + "documents[:3]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Indexing the Docs" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We're now ready to begin indexing (or *upserting*) our `documents`. To make these requests to the retrieval app API, we will need to provide authorization in the form of the `BEARER_TOKEN` we set earlier. We do this below:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "BEARER_TOKEN = os.environ.get(\"BEARER_TOKEN\") or \"BEARER_TOKEN_HERE\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the `BEARER_TOKEN` to create our authorization `headers`:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "headers = {\n", + " \"Authorization\": f\"Bearer {BEARER_TOKEN}\"\n", + "}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll perform the upsert in batches of `batch_size`. Make sure that the `endpoint_url` variable is set to the correct location for your running *retrieval-app* API." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8694da67455d4bb78cc778e49f69a872", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/10 [00:00" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res = requests.post(\n", + " \"http://0.0.0.0:8000/query\",\n", + " headers=headers,\n", + " json={\n", + " 'queries': queries[:3]\n", + " }\n", + ")\n", + "res" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can loop through the responses and see the results returned for each query:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "----------------------------------------------------------------------\n", + "To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?\n", + "\n", + "0.83: Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \"Venite Ad Me Omnes\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.\n", + "0.81: Within the white inescutcheon, the five quinas (small blue shields) with their five white bezants representing the five wounds of Christ (Portuguese: Cinco Chagas) when crucified and are popularly associated with the \"Miracle of Ourique\". The story associated with this miracle tells that before the Battle of Ourique (25 July 1139), an old hermit appeared before Count Afonso Henriques (future Afonso I) as a divine messenger. He foretold Afonso's victory and assured him that God was watching over him and his peers. The messenger advised him to walk away from his camp, alone, if he heard a nearby chapel bell tolling, in the following night. In doing so, he witnessed an apparition of Jesus on the cross. Ecstatic, Afonso heard Jesus promising victories for the coming battles, as well as God's wish to act through Afonso, and his descendants, in order to create an empire which would carry His name to unknown lands, thus choosing the Portuguese to perform great tasks.\n", + "0.79: In 1842, the Bishop of Vincennes, Célestine Guynemer de la Hailandière, offered land to Father Edward Sorin of the Congregation of the Holy Cross, on the condition that he build a college in two years. Fr. Sorin arrived on the site with eight Holy Cross brothers from France and Ireland on November 26, 1842, and began the school using Father Stephen Badin's old log chapel. He soon erected additional buildings, including Old College, the first church, and the first main building. They immediately acquired two students and set about building additions to the campus.\n", + "0.79: Because of its Catholic identity, a number of religious buildings stand on campus. The Old College building has become one of two seminaries on campus run by the Congregation of Holy Cross. The current Basilica of the Sacred Heart is located on the spot of Fr. Sorin's original church, which became too small for the growing college. It is built in French Revival style and it is decorated by stained glass windows imported directly from France. The interior was painted by Luigi Gregori, an Italian painter invited by Fr. Sorin to be artist in residence. The Basilica also features a bell tower with a carillon. Inside the church there are also sculptures by Ivan Mestrovic. The Grotto of Our Lady of Lourdes, which was built in 1896, is a replica of the original in Lourdes, France. It is very popular among students and alumni as a place of prayer and meditation, and it is considered one of the most beloved spots on campus.\n", + "0.78: The funeral, held at the Church of the Madeleine in Paris, was delayed almost two weeks, until 30 October. Entrance was restricted to ticket holders as many people were expected to attend. Over 3,000 people arrived without invitations, from as far as London, Berlin and Vienna, and were excluded.\n", + "----------------------------------------------------------------------\n", + "\n", + "\n", + "----------------------------------------------------------------------\n", + "When did the Scholastic Magazine of Notre dame begin publishing?\n", + "\n", + "0.88: As at most other universities, Notre Dame's students run a number of news media outlets. The nine student-run outlets include three newspapers, both a radio and television station, and several magazines and journals. Begun as a one-page journal in September 1876, the Scholastic magazine is issued twice monthly and claims to be the oldest continuous collegiate publication in the United States. The other magazine, The Juggler, is released twice a year and focuses on student literature and artwork. The Dome yearbook is published annually. The newspapers have varying publication interests, with The Observer published daily and mainly reporting university and other news, and staffed by students from both Notre Dame and Saint Mary's College. Unlike Scholastic and The Dome, The Observer is an independent publication and does not have a faculty advisor or any editorial oversight from the University. In 1987, when some students believed that The Observer began to show a conservative bias, a liberal newspaper, Common Sense was published. Likewise, in 2003, when other students believed that the paper showed a liberal bias, the conservative paper Irish Rover went into production. Neither paper is published as often as The Observer; however, all three are distributed to all students.\n", + "0.83: In 1919 Father James Burns became president of Notre Dame, and in three years he produced an academic revolution that brought the school up to national standards by adopting the elective system and moving away from the university's traditional scholastic and classical emphasis. By contrast, the Jesuit colleges, bastions of academic conservatism, were reluctant to move to a system of electives. Their graduates were shut out of Harvard Law School for that reason. Notre Dame continued to grow over the years, adding more colleges, programs, and sports teams. By 1921, with the addition of the College of Commerce, Notre Dame had grown from a small college to a university with five colleges and a professional law school. The university continued to expand and add new residence halls and buildings with each subsequent president.\n", + "0.83: The rise of Hitler and other dictators in the 1930s forced numerous Catholic intellectuals to flee Europe; president John O'Hara brought many to Notre Dame. From Germany came Anton-Hermann Chroust (1907–1982) in classics and law, and Waldemar Gurian a German Catholic intellectual of Jewish descent. Positivism dominated American intellectual life in the 1920s onward but in marked contrast, Gurian received a German Catholic education and wrote his doctoral dissertation under Max Scheler. Ivan Meštrović (1883–1962), a renowned sculptor, brought Croatian culture to campus, 1955–62. Yves Simon (1903–61), brought to ND in the 1940s the insights of French studies in the Aristotelian-Thomistic tradition of philosophy; his own teacher Jacques Maritain (1882–73) was a frequent visitor to campus.\n", + "0.82: In the 18 years under the presidency of Edward Malloy, C.S.C., (1987–2005), there was a rapid growth in the school's reputation, faculty, and resources. He increased the faculty by more than 500 professors; the academic quality of the student body has improved dramatically, with the average SAT score rising from 1240 to 1360; the number of minority students more than doubled; the endowment grew from $350 million to more than $3 billion; the annual operating budget rose from $177 million to more than $650 million; and annual research funding improved from $15 million to more than $70 million. Notre Dame's most recent[when?] capital campaign raised $1.1 billion, far exceeding its goal of $767 million, and is the largest in the history of Catholic higher education.\n", + "0.82: The Rev. John J. Cavanaugh, C.S.C. served as president from 1946 to 1952. Cavanaugh's legacy at Notre Dame in the post-war years was devoted to raising academic standards and reshaping the university administration to suit it to an enlarged educational mission and an expanded student body and stressing advanced studies and research at a time when Notre Dame quadrupled in student census, undergraduate enrollment increased by more than half, and graduate student enrollment grew fivefold. Cavanaugh also established the Lobund Institute for Animal Studies and Notre Dame's Medieval Institute. Cavanaugh also presided over the construction of the Nieuwland Science Hall, Fisher Hall, and the Morris Inn, as well as the Hall of Liberal Arts (now O'Shaughnessy Hall), made possible by a donation from I.A. O'Shaughnessy, at the time the largest ever made to an American Catholic university.\n", + "----------------------------------------------------------------------\n", + "\n", + "\n", + "----------------------------------------------------------------------\n", + "Where is the headquarters of the Congregation of the Holy Cross?\n", + "\n", + "0.88: The university is the major seat of the Congregation of Holy Cross (albeit not its official headquarters, which are in Rome). Its main seminary, Moreau Seminary, is located on the campus across St. Joseph lake from the Main Building. Old College, the oldest building on campus and located near the shore of St. Mary lake, houses undergraduate seminarians. Retired priests and brothers reside in Fatima House (a former retreat center), Holy Cross House, as well as Columba Hall near the Grotto. The university through the Moreau Seminary has ties to theologian Frederick Buechner. While not Catholic, Buechner has praised writers from Notre Dame and Moreau Seminary created a Buechner Prize for Preaching.\n", + "0.84: In 1842, the Bishop of Vincennes, Célestine Guynemer de la Hailandière, offered land to Father Edward Sorin of the Congregation of the Holy Cross, on the condition that he build a college in two years. Fr. Sorin arrived on the site with eight Holy Cross brothers from France and Ireland on November 26, 1842, and began the school using Father Stephen Badin's old log chapel. He soon erected additional buildings, including Old College, the first church, and the first main building. They immediately acquired two students and set about building additions to the campus.\n", + "0.84: Because of its Catholic identity, a number of religious buildings stand on campus. The Old College building has become one of two seminaries on campus run by the Congregation of Holy Cross. The current Basilica of the Sacred Heart is located on the spot of Fr. Sorin's original church, which became too small for the growing college. It is built in French Revival style and it is decorated by stained glass windows imported directly from France. The interior was painted by Luigi Gregori, an Italian painter invited by Fr. Sorin to be artist in residence. The Basilica also features a bell tower with a carillon. Inside the church there are also sculptures by Ivan Mestrovic. The Grotto of Our Lady of Lourdes, which was built in 1896, is a replica of the original in Lourdes, France. It is very popular among students and alumni as a place of prayer and meditation, and it is considered one of the most beloved spots on campus.\n", + "0.84: The university is affiliated with the Congregation of Holy Cross (Latin: Congregatio a Sancta Cruce, abbreviated postnominals: \"CSC\"). While religious affiliation is not a criterion for admission, more than 93% of students identify as Christian, with over 80% of the total being Catholic. Collectively, Catholic Mass is celebrated over 100 times per week on campus, and a large campus ministry program provides for the faith needs of the community. There are multitudes of religious statues and artwork around campus, most prominent of which are the statue of Mary on the Main Building, the Notre Dame Grotto, and the Word of Life mural on Hesburgh Library depicting Christ as a teacher. Additionally, every classroom displays a crucifix. There are many religious clubs (catholic and non-Catholic) at the school, including Council #1477 of the Knights of Columbus (KOC), Baptist Collegiate Ministry (BCM), Jewish Club, Muslim Student Association, Orthodox Christian Fellowship, The Mormon Club, and many more. The Notre Dame KofC are known for being the first collegiate council of KofC, operating a charitable concession stand during every home football game and owning their own building on campus which can be used as a cigar lounge.\n", + "0.83: Architecturally, the school has a Catholic character. Atop the Main Building's gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend \"Venite Ad Me Omnes\". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.\n", + "----------------------------------------------------------------------\n", + "\n", + "\n" + ] + } + ], + "source": [ + "for query_result in res.json()['results']:\n", + " query = query_result['query']\n", + " answers = []\n", + " scores = []\n", + " for result in query_result['results']:\n", + " answers.append(result['text'])\n", + " scores.append(round(result['score'], 2))\n", + " print(\"-\"*70+\"\\n\"+query+\"\\n\\n\"+\"\\n\".join([f\"{s}: {a}\" for a, s in zip(answers, scores)])+\"\\n\"+\"-\"*70+\"\\n\\n\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The top results are all relevant as we would have hoped. With that we've finished. The retrieval app API can be shut down, and to save resources the Pinecone index can be deleted within the [Pinecone console](https://app.pinecone.io/)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "chatgpt-retrieval-plugin-S7h-2AWq-py3.10", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "1979a773a5778de9a5fa593a629dff0ab3c80c2563810d3e6a8dfb123dc01c7d" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/retrieval/examples/providers/redis/semantic-search-and-filter.ipynb b/retrieval/examples/providers/redis/semantic-search-and-filter.ipynb new file mode 100644 index 0000000..1d7b63a --- /dev/null +++ b/retrieval/examples/providers/redis/semantic-search-and-filter.ipynb @@ -0,0 +1,511 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os\n", + "import requests" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Document retrieval: upsert and query basic usage\n", + "\n", + "In this walkthrough we will see how to use the retrieval API with a Redis datastore for *semantic search / question-answering*. We will also provide a basic demo showing how to use the \"filter\" function.\n", + "\n", + "Before running this notebook you should have already initialized the retrieval API and have it running locally or elsewhere. The full instructions for doing this are found in on the chatgpt-retrieval-plugin page [page](https://github.com/openai/chatgpt-retrieval-plugin#quickstart). Please follow the instructions to start the app with the redis datastore.\n", + "\n", + "Additional examples using the search features can be found [here](https://github.com/openai/chatgpt-retrieval-plugin/blob/main/examples/providers/pinecone/semantic-search.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Document\n", + "\n", + "First we will prepare a collection of documents. From the perspective of the retrieval plugin, a [document](https://github.com/openai/chatgpt-retrieval-plugin/blob/main/models/models.py) this consists\n", + "of an \"id\", \"text\" and a collection of \"metadata\".\n", + "\n", + "The \"metadata\" has \"source\", \"source_id\", \"created_at\", \"url\" and \"author\" fields. Query metadata does not expose the \"url\" field.\n", + "\n", + "The \"source\" field is an Enum and can only be one of (\"file\", \"email\" or \"chat\").\n", + "\n", + "Text is taken from company SEC 10-K filings which are in the public domain.\n", + "\n", + "For demonstration, we will insert some **fake** authors for the documents, see the respective links for the original sources. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "document_1 = {\n", + " \"id\": \"twtr\",\n", + " \"text\": \"\"\"Postponements, suspensions or cancellations of major events, such as sporting events\n", + " and music festivals, may lead to people perceiving the content on Twitter as less\n", + " relevant or useful or of lower quality, which could negatively affect mDAU growth,\n", + " or may reduce monetization opportunities in connection with such events.\"\"\",\n", + " \"metadata\" : {\n", + " \"source\" : \"file\",\n", + " \"source_id\" : \"test:twtr10k\",\n", + " \"created_at\": \"2020-12-31\",\n", + " \"url\": \"https://www.sec.gov/Archives/edgar/data/1418091/000141809121000031/twtr-20201231.htm\",\n", + " \"author\": 'Elvis Tusk Sr.' \n", + " }\n", + "}\n", + "\n", + "document_2 = {\n", + " \"id\": \"tsla\",\n", + " \"text\": \"\"\"Because we do not have independent dealer networks, we are responsible for delivering\n", + " all of our vehicles to our customers.\"\"\",\n", + " \"metadata\" : {\n", + " \"source\" : \"file\",\n", + " \"source_id\" : \"test:tesla10k\",\n", + " \"created_at\": \"2021-12-31\",\n", + " \"url\": \"https://www.sec.gov/Archives/edgar/data/1318605/000095017022000796/tsla-20211231.htm\",\n", + " \"author\": 'Elvis Tusk Jr.' \n", + " } \n", + "}\n", + "\n", + "document_3 = {\n", + " \"id\": \"xom\",\n", + " \"text\": \"\"\"All practical and economically-viable energy sources will need to be pursued to continue\n", + " meeting global energy demand, recognizing the scale and variety of worldwide energy needs\n", + " as well as the importance of expanding access to modern energy to promote better standards\n", + " of living for billions of people.\"\"\",\n", + " \"metadata\" : {\n", + " \"source\" : \"file\",\n", + " \"source_id\" : \"test:xom10k\",\n", + " \"created_at\": \"2020-12-31\",\n", + " \"url\": \"https://www.sec.gov/Archives/edgar/data/34088/000003408821000012/xom-20201231.htm\",\n", + " \"author\": 'Vape Jordan' \n", + " } \n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Indexing the Docs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We're now ready to begin indexing (or *upserting*) our `documents`. To make these requests to the retrieval app API, we will need to provide authorization in the form of the `BEARER_TOKEN` we set earlier. We do this below:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "BEARER_TOKEN = os.environ.get(\"BEARER_TOKEN\") or \"BEARER_TOKEN_HERE\"\n", + "endpoint_url = 'http://0.0.0.0:8000'\n", + "headers = {\n", + " \"Authorization\": f\"Bearer {BEARER_TOKEN}\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the `BEARER_TOKEN` to create our authorization `headers`:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "response = requests.post(\n", + " f\"{endpoint_url}/upsert\",\n", + " headers=headers,\n", + " json={\n", + " \"documents\": [document_1, document_2, document_3]\n", + " }\n", + ")\n", + "response.raise_for_status()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example filter syntax\n", + "In our example data we have tagged each companies 10k documents as a source: test:twtr10k, test:tsla10k, and test:xom10k.\n", + "And we have created **fake** authors of the documents, Elvis Tusk Jr., Elvis Tusk Sr. and Vape Jordan. We will then filter based on these fields." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### TAG Fields\n", + "\n", + "source and source_id are \"TAG\" fields, Redis supports a limited [query syntax](https://redis.io/docs/stack/search/reference/tags/) on TAGS, which includes and \"or\" syntax, i.e. \"test:twtr10k|test:tesla10k\" or a ```*``` wildcard to match a prefix.\n", + "\n", + "In this example we have only two documents that match the filter so only two documents will show.\n", + "\n", + "Gotcha: There cannot be a space between the bar \"|\", i.e. \"test:twtr10k|test:tesla10k\" is valid, \"test:twtr10k | test:tesla10k\" is not." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'results': [{'query': 'How does Tesla deliver cars?',\n", + " 'results': [{'id': 'tsla',\n", + " 'text': 'Because we do not have independent dealer networks, we are responsible for delivering all of our vehicles to our customers.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:tesla10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/1318605/000095017022000796/tsla-20211231.htm',\n", + " 'created_at': '1640908800',\n", + " 'author': 'Elvis Tusk Jr.',\n", + " 'document_id': 'tsla'},\n", + " 'embedding': None,\n", + " 'score': 0.185401830213},\n", + " {'id': 'twtr',\n", + " 'text': 'Postponements, suspensions or cancellations of major events, such as sporting events and music festivals, may lead to people perceiving the content on Twitter as less relevant or useful or of lower quality, which could negatively affect mDAU growth, or may reduce monetization opportunities in connection with such events.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:twtr10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/1418091/000141809121000031/twtr-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Elvis Tusk Sr.',\n", + " 'document_id': 'twtr'},\n", + " 'embedding': None,\n", + " 'score': 0.300053447242}]}]}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"query\": \"How does Tesla deliver cars?\",\n", + " \"filter\": {\"source_id\": \"test:twtr10k|test:tesla10k\"},\n", + " \"top_k\": 3\n", + "}\n", + "\n", + "response = requests.post(\n", + " f\"{endpoint_url}/query\",\n", + " headers=headers,\n", + " json={\n", + " \"queries\": [query]\n", + " }\n", + ")\n", + "response.raise_for_status()\n", + "\n", + "response.json()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we use a wild card to filter by prefix. There are three documents matching this filter so three results will be printed.\n", + "\n", + "Gotcha, only prefix filtering is supported for redis TAGS, i.e. \"test*\" is valid, where as \"te\\*t\\*\" is not." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'results': [{'query': 'I want information related to car dealerships.',\n", + " 'results': [{'id': 'tsla',\n", + " 'text': 'Because we do not have independent dealer networks, we are responsible for delivering all of our vehicles to our customers.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:tesla10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/1318605/000095017022000796/tsla-20211231.htm',\n", + " 'created_at': '1640908800',\n", + " 'author': 'Elvis Tusk Jr.',\n", + " 'document_id': 'tsla'},\n", + " 'embedding': None,\n", + " 'score': 0.204279193893},\n", + " {'id': 'twtr',\n", + " 'text': 'Postponements, suspensions or cancellations of major events, such as sporting events and music festivals, may lead to people perceiving the content on Twitter as less relevant or useful or of lower quality, which could negatively affect mDAU growth, or may reduce monetization opportunities in connection with such events.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:twtr10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/1418091/000141809121000031/twtr-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Elvis Tusk Sr.',\n", + " 'document_id': 'twtr'},\n", + " 'embedding': None,\n", + " 'score': 0.292188997496},\n", + " {'id': 'xom',\n", + " 'text': 'All practical and economically-viable energy sources will need to be pursued to continue meeting global energy demand, recognizing the scale and variety of worldwide energy needs as well as the importance of expanding access to modern energy to promote better standards of living for billions of people.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:xom10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/34088/000003408821000012/xom-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Vape Jordan',\n", + " 'document_id': 'xom'},\n", + " 'embedding': None,\n", + " 'score': 0.305264299269}]}]}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"query\": \"I want information related to car dealerships.\",\n", + " \"filter\": {\"source_id\": \"test:*\"},\n", + " \"top_k\": 3\n", + "}\n", + "\n", + "response = requests.post(\n", + " f\"{endpoint_url}/query\",\n", + " headers=headers,\n", + " json={\n", + " \"queries\": [query]\n", + " }\n", + ")\n", + "response.raise_for_status()\n", + "\n", + "response.json()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last example we filter by the \"author\" field. The author field is a TextField, and so we have more options for filtering, \n", + "see [here](https://redis.io/docs/stack/search/reference/query_syntax/) for a complete set of examples.\n", + "\n", + "We can select by a specific author, here we only expect to return a single result." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'results': [{'query': 'I want information related to car dealerships.',\n", + " 'results': [{'id': 'xom',\n", + " 'text': 'All practical and economically-viable energy sources will need to be pursued to continue meeting global energy demand, recognizing the scale and variety of worldwide energy needs as well as the importance of expanding access to modern energy to promote better standards of living for billions of people.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:xom10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/34088/000003408821000012/xom-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Vape Jordan',\n", + " 'document_id': 'xom'},\n", + " 'embedding': None,\n", + " 'score': 0.305264299269}]}]}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"query\": \"I want information related to car dealerships.\",\n", + " \"filter\": {\"source_id\": \"test:*\", \"author\": \"Vape Jordan\"},\n", + " \"top_k\": 3\n", + "}\n", + "\n", + "response = requests.post(\n", + " f\"{endpoint_url}/query\",\n", + " headers=headers,\n", + " json={\n", + " \"queries\": [query]\n", + " }\n", + ")\n", + "response.raise_for_status()\n", + "\n", + "response.json()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we use the negation \"-\" to select all documents, except those published by an author called Elvis" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'results': [{'query': 'I want information related to car dealerships.',\n", + " 'results': [{'id': 'xom',\n", + " 'text': 'All practical and economically-viable energy sources will need to be pursued to continue meeting global energy demand, recognizing the scale and variety of worldwide energy needs as well as the importance of expanding access to modern energy to promote better standards of living for billions of people.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:xom10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/34088/000003408821000012/xom-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Vape Jordan',\n", + " 'document_id': 'xom'},\n", + " 'embedding': None,\n", + " 'score': 0.305264299269}]}]}" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"query\": \"I want information related to car dealerships.\",\n", + " \"filter\": {\"source_id\": \"test:*\", \"author\": \"-Elvis\"},\n", + " \"top_k\": 3\n", + "}\n", + "\n", + "response = requests.post(\n", + " f\"{endpoint_url}/query\",\n", + " headers=headers,\n", + " json={\n", + " \"queries\": [query]\n", + " }\n", + ")\n", + "response.raise_for_status()\n", + "\n", + "response.json()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Last example we filter two of the authors:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'results': [{'query': 'I want information related to car dealerships.',\n", + " 'results': [{'id': 'tsla',\n", + " 'text': 'Because we do not have independent dealer networks, we are responsible for delivering all of our vehicles to our customers.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:tesla10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/1318605/000095017022000796/tsla-20211231.htm',\n", + " 'created_at': '1640908800',\n", + " 'author': 'Elvis Tusk Jr.',\n", + " 'document_id': 'tsla'},\n", + " 'embedding': None,\n", + " 'score': 0.204279193893},\n", + " {'id': 'xom',\n", + " 'text': 'All practical and economically-viable energy sources will need to be pursued to continue meeting global energy demand, recognizing the scale and variety of worldwide energy needs as well as the importance of expanding access to modern energy to promote better standards of living for billions of people.',\n", + " 'metadata': {'source': 'file',\n", + " 'source_id': 'test:xom10k',\n", + " 'url': 'https://www.sec.gov/Archives/edgar/data/34088/000003408821000012/xom-20201231.htm',\n", + " 'created_at': '1609372800',\n", + " 'author': 'Vape Jordan',\n", + " 'document_id': 'xom'},\n", + " 'embedding': None,\n", + " 'score': 0.305264299269}]}]}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"query\": \"I want information related to car dealerships.\",\n", + " \"filter\": {\"source_id\": \"test:*\", \"author\": \"Elvis*Jr.|Vape\"},\n", + " \"top_k\": 3\n", + "}\n", + "\n", + "response = requests.post(\n", + " f\"{endpoint_url}/query\",\n", + " headers=headers,\n", + " json={\n", + " \"queries\": [query]\n", + " }\n", + ")\n", + "response.raise_for_status()\n", + "\n", + "response.json()" + ] + } + ], + "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.1" + }, + "vscode": { + "interpreter": { + "hash": "1979a773a5778de9a5fa593a629dff0ab3c80c2563810d3e6a8dfb123dc01c7d" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/retrieval/examples/providers/supabase/.gitignore b/retrieval/examples/providers/supabase/.gitignore new file mode 100644 index 0000000..773c7c3 --- /dev/null +++ b/retrieval/examples/providers/supabase/.gitignore @@ -0,0 +1,3 @@ +# Supabase +.branches +.temp diff --git a/retrieval/examples/providers/supabase/config.toml b/retrieval/examples/providers/supabase/config.toml new file mode 100644 index 0000000..9213130 --- /dev/null +++ b/retrieval/examples/providers/supabase/config.toml @@ -0,0 +1,72 @@ +# A string used to distinguish different Supabase projects on the same host. Defaults to the working +# directory name when running `supabase init`. +project_id = "providers" + +[api] +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. public and storage are always included. +schemas = ["public", "storage", "graphql_public"] +# Extra schemas to add to the search_path of every request. public is always included. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[db] +# Port to use for the local database URL. +port = 54322 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 15 + +[studio] +# Port to use for Supabase Studio. +port = 54323 + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +# Port to use for the email testing server web interface. +port = 54324 +smtp_port = 54325 +pop3_port = 54326 + +[storage] +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[auth] +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://localhost:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://localhost:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one +# week). +jwt_expiry = 3600 +# Allow/disallow new user signups to your project. +enable_signup = true + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +secret = "" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" diff --git a/retrieval/examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql b/retrieval/examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql new file mode 100644 index 0000000..4d54797 --- /dev/null +++ b/retrieval/examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql @@ -0,0 +1,70 @@ +create extension vector; + +create table if not exists documents ( + id text primary key default gen_random_uuid()::text, + source text, + source_id text, + content text, + document_id text, + author text, + url text, + created_at timestamptz default now(), + embedding vector(1536) +); + +create index ix_documents_document_id on documents using btree ( document_id ); +create index ix_documents_source on documents using btree ( source ); +create index ix_documents_source_id on documents using btree ( source_id ); +create index ix_documents_author on documents using btree ( author ); +create index ix_documents_created_at on documents using brin ( created_at ); + +alter table documents enable row level security; + +create or replace function match_page_sections(in_embedding vector(1536) + , in_match_count int default 3 + , in_document_id text default '%%' + , in_source_id text default '%%' + , in_source text default '%%' + , in_author text default '%%' + , in_start_date timestamptz default '-infinity' + , in_end_date timestamptz default 'infinity') +returns table (id text + , source text + , source_id text + , document_id text + , url text + , created_at timestamptz + , author text + , content text + , embedding vector(1536) + , similarity float) +language plpgsql +as $$ +#variable_conflict use_variable +begin +return query +select + documents.id, + documents.source, + documents.source_id, + documents.document_id, + documents.url, + documents.created_at, + documents.author, + documents.content, + documents.embedding, + (documents.embedding <#> in_embedding) * -1 as similarity +from documents + +where in_start_date <= documents.created_at and + documents.created_at <= in_end_date and + (documents.source_id like in_source_id or documents.source_id is null) and + (documents.source like in_source or documents.source is null) and + (documents.author like in_author or documents.author is null) and + (documents.document_id like in_document_id or documents.document_id is null) + +order by documents.embedding <#> in_embedding + +limit in_match_count; +end; +$$; \ No newline at end of file diff --git a/retrieval/examples/providers/supabase/seed.sql b/retrieval/examples/providers/supabase/seed.sql new file mode 100644 index 0000000..e69de29 diff --git a/retrieval/local_server/ai-plugin.json b/retrieval/local_server/ai-plugin.json new file mode 100644 index 0000000..5911818 --- /dev/null +++ b/retrieval/local_server/ai-plugin.json @@ -0,0 +1,18 @@ +{ + "schema_version": "v1", + "name_for_model": "retrieval", + "name_for_human": "Retrieval Plugin", + "description_for_model": "Plugin for searching through the user's documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.", + "description_for_human": "Search through your documents.", + "auth": { + "type": "none" + }, + "api": { + "type": "openapi", + "url": "http://localhost:3333/.well-known/openapi.yaml" + }, + "logo_url": "http://localhost:3333/.well-known/logo.png", + "contact_email": "hello@contact.com", + "legal_info_url": "hello@legal.com" +} + diff --git a/retrieval/local_server/logo.png b/retrieval/local_server/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..af562f798e9ba772e44fb42c21d3b9de398d120d GIT binary patch literal 17015 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.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + +[[package]] +name = "arrow" +version = "1.2.3" +description = "Better dates & times for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"}, + {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" + +[[package]] +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "authlib" +version = "1.2.0" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "Authlib-1.2.0-py2.py3-none-any.whl", hash = "sha256:4ddf4fd6cfa75c9a460b361d4bd9dac71ffda0be879dbe4292a02e92349ad55a"}, + {file = "Authlib-1.2.0.tar.gz", hash = "sha256:4fa3e80883a5915ef9f5bc28630564bc4ed5b5af39812a3ff130ec76bd631e9d"}, +] + +[package.dependencies] +cryptography = ">=3.2" + +[[package]] +name = "azure-common" +version = "1.1.28" +description = "Microsoft Azure Client Library for Python (Common)" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3"}, + {file = "azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"}, +] + +[[package]] +name = "azure-core" +version = "1.26.4" +description = "Microsoft Azure Core Library for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-core-1.26.4.zip", hash = "sha256:075fe06b74c3007950dd93d49440c2f3430fd9b4a5a2756ec8c79454afc989c6"}, + {file = "azure_core-1.26.4-py3-none-any.whl", hash = "sha256:d9664b4bc2675d72fba461a285ac43ae33abb2967014a955bf136d9703a2ab3c"}, +] + +[package.dependencies] +requests = ">=2.18.4" +six = ">=1.11.0" +typing-extensions = ">=4.3.0" + +[package.extras] +aio = ["aiohttp (>=3.0)"] + +[[package]] +name = "azure-identity" +version = "1.13.0" +description = "Microsoft Azure Identity Library for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-identity-1.13.0.zip", hash = "sha256:c931c27301ffa86b07b4dcf574e29da73e3deba9ab5d1fe4f445bb6a3117e260"}, + {file = "azure_identity-1.13.0-py3-none-any.whl", hash = "sha256:bd700cebb80cd9862098587c29d8677e819beca33c62568ced6d5a8e5e332b82"}, +] + +[package.dependencies] +azure-core = ">=1.11.0,<2.0.0" +cryptography = ">=2.5" +msal = ">=1.20.0,<2.0.0" +msal-extensions = ">=0.3.0,<2.0.0" +six = ">=1.12.0" + +[[package]] +name = "azure-search-documents" +version = "11.4.0a20230509004" +description = "Microsoft Azure Cognitive Search Client Library for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-search-documents-11.4.0a20230509004.zip", hash = "sha256:6cca144573161a10aa0fcd13927264453e79c63be6a53cf2ec241c9c8c22f6b5"}, + {file = "azure_search_documents-11.4.0a20230509004-py3-none-any.whl", hash = "sha256:6215e9a4f9e935ff3eac1b7d5519c6c0789b4497eb11242d376911aaefbb0359"}, +] + +[package.dependencies] +azure-common = ">=1.1,<2.0" +azure-core = ">=1.24.0,<2.0.0" +isodate = ">=0.6.0" + +[package.source] +type = "legacy" +url = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple" +reference = "azure-sdk-dev" + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + +[[package]] +name = "bleach" +version = "6.0.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, + {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.2)"] + +[[package]] +name = "blobfile" +version = "2.0.2" +description = "Read GCS, ABS and local paths with the same interface, clone of tensorflow.io.gfile" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "blobfile-2.0.2-py3-none-any.whl", hash = "sha256:c48afb61d14d6f94b0c109aa35475bc8d586e5eecde9a25dc2c9c52e7dd4feaf"}, +] + +[package.dependencies] +filelock = ">=3.0,<4.0" +lxml = ">=4.9,<5.0" +pycryptodomex = ">=3.8,<4.0" +urllib3 = ">=1.25.3,<3" + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "chromadb" +version = "0.3.22" +description = "Chroma." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chromadb-0.3.22-py3-none-any.whl", hash = "sha256:54b58e562ab8a63194ce3b453633ce351475193de2184845f0577db969f1cf49"}, + {file = "chromadb-0.3.22.tar.gz", hash = "sha256:41acb262c2c7bb41afecd50737f440dce3fdaa3d3fe1749d0e4be1ffc8699e63"}, +] + +[package.dependencies] +clickhouse-connect = ">=0.5.7" +duckdb = ">=0.7.1" +fastapi = ">=0.85.1" +hnswlib = ">=0.7" +numpy = ">=1.21.6" +pandas = ">=1.3" +posthog = ">=2.4.0" +pydantic = ">=1.9" +requests = ">=2.28" +sentence-transformers = ">=2.2.2" +typing-extensions = ">=4.5.0" +uvicorn = {version = ">=0.18.3", extras = ["standard"]} + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "click-log" +version = "0.4.0" +description = "Logging integration for Click" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "click-log-0.4.0.tar.gz", hash = "sha256:3970f8570ac54491237bcdb3d8ab5e3eef6c057df29f8c3d1151a51a9c23b975"}, + {file = "click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756"}, +] + +[package.dependencies] +click = "*" + +[[package]] +name = "clickhouse-connect" +version = "0.5.24" +description = "ClickHouse core driver, SqlAlchemy, and Superset libraries" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "clickhouse-connect-0.5.24.tar.gz", hash = "sha256:f1c6a4a20c19612eedaf1cea82e532010942cb08a29326db74cce0ea48bbe56d"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5b91584305b6133eff83e8a0436b3c48681dd44dcf8b2f5b54d558bafd30afa6"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:17f3ca231aeff7c9f316dc03cba49ea8cd1e91e0f129519f8857f0e1d9aa7f49"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b126b324ca9e34662bc07335f55ff51f9a5a5c5e4df97778f0a427b4bde8cfa"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c756b8f290fc68af83129d378b749e74c40560107b926ef047c098b7c95a2ad"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:486f538781d765993cc2b6f30ef8c274674b1be2c36dc03767d14feea24df566"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:67cfb63b155c36413ff301c321de09e2476a936dc784c7954a63d612ec66f1ec"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:56b004a0e001e49a2b6a022a98832b5558642299de9c808cf7b9333180f28e1b"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:68bae08ef93aa21e02c961c79f2932cc88d0682a91099ec2f007c032ab4b68e1"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-win32.whl", hash = "sha256:b7f73598f118c7466230f7149de0b4e1af992b2ac086a9200ac0011ab03ee468"}, + {file = "clickhouse_connect-0.5.24-cp310-cp310-win_amd64.whl", hash = "sha256:5b83b4c6994e43ce3192c11ac4eb84f8ac8b6317d860fc2c4ff8f8f3609b20c1"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed329a93171ca867df9b903b95992d9dec2e256a657e16a88d27452dfe8f064e"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9bc64de89be44c30bf036aab551da196e11ebf14502533b6e2a0e8ca60c27599"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84adbe15ad0dd745aa1b2a183cf4d1573d39cdb81e9d0a2d37571805dfda4cd7"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50f7f3756c64791fa8a4ec73f87954a6c3aa44523394ad22e13e31ba1cd9c25"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08499995addd7d0e758086622d32aa8f8fdf6dde61bedb106f453191b16af15f"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d8607c4b388a46b312fd34cdd26fe958002e414c0320aad0e24ac93854191325"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f0adcfbda306a1aa9f3cdc2f638b36c748c68104be97d9dc935c130ad632be82"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2abee0170d60d0621f7feec6b1e9c7434e3bb23a7b025d32a513f2df969b9a2d"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-win32.whl", hash = "sha256:d6f7ea32b46a5fafa49a85b94b18902af38b0910f34ac588ec95b5b66faf7855"}, + {file = "clickhouse_connect-0.5.24-cp311-cp311-win_amd64.whl", hash = "sha256:f0ae6e14f526c5fe504103d00992bf8e0ab3359266664b327c273e16f957545d"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc0b18678b66160ca4ca6ce7fe074188975546c5d196092ef06510eb16067964"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91a6d666c4c3f4dea7bca84098a4624102cb3efa7f882352e8b914238b0ab3b0"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1732ea5fddf201425baf53d1434516c1242184139d61202f885575cb8742167c"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be9c23721caacc52e9f75ba2239a5ca5bbdbafa913d36bcddf9eaf33578ba937"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b9aee9588b863ab3d33c11e9d2f350cee1f17753db74cedd3eb2bb4fc5ed31d1"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7158f70e5ba787f64f01098fa729942d1d4dfd1a46c4519aab10ed3a4b32ead"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6684d253580c2e9cbcab8322189ca66fafc27ccabf67da58f178b31a09ecb60f"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-win32.whl", hash = "sha256:ba015b5337ecab0e9064eed3966acd2fe2c10f0391fc5f28d8c0fd73802d0810"}, + {file = "clickhouse_connect-0.5.24-cp37-cp37m-win_amd64.whl", hash = "sha256:34feb3cb81298beff8e2be233719cf1271fd0f1aca2a0ae5dfff9716f9ab94c1"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ae2551daec4731373bffc6bc9d3e30a5dfbc0bdceb66cbc93c56dd0797c0740"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2cf26c82f3bd03e3088251f249776285a01da3268936d88d98b7cbecb2783497"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0437c44d342edada639fed6f5064226cc9ad9f37406ea1cf550a50cb3f66db5a"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e7b5f68b7bae44ec5dfc80510bb81f9f2af88662681c103d5a58da170f4eb78"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc0ccf9ef68377291aba32dc7754b8aab658c2b4cfe06488140114f8abbef819"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1e9c3f146bdb1929223ebba04610ebf7bbbed313ee452754268c546966eff9db"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f7e31461ce8e13e2b9f67b21e2ac7bd1121420d85bf6dc888082dfd2f6ca9bc4"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7b9b5a24cad361845f1d138ba9fb45f690c84583ca584adac76379a65fd8c00"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-win32.whl", hash = "sha256:7d223477041ae31b62917b5f9abeaa468fe2a1efa8391070da4258a41fdc7643"}, + {file = "clickhouse_connect-0.5.24-cp38-cp38-win_amd64.whl", hash = "sha256:c82fcf42d9a2318cf53086147376c31246e3842b73a09b4bac16a6f0c299a294"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:586d7193ece84ddc2608fdc29cd10cc80eff26f283b2ad9d738bbd522f1f84cd"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:71b452bed17aee315b93944174053cd84dc5efb245d4a556a2e49b78022f7ed6"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:788722210e636bec7a870b0625999f97c3285bc19fd46763b58472ee445b67e9"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:268e3375d9a3985ea961cb1be338c1d13154b617f5eb027ace0e8670de9501ce"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28ea9abd595d7400e3ef2842f5e9db5307133dfa24d97a8c45f71713048bad97"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00b0ac033dc47e0409a19ff974d938006a198445980028d911a47ba05facf6cd"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:601a26ddb18e266e79b76d1672ac15ef5b6043ea17ba4c9dc3dc80130a0775d9"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:eb502ccb7c5dcb907cf4c8316f9b787e4bd3a7b65cd8cbc37b24c5e9c890a801"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-win32.whl", hash = "sha256:e6acedfd795cd1db7d89f21597389805e583f2b4ae9495cb0b89b8eda13ff6ad"}, + {file = "clickhouse_connect-0.5.24-cp39-cp39-win_amd64.whl", hash = "sha256:921d3a8a287844c031c470547c07dd5b7454c883c44f13e1d4f5b9d0896444d2"}, + {file = "clickhouse_connect-0.5.24-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ec051a1f6f3912f2f3b659d3e3c344a67f676d2d42583885b3ed8365c51753b2"}, + {file = "clickhouse_connect-0.5.24-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b116538fd7d75df991b211a3db311c158a2664301b2f5d1ffc18feb5b5da89d"}, + {file = "clickhouse_connect-0.5.24-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b116747e4b187d3aac49a51e865a4fe0c11b39775724f0d7f719b4222810a5a4"}, + {file = "clickhouse_connect-0.5.24-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4fa54e11e651979d9a4e355564d2128c6a8394d4cffda295a8188c9869ab93cc"}, + {file = "clickhouse_connect-0.5.24-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7c17e691e27d3b2e950cb2f597f0a895eb6b9d6717e886fafae861d34ac5bbb0"}, + {file = "clickhouse_connect-0.5.24-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34e2ae809ac1244da6fa67c4021431f9a1865d14c6df2d7fe57d22841f361497"}, + {file = "clickhouse_connect-0.5.24-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e7b2ef89e9c1c92a09988a812626f7d529acfda93f420b75e59fe2981960886"}, + {file = "clickhouse_connect-0.5.24-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6200bdf94a52847d3f10ab8675c58db9ff3e90ce6ee98bc0c49f01c74d934798"}, + {file = "clickhouse_connect-0.5.24-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4def3ee218f6fbb320fbb1c5c1bb3b23753b9e56e50759fc396ea70631dff846"}, + {file = "clickhouse_connect-0.5.24-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:378f6a6289080f0c103f17eda9f8edcabc4878eb783e6b4e596d8bf8f543244e"}, + {file = "clickhouse_connect-0.5.24-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e29389baa14a3f1db4e52b32090e1e32533496e35833514c689b190f26dfb039"}, + {file = "clickhouse_connect-0.5.24-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7418e2c6533eebf0de9f3e85f1e3b6095d1a0bf42e4fed479f92f538725ff666"}, + {file = "clickhouse_connect-0.5.24-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3f23f819f20d130daed64ba058e01336e2f5f6d4b9f576038c0b800473af1ac"}, + {file = "clickhouse_connect-0.5.24-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a846fc412475d55d7727c8a82ba1247b1b7ff0c6341a1818f99fd348ee9b1580"}, + {file = "clickhouse_connect-0.5.24-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:34afc74ea27dcb85c1929f6105c4701566f51a1216bd6648b63ccb4871906729"}, +] + +[package.dependencies] +certifi = "*" +lz4 = "*" +pytz = "*" +urllib3 = ">=1.26" +zstandard = "*" + +[package.extras] +arrow = ["pyarrow"] +numpy = ["numpy"] +orjson = ["orjson"] +pandas = ["pandas"] +sqlalchemy = ["sqlalchemy (>1.3.21,<1.4)"] +superset = ["apache-superset (>=1.4.1)"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.2.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c"}, + {file = "coverage-7.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c"}, + {file = "coverage-7.2.5-cp310-cp310-win32.whl", hash = "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5"}, + {file = "coverage-7.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6"}, + {file = "coverage-7.2.5-cp311-cp311-win32.whl", hash = "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b"}, + {file = "coverage-7.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068"}, + {file = "coverage-7.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969"}, + {file = "coverage-7.2.5-cp37-cp37m-win32.whl", hash = "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718"}, + {file = "coverage-7.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1"}, + {file = "coverage-7.2.5-cp38-cp38-win32.whl", hash = "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813"}, + {file = "coverage-7.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1"}, + {file = "coverage-7.2.5-cp39-cp39-win32.whl", hash = "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31"}, + {file = "coverage-7.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252"}, + {file = "coverage-7.2.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3"}, + {file = "coverage-7.2.5.tar.gz", hash = "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "40.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"}, + {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"}, + {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"}, + {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"}, + {file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"}, + {file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"}, + {file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "check-manifest", "mypy", "ruff"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] +tox = ["tox"] + +[[package]] +name = "dataclasses-json" +version = "0.5.7" +description = "Easily serialize dataclasses to and from JSON" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "dataclasses-json-0.5.7.tar.gz", hash = "sha256:c2c11bc8214fbf709ffc369d11446ff6945254a7f09128154a7620613d8fda90"}, + {file = "dataclasses_json-0.5.7-py3-none-any.whl", hash = "sha256:bc285b5f892094c3a53d558858a88553dd6a61a11ab1a8128a0e554385dcc5dd"}, +] + +[package.dependencies] +marshmallow = ">=3.3.0,<4.0.0" +marshmallow-enum = ">=1.5.1,<2.0.0" +typing-inspect = ">=0.4.0" + +[package.extras] +dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest (>=6.2.3)", "simplejson", "types-dataclasses"] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "deprecation" +version = "2.1.0" +description = "A library to handle automated deprecations" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] + +[package.dependencies] +packaging = "*" + +[[package]] +name = "dnspython" +version = "2.3.0" +description = "DNS toolkit" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, + {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, +] + +[package.extras] +curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] +dnssec = ["cryptography (>=2.6,<40.0)"] +doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] +doq = ["aioquic (>=0.9.20)"] +idna = ["idna (>=2.1,<4.0)"] +trio = ["trio (>=0.14,<0.23)"] +wmi = ["wmi (>=1.5.1,<2.0.0)"] + +[[package]] +name = "docutils" +version = "0.20" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20-py3-none-any.whl", hash = "sha256:a428f10de4de4774389734c986a01b4af2d802d26717108b0f1b9356862937c5"}, + {file = "docutils-0.20.tar.gz", hash = "sha256:f75a5a52fbcacd81b47e42888ad2b380748aaccfb3f13af0fe69deb759f01eb6"}, +] + +[[package]] +name = "docx2txt" +version = "0.8" +description = "A pure python-based utility to extract text and images from docx files." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "docx2txt-0.8.tar.gz", hash = "sha256:2c06d98d7cfe2d3947e5760a57d924e3ff07745b379c8737723922e7009236e5"}, +] + +[[package]] +name = "dotty-dict" +version = "1.3.1" +description = "Dictionary wrapper for quick access to deeply nested keys." +category = "main" +optional = false +python-versions = ">=3.5,<4.0" +files = [ + {file = "dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f"}, + {file = "dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15"}, +] + +[[package]] +name = "duckdb" +version = "0.7.1" +description = "DuckDB embedded database" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "duckdb-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3e0170be6cc315c179169dfa3e06485ef7009ef8ce399cd2908f29105ef2c67b"}, + {file = "duckdb-0.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6360d41023e726646507d5479ba60960989a09f04527b36abeef3643c61d8c48"}, + {file = "duckdb-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:578c269d7aa27184e8d45421694f89deda3f41fe6bd2a8ce48b262b9fc975326"}, + {file = "duckdb-0.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36aae9a923c9f78da1cf3fcf75873f62d32ea017d4cef7c706d16d3eca527ca2"}, + {file = "duckdb-0.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:630e0122a02f19bb1fafae00786350b2c31ae8422fce97c827bd3686e7c386af"}, + {file = "duckdb-0.7.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9b9ca2d294725e523ce207bc37f28787478ae6f7a223e2cf3a213a2d498596c3"}, + {file = "duckdb-0.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0bd89f388205b6c99b62650169efe9a02933555ee1d46ddf79fbd0fb9e62652b"}, + {file = "duckdb-0.7.1-cp310-cp310-win32.whl", hash = "sha256:a9e987565a268fd8da9f65e54621d28f39c13105b8aee34c96643074babe6d9c"}, + {file = "duckdb-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:5d986b5ad1307b069309f9707c0c5051323e29865aefa059eb6c3b22dc9751b6"}, + {file = "duckdb-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54606dfd24d7181d3098030ca6858f6be52f3ccbf42fff05f7587f2d9cdf4343"}, + {file = "duckdb-0.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd9367ae650b6605ffe00412183cf0edb688a5fc9fbb03ed757e8310e7ec3b6c"}, + {file = "duckdb-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aaf33aeb543c7816bd915cd10141866d54f92f698e1b5712de9d8b7076da19df"}, + {file = "duckdb-0.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e56b0329c38c0356b40449917bab6fce6ac27d356257b9a9da613d2a0f064e0"}, + {file = "duckdb-0.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:604b8b476d6cc6bf91625d8c2722ef9c50c402b3d64bc518c838d6c279e6d93b"}, + {file = "duckdb-0.7.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:32a268508c6d7fdc99d5442736051de74c28a5166c4cc3dcbbf35d383299b941"}, + {file = "duckdb-0.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90794406fa2111414877ee9db154fef940911f3920c312c1cf69947621737c8d"}, + {file = "duckdb-0.7.1-cp311-cp311-win32.whl", hash = "sha256:bf20c5ee62cbbf10b39ebdfd70d454ce914e70545c7cb6cb78cb5befef96328a"}, + {file = "duckdb-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bb2700785cab37cd1e7a76c4547a5ab0f8a7c28ad3f3e4d02a8fae52be223090"}, + {file = "duckdb-0.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b09741cfa31388b8f9cdf5c5200e0995d55a5b54d2d1a75b54784e2f5c042f7f"}, + {file = "duckdb-0.7.1-cp36-cp36m-win32.whl", hash = "sha256:766e6390f7ace7f1e322085c2ca5d0ad94767bde78a38d168253d2b0b4d5cd5c"}, + {file = "duckdb-0.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6a3f3315e2b553db3463f07324f62dfebaf3b97656a87558e59e2f1f816eaf15"}, + {file = "duckdb-0.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:278edb8c912d836b3b77fd1695887e1dbd736137c3912478af3608c9d7307bb0"}, + {file = "duckdb-0.7.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e189b558d10b58fe6ed85ce79f728e143eb4115db1e63147a44db613cd4dd0d9"}, + {file = "duckdb-0.7.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b91ec3544ee4dc9e6abbdf2669475d5adedaaea51987c67acf161673e6b7443"}, + {file = "duckdb-0.7.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3fe3f3dbd62b76a773144eef31aa29794578c359da932e77fef04516535318ca"}, + {file = "duckdb-0.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1e78c7f59325e99f0b3d9fe7c2bad4aaadf42d2c7711925cc26331d7647a91b2"}, + {file = "duckdb-0.7.1-cp37-cp37m-win32.whl", hash = "sha256:bc2a12d9f4fc8ef2fd1022d610287c9fc9972ea06b7510fc87387f1fa256a390"}, + {file = "duckdb-0.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:53e3db1bc0f445ee48b23cde47bfba08c7fa5a69976c740ec8cdf89543d2405d"}, + {file = "duckdb-0.7.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1247cc11bac17f2585d11681329806c86295e32242f84a10a604665e697d5c81"}, + {file = "duckdb-0.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5feaff16a012075b49dfa09d4cb24455938d6b0e06b08e1404ec00089119dba2"}, + {file = "duckdb-0.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b411a0c361eab9b26dcd0d0c7a0d1bc0ad6b214068555de7e946fbdd2619961a"}, + {file = "duckdb-0.7.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c76d8694ecdb579241ecfeaf03c51d640b984dbbe8e1d9f919089ebf3cdea6"}, + {file = "duckdb-0.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193b896eed44d8751a755ccf002a137630020af0bc3505affa21bf19fdc90df3"}, + {file = "duckdb-0.7.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7da132ee452c80a3784b8daffd86429fa698e1b0e3ecb84660db96d36c27ad55"}, + {file = "duckdb-0.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5fd08c97c3e8cb5bec3822cf78b966b489213dcaab24b25c05a99f7caf8db467"}, + {file = "duckdb-0.7.1-cp38-cp38-win32.whl", hash = "sha256:9cb956f94fa55c4782352dac7cc7572a58312bd7ce97332bb14591d6059f0ea4"}, + {file = "duckdb-0.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:289a5f65213e66d320ebcd51a94787e7097b9d1c3492d01a121a2c809812bf19"}, + {file = "duckdb-0.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8085ad58c9b5854ee3820804fa1797e6b3134429c1506c3faab3cb96e71b07e9"}, + {file = "duckdb-0.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b47c19d1f2f662a5951fc6c5f6939d0d3b96689604b529cdcffd9afdcc95bff2"}, + {file = "duckdb-0.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6a611f598226fd634b7190f509cc6dd668132ffe436b0a6b43847b4b32b99e4a"}, + {file = "duckdb-0.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6730f03b5b78f3943b752c90bdf37b62ae3ac52302282a942cc675825b4a8dc9"}, + {file = "duckdb-0.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe23e938d29cd8ea6953d77dc828b7f5b95a4dbc7cd7fe5bcc3531da8cec3dba"}, + {file = "duckdb-0.7.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:feffe503c2e2a99480e1e5e15176f37796b3675e4dadad446fe7c2cc672aed3c"}, + {file = "duckdb-0.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72fceb06f5bf24ad6bb5974c60d397a7a7e61b3d847507a22276de076f3392e2"}, + {file = "duckdb-0.7.1-cp39-cp39-win32.whl", hash = "sha256:c4d5217437d20d05fe23317bbc161befa1f9363f3622887cd1d2f4719b407936"}, + {file = "duckdb-0.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:066885e1883464ce3b7d1fd844f9431227dcffe1ee39bfd2a05cd6d53f304557"}, + {file = "duckdb-0.7.1.tar.gz", hash = "sha256:a7db6da0366b239ea1e4541fcc19556b286872f5015c9a54c2e347146e25a2ad"}, +] + +[[package]] +name = "environs" +version = "9.5.0" +description = "simplified environment variable parsing" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"}, + {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"}, +] + +[package.dependencies] +marshmallow = ">=3.0.0" +python-dotenv = "*" + +[package.extras] +dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] +tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.92.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.92.0-py3-none-any.whl", hash = "sha256:ae7b97c778e2f2ec3fb3cb4fb14162129411d99907fb71920f6d69a524340ebf"}, + {file = "fastapi-0.92.0.tar.gz", hash = "sha256:023a0f5bd2c8b2609014d3bba1e14a1d7df96c6abea0a73070621c9862b9a4de"}, +] + +[package.dependencies] +pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = ">=0.25.0,<0.26.0" + +[package.extras] +all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.8.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.6.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "frozenlist" +version = "1.3.3" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, + {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, + {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, + {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, + {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, + {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, + {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, + {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, + {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, + {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, + {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, +] + +[[package]] +name = "fsspec" +version = "2023.5.0" +description = "File-system specification" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2023.5.0-py3-none-any.whl", hash = "sha256:51a4ad01a5bb66fcc58036e288c0d53d3975a0df2a5dc59a93b59bade0391f2a"}, + {file = "fsspec-2023.5.0.tar.gz", hash = "sha256:b3b56e00fb93ea321bc9e5d9cf6f8522a0198b20eb24e02774d329e9c6fb84ce"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +devel = ["pytest", "pytest-cov"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "requests"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +tqdm = ["tqdm"] + +[[package]] +name = "gitdb" +version = "4.0.10" +description = "Git Object Database" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, + {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.31" +description = "GitPython is a Python library used to interact with Git repositories" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, + {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[[package]] +name = "gotrue" +version = "1.0.1" +description = "Python Client Library for GoTrue" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "gotrue-1.0.1-py3-none-any.whl", hash = "sha256:005e8bc8d7f2da87606504c9c269f2943245843e2ddefb99e583f45a8612e715"}, + {file = "gotrue-1.0.1.tar.gz", hash = "sha256:9d7e01703beb3c017bcf0461f518f93bc5a400720df3ba8c082264d405cee4d0"}, +] + +[package.dependencies] +httpx = ">=0.23.0,<0.24.0" +pydantic = ">=1.10.0,<2.0.0" + +[[package]] +name = "greenlet" +version = "2.0.2" +description = "Lightweight in-process concurrent programming" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +files = [ + {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, + {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, + {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, + {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, + {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, + {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, + {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, + {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, + {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, + {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, + {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, + {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, + {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, + {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, + {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, + {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, + {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, + {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, + {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, + {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, + {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, + {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, +] + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpcio" +version = "1.53.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.53.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:752d2949b40e12e6ad3ed8cc552a65b54d226504f6b1fb67cab2ccee502cc06f"}, + {file = "grpcio-1.53.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8a48fd3a7222be226bb86b7b413ad248f17f3101a524018cdc4562eeae1eb2a3"}, + {file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f3e837d29f0e1b9d6e7b29d569e2e9b0da61889e41879832ea15569c251c303a"}, + {file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef7d30242409c3aa5839b501e877e453a2c8d3759ca8230dd5a21cda029f046"}, + {file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6f90698b5d1c5dd7b3236cd1fa959d7b80e17923f918d5be020b65f1c78b173"}, + {file = "grpcio-1.53.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a96c3c7f564b263c5d7c0e49a337166c8611e89c4c919f66dba7b9a84abad137"}, + {file = "grpcio-1.53.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ee81349411648d1abc94095c68cd25e3c2812e4e0367f9a9355be1e804a5135c"}, + {file = "grpcio-1.53.0-cp310-cp310-win32.whl", hash = "sha256:fdc6191587de410a184550d4143e2b24a14df495c86ca15e59508710681690ac"}, + {file = "grpcio-1.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:658ffe1e39171be00490db5bd3b966f79634ac4215a1eb9a85c6cd6783bf7f6e"}, + {file = "grpcio-1.53.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:1b172e6d497191940c4b8d75b53de82dc252e15b61de2951d577ec5b43316b29"}, + {file = "grpcio-1.53.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:82434ba3a5935e47908bc861ce1ebc43c2edfc1001d235d6e31e5d3ed55815f7"}, + {file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:1c734a2d4843e4e14ececf5600c3c4750990ec319e1299db7e4f0d02c25c1467"}, + {file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a2ead3de3b2d53119d473aa2f224030257ef33af1e4ddabd4afee1dea5f04c"}, + {file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34d6e905f071f9b945cabbcc776e2055de1fdb59cd13683d9aa0a8f265b5bf9"}, + {file = "grpcio-1.53.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eaf8e3b97caaf9415227a3c6ca5aa8d800fecadd526538d2bf8f11af783f1550"}, + {file = "grpcio-1.53.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:da95778d37be8e4e9afca771a83424f892296f5dfb2a100eda2571a1d8bbc0dc"}, + {file = "grpcio-1.53.0-cp311-cp311-win32.whl", hash = "sha256:e4f513d63df6336fd84b74b701f17d1bb3b64e9d78a6ed5b5e8a198bbbe8bbfa"}, + {file = "grpcio-1.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:ddb2511fbbb440ed9e5c9a4b9b870f2ed649b7715859fd6f2ebc585ee85c0364"}, + {file = "grpcio-1.53.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:2a912397eb8d23c177d6d64e3c8bc46b8a1c7680b090d9f13a640b104aaec77c"}, + {file = "grpcio-1.53.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:55930c56b8f5b347d6c8c609cc341949a97e176c90f5cbb01d148d778f3bbd23"}, + {file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:6601d812105583948ab9c6e403a7e2dba6e387cc678c010e74f2d6d589d1d1b3"}, + {file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c705e0c21acb0e8478a00e7e773ad0ecdb34bd0e4adc282d3d2f51ba3961aac7"}, + {file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba074af9ca268ad7b05d3fc2b920b5fb3c083da94ab63637aaf67f4f71ecb755"}, + {file = "grpcio-1.53.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:14817de09317dd7d3fbc8272864288320739973ef0f4b56bf2c0032349da8cdf"}, + {file = "grpcio-1.53.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c7ad9fbedb93f331c2e9054e202e95cf825b885811f1bcbbdfdc301e451442db"}, + {file = "grpcio-1.53.0-cp37-cp37m-win_amd64.whl", hash = "sha256:dad5b302a4c21c604d88a5d441973f320134e6ff6a84ecef9c1139e5ffd466f6"}, + {file = "grpcio-1.53.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:fa8eaac75d3107e3f5465f2c9e3bbd13db21790c6e45b7de1756eba16b050aca"}, + {file = "grpcio-1.53.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:104a2210edd3776c38448b4f76c2f16e527adafbde171fc72a8a32976c20abc7"}, + {file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:dbc1ba968639c1d23476f75c356e549e7bbf2d8d6688717dcab5290e88e8482b"}, + {file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95952d3fe795b06af29bb8ec7bbf3342cdd867fc17b77cc25e6733d23fa6c519"}, + {file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f144a790f14c51b8a8e591eb5af40507ffee45ea6b818c2482f0457fec2e1a2e"}, + {file = "grpcio-1.53.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0698c094688a2dd4c7c2f2c0e3e142cac439a64d1cef6904c97f6cde38ba422f"}, + {file = "grpcio-1.53.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6b6d60b0958be711bab047e9f4df5dbbc40367955f8651232bfdcdd21450b9ab"}, + {file = "grpcio-1.53.0-cp38-cp38-win32.whl", hash = "sha256:1948539ce78805d4e6256ab0e048ec793956d54787dc9d6777df71c1d19c7f81"}, + {file = "grpcio-1.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:df9ba1183b3f649210788cf80c239041dddcb375d6142d8bccafcfdf549522cd"}, + {file = "grpcio-1.53.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:19caa5b7282a89b799e63776ff602bb39604f7ca98db6df27e2de06756ae86c3"}, + {file = "grpcio-1.53.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:b5bd026ac928c96cc23149e6ef79183125542062eb6d1ccec34c0a37e02255e7"}, + {file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:7dc8584ca6c015ad82e186e82f4c0fe977394588f66b8ecfc4ec873285314619"}, + {file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2eddaae8af625e45b5c8500dcca1043264d751a6872cde2eda5022df8a336959"}, + {file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5fb6f3d7824696c1c9f2ad36ddb080ba5a86f2d929ef712d511b4d9972d3d27"}, + {file = "grpcio-1.53.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8270d1dc2c98ab57e6dbf36fa187db8df4c036f04a398e5d5e25b4e01a766d70"}, + {file = "grpcio-1.53.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:976a7f24eb213e8429cab78d5e120500dfcdeb01041f1f5a77b17b9101902615"}, + {file = "grpcio-1.53.0-cp39-cp39-win32.whl", hash = "sha256:9c84a481451e7174f3a764a44150f93b041ab51045aa33d7b5b68b6979114e48"}, + {file = "grpcio-1.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:6beb84f83360ff29a3654f43f251ec11b809dcb5524b698d711550243debd289"}, + {file = "grpcio-1.53.0.tar.gz", hash = "sha256:a4952899b4931a6ba12951f9a141ef3e74ff8a6ec9aa2dc602afa40f63595e33"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.53.0)"] + +[[package]] +name = "grpcio-tools" +version = "1.53.0" +description = "Protobuf code generator for gRPC" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-tools-1.53.0.tar.gz", hash = "sha256:925efff2d63ca3266f93c924ffeba5d496f16a8ccbe125fa0d18acf47cc5fa88"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:41b859cf943256debba1e7b921e3689c89f95495b65f7ad226c4f0e38edf8ee4"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:17c557240f7fbe1886dcfb5f3ba79740ecb65fe3b93061e64b8f4dfc6a6a5dc5"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:6afffd7e97e5bddc63b3ce9abe912b9adb704a36ba86d4406be94426734b97c2"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f55e2c13620271b7f5a81a489a188d6e34a24da8885d46f1566f0e798cb59e6f"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bd4c732d8d7a736e787b5d0963d4195267fc856e1d313d4532d1625e19a0e4a"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:99ecefb6b66e9fe41468a70ee2f05da2eb9c7bf63867fb9ff07f7dd90ea813ae"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7754d6466191d327a0eef364ad5b863477a8fcc12953adc06b30b8e470c70e4a"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-win32.whl", hash = "sha256:f31c549d793a0e72c044f724b3373141d2aa9970fe97b1c2cfaa7ea44002b9aa"}, + {file = "grpcio_tools-1.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4173b95e2c29a5145c806d16945ce1e5b38a11c7eb6ab1a6d74afc0a2ce47d9"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:613a84ebd1881635370c12503f2b15b37332a53fbac32904c94ac4c0c10f0a2a"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:af686b83bc6b5c1f1591c9f49183717974047de9546adcf5e09a18781b550c96"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3cc832e8297e9437bc2b137fe815c8ba1d9af6ffdd76c5c6d7f911bf8e1b0f45"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0a254de49d852f5fe9f9df0a45b2ae66bc04e2d9ee1d6d2c0ba1e70fac91a"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7062109553ec1873c5c09cc379b8ae0aa76a2d6d6aae97759b97787b93fa9786"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7728407b1e89fb1473b86152fc33be00f1a25a5aa3264245521f05cbbef9d817"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2758ea125442bc81251267fc9c28f65555a571f6a0afda4d71a6e7d669347095"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-win32.whl", hash = "sha256:8940d59fca790f1bd45785d0661c3a8c081231c9f8049d7fbf6c6c00737e43da"}, + {file = "grpcio_tools-1.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:c2cff79be5a06d63e9a6a7e38f8f160ade21517386eabe27afacef65a8531358"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:d646d65fafbf70a57416493e719a0df7ffa0772133266cfe1b2b72e072ae64a2"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:7da0fc185735050d8240b1d74c4667a02baf1b4fa379a5fc05d1fc067eeba596"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:2be17265c0f070efd625683cef986e07dbc495103fcc719009ff2f6988003166"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4701d48f649443f1101a24d85e9d5ac13346ccac7781e243f49491328e172266"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54c64d85bea5c3a3d895454878c7d6bed5cbb80dc3cafcd75dc1e78300d8c95"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7152045190e9bd665d1feaeaef931d82c75cacce2b116ab150befa90855de3d0"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e18292123c86975d0aa47f1bcb176393640dcc23912e9f3a2247f1eff81ac8e8"}, + {file = "grpcio_tools-1.53.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b1b76b6ab5c24e44b15d6a7df6c1b81c3099a54b82d41a3ce96e73a2e6a5081c"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:e76e8dfe6fe4e61ce3049e9d56c0d806d0d3edc28aa32117d1b17f387469c52e"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:4c6acaca09cfcd59850e27bd138df9d01c0686c42a5412aa6a92141c15316b1e"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:76898c1dadf8630a75a40b5a89ab38e326f1288dcfde3413cdfa7a58e149c987"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b47f8b1bd3af2fb25548b625ad9c3659da30fe83c06f462f357c754f49b71ae"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2faad4b6362e7ff3ae43ef2d51dfce0a3bc32cf52469e88568c3f65cae377d5"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:830261fe08541f0fd2dd5035264df2b91012988f37aa1d80a0b4ee6404dc25ae"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4be32c694c760f3281555089f7aed7d48ca7ea4094115a08b5fc895e17d7e62e"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-win32.whl", hash = "sha256:4605db5a5828205d7fa33a5de9e00723bd037709e74e15c028b9dcec2339b7bc"}, + {file = "grpcio_tools-1.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:0229e6cd442915192b8f8ee2e7e1c8b9986c878bc4dd8be3539f3be35f1b8282"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:ad0c20688a650e731e8328a7a08899c433a59bfc995a7afcf715b5ad9eca9e7b"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:a8c3e30c531969c62a5a219be414277b269c1be9a76bcd6948571868894e19b2"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:326c67b35be69409a88632e6145032d53b8b8141634e9cbcd27fa8f9015a112c"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:102b6d323d7cef7ac29683f949ec66885b417c06df6059f6a88d07c5556c2592"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:861f8634cca3ca5bb5336ba16cc78291dba3e7fcadedff195bfdeb433f2c29f2"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c9a9e1da1868349eba401e9648eac19132700942c475adcc97b6938bf4bf0182"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ccf7313e5bee13f2f86d12741489f3ed8c901d6b463dff2604191cd4ff518abb"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-win32.whl", hash = "sha256:65b77532bb8f6ab1bfbdd2ac0788626a6c05b227f4722d3bbc2c54258e49c3e5"}, + {file = "grpcio_tools-1.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:7c0ede22796259e83aa1f108038513e86672b2892d3654f94415e3930b74b871"}, +] + +[package.dependencies] +grpcio = ">=1.53.0" +protobuf = ">=4.21.6,<5.0dev" +setuptools = "*" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "h2" +version = "4.1.0" +description = "HTTP/2 State-Machine based protocol implementation" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, + {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, +] + +[package.dependencies] +hpack = ">=4.0,<5" +hyperframe = ">=6.0,<7" + +[[package]] +name = "hnswlib" +version = "0.7.0" +description = "hnswlib" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "hnswlib-0.7.0.tar.gz", hash = "sha256:bc459668e7e44bb7454b256b90c98c5af750653919d9a91698dafcf416cf64c4"}, +] + +[package.dependencies] +numpy = "*" + +[[package]] +name = "hpack" +version = "4.0.0" +description = "Pure-Python HPACK header compression" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, + {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, +] + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httptools" +version = "0.5.0" +description = "A collection of framework independent HTTP protocol utils." +category = "main" +optional = false +python-versions = ">=3.5.0" +files = [ + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, + {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, + {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, + {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, + {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, + {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, + {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, + {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, + {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, + {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "huggingface-hub" +version = "0.14.1" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "huggingface_hub-0.14.1-py3-none-any.whl", hash = "sha256:9fc619170d800ff3793ad37c9757c255c8783051e1b5b00501205eb43ccc4f27"}, + {file = "huggingface_hub-0.14.1.tar.gz", hash = "sha256:9ab899af8e10922eac65e290d60ab956882ab0bf643e3d990b1394b6b47b7fbc"}, +] + +[package.dependencies] +filelock = "*" +fsspec = "*" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "black (>=23.1,<24.0)", "gradio", "jedi", "mypy (==0.982)", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "ruff (>=0.0.241)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "black (>=23.1,<24.0)", "gradio", "jedi", "mypy (==0.982)", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "ruff (>=0.0.241)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +quality = ["black (>=23.1,<24.0)", "mypy (==0.982)", "ruff (>=0.0.241)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "gradio", "jedi", "pytest", "pytest-cov", "pytest-env", "pytest-xdist", "soundfile"] +torch = ["torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"] + +[[package]] +name = "hyperframe" +version = "6.0.1" +description = "HTTP/2 framing layer for Python" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, + {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, +] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "invoke" +version = "1.7.3" +description = "Pythonic task execution" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"}, + {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"}, +] + +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "jaraco-classes" +version = "3.2.3" +description = "Utility functions for Python class constructs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, + {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "joblib" +version = "1.2.0" +description = "Lightweight pipelining with Python functions" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.2.0-py3-none-any.whl", hash = "sha256:091138ed78f800342968c523bdde947e7a305b8594b910a0fea2ab83c3c6d385"}, + {file = "joblib-1.2.0.tar.gz", hash = "sha256:e1cee4a79e4af22881164f218d4311f60074197fb707e082e803b61f6d137018"}, +] + +[[package]] +name = "keyring" +version = "23.13.1" +description = "Store and access your passwords safely." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"}, + {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +completion = ["shtab"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "langchain" +version = "0.0.166" +description = "Building applications with LLMs through composability" +category = "main" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchain-0.0.166-py3-none-any.whl", hash = "sha256:32417cc38ba211d46c3e97f29cb8124175fe46047bda14a4c634351b005acd21"}, + {file = "langchain-0.0.166.tar.gz", hash = "sha256:fb1e90eb0aeef9c574e6683586bfbfed1974e187dd8261b571cb33888c35a92e"}, +] + +[package.dependencies] +aiohttp = ">=3.8.3,<4.0.0" +async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} +dataclasses-json = ">=0.5.7,<0.6.0" +numexpr = ">=2.8.4,<3.0.0" +numpy = ">=1,<2" +openapi-schema-pydantic = ">=1.2,<2.0" +pydantic = ">=1,<2" +PyYAML = ">=5.4.1" +requests = ">=2,<3" +SQLAlchemy = ">=1.4,<3" +tenacity = ">=8.1.0,<9.0.0" +tqdm = ">=4.48.0" + +[package.extras] +all = ["O365 (>=2.0.26,<3.0.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.2.6,<0.3.0)", "arxiv (>=1.4,<2.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "beautifulsoup4 (>=4,<5)", "clickhouse-connect (>=0.5.14,<0.6.0)", "cohere (>=3,<4)", "deeplake (>=3.3.0,<4.0.0)", "docarray (>=0.31.0,<0.32.0)", "duckduckgo-search (>=2.8.6,<3.0.0)", "elasticsearch (>=8,<9)", "faiss-cpu (>=1,<2)", "google-api-python-client (==2.70.0)", "google-search-results (>=2,<3)", "gptcache (>=0.1.7)", "hnswlib (>=0.7.0,<0.8.0)", "html2text (>=2020.1.16,<2021.0.0)", "huggingface_hub (>=0,<1)", "jina (>=3.14,<4.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "lancedb (>=0.1,<0.2)", "lark (>=1.1.5,<2.0.0)", "manifest-ml (>=0.0.1,<0.0.2)", "networkx (>=2.6.3,<3.0.0)", "nlpcloud (>=1,<2)", "nltk (>=3,<4)", "nomic (>=1.0.43,<2.0.0)", "openai (>=0,<1)", "opensearch-py (>=2.0.0,<3.0.0)", "pexpect (>=4.8.0,<5.0.0)", "pgvector (>=0.1.6,<0.2.0)", "pinecone-client (>=2,<3)", "pinecone-text (>=0.4.2,<0.5.0)", "protobuf (==3.19)", "psycopg2-binary (>=2.9.5,<3.0.0)", "pyowm (>=3.3.0,<4.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pytesseract (>=0.3.10,<0.4.0)", "pyvespa (>=0.33.0,<0.34.0)", "qdrant-client (>=1.1.2,<2.0.0)", "redis (>=4,<5)", "sentence-transformers (>=2,<3)", "spacy (>=3,<4)", "tensorflow-text (>=2.11.0,<3.0.0)", "tiktoken (>=0.3.2,<0.4.0)", "torch (>=1,<3)", "transformers (>=4,<5)", "weaviate-client (>=3,<4)", "wikipedia (>=1,<2)", "wolframalpha (==5.0.0)"] +azure = ["azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "openai (>=0,<1)"] +cohere = ["cohere (>=3,<4)"] +embeddings = ["sentence-transformers (>=2,<3)"] +extended-testing = ["pdfminer-six (>=20221105,<20221106)", "pypdf (>=3.4.0,<4.0.0)"] +hnswlib = ["docarray (>=0.31.0,<0.32.0)", "hnswlib (>=0.7.0,<0.8.0)", "protobuf (==3.19)"] +in-memory-store = ["docarray (>=0.31.0,<0.32.0)"] +llms = ["anthropic (>=0.2.6,<0.3.0)", "cohere (>=3,<4)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (>=0,<1)", "torch (>=1,<3)", "transformers (>=4,<5)"] +openai = ["openai (>=0,<1)"] +qdrant = ["qdrant-client (>=1.1.2,<2.0.0)"] + +[[package]] +name = "llama-index" +version = "0.5.4" +description = "Interface between LLMs and your data." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "llama_index-0.5.4.tar.gz", hash = "sha256:f6c556370f7f03039bc19be4ca594b00f38d7cea18399f6045bc776fa3a90d15"}, +] + +[package.dependencies] +dataclasses_json = "*" +langchain = "*" +numpy = "*" +openai = ">=0.26.4" +pandas = "*" +tenacity = ">=8.2.0,<9.0.0" +tiktoken = "*" + +[[package]] +name = "loguru" +version = "0.7.0" +description = "Python logging made (stupidly) simple" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"}, + {file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"] + +[[package]] +name = "lxml" +version = "4.9.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ + {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"}, + {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"}, + {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"}, + {file = "lxml-4.9.2-cp27-cp27m-win32.whl", hash = "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de"}, + {file = "lxml-4.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3"}, + {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50"}, + {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975"}, + {file = "lxml-4.9.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4"}, + {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7"}, + {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184"}, + {file = "lxml-4.9.2-cp310-cp310-win32.whl", hash = "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda"}, + {file = "lxml-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380"}, + {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92"}, + {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1"}, + {file = "lxml-4.9.2-cp311-cp311-win32.whl", hash = "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33"}, + {file = "lxml-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd"}, + {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"}, + {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"}, + {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"}, + {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"}, + {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e"}, + {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"}, + {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"}, + {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"}, + {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"}, + {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45"}, + {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e"}, + {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b"}, + {file = "lxml-4.9.2-cp37-cp37m-win32.whl", hash = "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe"}, + {file = "lxml-4.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9"}, + {file = "lxml-4.9.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c"}, + {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f"}, + {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457"}, + {file = "lxml-4.9.2-cp38-cp38-win32.whl", hash = "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b"}, + {file = "lxml-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7"}, + {file = "lxml-4.9.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5"}, + {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5"}, + {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2"}, + {file = "lxml-4.9.2-cp39-cp39-win32.whl", hash = "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1"}, + {file = "lxml-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f"}, + {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c"}, + {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"}, + {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "lz4" +version = "4.3.2" +description = "LZ4 Bindings for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "lz4-4.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1c4c100d99eed7c08d4e8852dd11e7d1ec47a3340f49e3a96f8dfbba17ffb300"}, + {file = "lz4-4.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:edd8987d8415b5dad25e797043936d91535017237f72fa456601be1479386c92"}, + {file = "lz4-4.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7c50542b4ddceb74ab4f8b3435327a0861f06257ca501d59067a6a482535a77"}, + {file = "lz4-4.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5614d8229b33d4a97cb527db2a1ac81308c6e796e7bdb5d1309127289f69d5"}, + {file = "lz4-4.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f00a9ba98f6364cadda366ae6469b7b3568c0cced27e16a47ddf6b774169270"}, + {file = "lz4-4.3.2-cp310-cp310-win32.whl", hash = "sha256:b10b77dc2e6b1daa2f11e241141ab8285c42b4ed13a8642495620416279cc5b2"}, + {file = "lz4-4.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:86480f14a188c37cb1416cdabacfb4e42f7a5eab20a737dac9c4b1c227f3b822"}, + {file = "lz4-4.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7c2df117def1589fba1327dceee51c5c2176a2b5a7040b45e84185ce0c08b6a3"}, + {file = "lz4-4.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1f25eb322eeb24068bb7647cae2b0732b71e5c639e4e4026db57618dcd8279f0"}, + {file = "lz4-4.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8df16c9a2377bdc01e01e6de5a6e4bbc66ddf007a6b045688e285d7d9d61d1c9"}, + {file = "lz4-4.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f571eab7fec554d3b1db0d666bdc2ad85c81f4b8cb08906c4c59a8cad75e6e22"}, + {file = "lz4-4.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7211dc8f636ca625abc3d4fb9ab74e5444b92df4f8d58ec83c8868a2b0ff643d"}, + {file = "lz4-4.3.2-cp311-cp311-win32.whl", hash = "sha256:867664d9ca9bdfce840ac96d46cd8838c9ae891e859eb98ce82fcdf0e103a947"}, + {file = "lz4-4.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:a6a46889325fd60b8a6b62ffc61588ec500a1883db32cddee9903edfba0b7584"}, + {file = "lz4-4.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a85b430138882f82f354135b98c320dafb96fc8fe4656573d95ab05de9eb092"}, + {file = "lz4-4.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65d5c93f8badacfa0456b660285e394e65023ef8071142e0dcbd4762166e1be0"}, + {file = "lz4-4.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b50f096a6a25f3b2edca05aa626ce39979d63c3b160687c8c6d50ac3943d0ba"}, + {file = "lz4-4.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:200d05777d61ba1ff8d29cb51c534a162ea0b4fe6d3c28be3571a0a48ff36080"}, + {file = "lz4-4.3.2-cp37-cp37m-win32.whl", hash = "sha256:edc2fb3463d5d9338ccf13eb512aab61937be50aa70734bcf873f2f493801d3b"}, + {file = "lz4-4.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:83acfacab3a1a7ab9694333bcb7950fbeb0be21660d236fd09c8337a50817897"}, + {file = "lz4-4.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a9eec24ec7d8c99aab54de91b4a5a149559ed5b3097cf30249b665689b3d402"}, + {file = "lz4-4.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:31d72731c4ac6ebdce57cd9a5cabe0aecba229c4f31ba3e2c64ae52eee3fdb1c"}, + {file = "lz4-4.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83903fe6db92db0be101acedc677aa41a490b561567fe1b3fe68695b2110326c"}, + {file = "lz4-4.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926b26db87ec8822cf1870efc3d04d06062730ec3279bbbd33ba47a6c0a5c673"}, + {file = "lz4-4.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e05afefc4529e97c08e65ef92432e5f5225c0bb21ad89dee1e06a882f91d7f5e"}, + {file = "lz4-4.3.2-cp38-cp38-win32.whl", hash = "sha256:ad38dc6a7eea6f6b8b642aaa0683253288b0460b70cab3216838747163fb774d"}, + {file = "lz4-4.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:7e2dc1bd88b60fa09b9b37f08553f45dc2b770c52a5996ea52b2b40f25445676"}, + {file = "lz4-4.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:edda4fb109439b7f3f58ed6bede59694bc631c4b69c041112b1b7dc727fffb23"}, + {file = "lz4-4.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ca83a623c449295bafad745dcd399cea4c55b16b13ed8cfea30963b004016c9"}, + {file = "lz4-4.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5ea0e788dc7e2311989b78cae7accf75a580827b4d96bbaf06c7e5a03989bd5"}, + {file = "lz4-4.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a98b61e504fb69f99117b188e60b71e3c94469295571492a6468c1acd63c37ba"}, + {file = "lz4-4.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4931ab28a0d1c133104613e74eec1b8bb1f52403faabe4f47f93008785c0b929"}, + {file = "lz4-4.3.2-cp39-cp39-win32.whl", hash = "sha256:ec6755cacf83f0c5588d28abb40a1ac1643f2ff2115481089264c7630236618a"}, + {file = "lz4-4.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:4caedeb19e3ede6c7a178968b800f910db6503cb4cb1e9cc9221157572139b49"}, + {file = "lz4-4.3.2.tar.gz", hash = "sha256:e1431d84a9cfb23e6773e72078ce8e65cad6745816d4cbf9ae67da5ea419acda"}, +] + +[package.extras] +docs = ["sphinx (>=1.6.0)", "sphinx-bootstrap-theme"] +flake8 = ["flake8"] +tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "marshmallow" +version = "3.19.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"}, + {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-enum" +version = "1.5.1" +description = "Enum field for Marshmallow" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"}, + {file = "marshmallow_enum-1.5.1-py2.py3-none-any.whl", hash = "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"}, +] + +[package.dependencies] +marshmallow = ">=2.0.0" + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "more-itertools" +version = "9.1.0" +description = "More routines for operating on iterables, beyond itertools" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, + {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + +[[package]] +name = "msal" +version = "1.22.0" +description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "msal-1.22.0-py2.py3-none-any.whl", hash = "sha256:9120b7eafdf061c92f7b3d744e5f325fca35873445fa8ffebb40b1086a13dd58"}, + {file = "msal-1.22.0.tar.gz", hash = "sha256:8a82f5375642c1625c89058018430294c109440dce42ea667d466c2cab520acd"}, +] + +[package.dependencies] +cryptography = ">=0.6,<43" +PyJWT = {version = ">=1.0.0,<3", extras = ["crypto"]} +requests = ">=2.0.0,<3" + +[package.extras] +broker = ["pymsalruntime (>=0.13.2,<0.14)"] + +[[package]] +name = "msal-extensions" +version = "1.0.0" +description = "Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "msal-extensions-1.0.0.tar.gz", hash = "sha256:c676aba56b0cce3783de1b5c5ecfe828db998167875126ca4b47dc6436451354"}, + {file = "msal_extensions-1.0.0-py2.py3-none-any.whl", hash = "sha256:91e3db9620b822d0ed2b4d1850056a0f133cba04455e62f11612e40f5502f2ee"}, +] + +[package.dependencies] +msal = ">=0.4.1,<2.0.0" +portalocker = [ + {version = ">=1.0,<3", markers = "python_version >= \"3.5\" and platform_system != \"Windows\""}, + {version = ">=1.6,<3", markers = "python_version >= \"3.5\" and platform_system == \"Windows\""}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "networkx" +version = "3.1" +description = "Python package for creating and manipulating graphs and networks" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, + {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, +] + +[package.extras] +default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] +developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] +test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "nltk" +version = "3.8.1" +description = "Natural Language Toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "nltk-3.8.1-py3-none-any.whl", hash = "sha256:fd5c9109f976fa86bcadba8f91e47f5e9293bd034474752e92a520f81c93dda5"}, + {file = "nltk-3.8.1.zip", hash = "sha256:1834da3d0682cba4f2cede2f9aad6b0fafb6461ba451db0efb6f9c39798d64d3"}, +] + +[package.dependencies] +click = "*" +joblib = "*" +regex = ">=2021.8.3" +tqdm = "*" + +[package.extras] +all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"] +corenlp = ["requests"] +machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] + +[[package]] +name = "numexpr" +version = "2.8.4" +description = "Fast numerical expression evaluator for NumPy" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "numexpr-2.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a75967d46b6bd56455dd32da6285e5ffabe155d0ee61eef685bbfb8dafb2e484"}, + {file = "numexpr-2.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db93cf1842f068247de631bfc8af20118bf1f9447cd929b531595a5e0efc9346"}, + {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bca95f4473b444428061d4cda8e59ac564dc7dc6a1dea3015af9805c6bc2946"}, + {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e34931089a6bafc77aaae21f37ad6594b98aa1085bb8b45d5b3cd038c3c17d9"}, + {file = "numexpr-2.8.4-cp310-cp310-win32.whl", hash = "sha256:f3a920bfac2645017110b87ddbe364c9c7a742870a4d2f6120b8786c25dc6db3"}, + {file = "numexpr-2.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:6931b1e9d4f629f43c14b21d44f3f77997298bea43790cfcdb4dd98804f90783"}, + {file = "numexpr-2.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9400781553541f414f82eac056f2b4c965373650df9694286b9bd7e8d413f8d8"}, + {file = "numexpr-2.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ee9db7598dd4001138b482342b96d78110dd77cefc051ec75af3295604dde6a"}, + {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff5835e8af9a212e8480003d731aad1727aaea909926fd009e8ae6a1cba7f141"}, + {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655d84eb09adfee3c09ecf4a89a512225da153fdb7de13c447404b7d0523a9a7"}, + {file = "numexpr-2.8.4-cp311-cp311-win32.whl", hash = "sha256:5538b30199bfc68886d2be18fcef3abd11d9271767a7a69ff3688defe782800a"}, + {file = "numexpr-2.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:3f039321d1c17962c33079987b675fb251b273dbec0f51aac0934e932446ccc3"}, + {file = "numexpr-2.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c867cc36cf815a3ec9122029874e00d8fbcef65035c4a5901e9b120dd5d626a2"}, + {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059546e8f6283ccdb47c683101a890844f667fa6d56258d48ae2ecf1b3875957"}, + {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845a6aa0ed3e2a53239b89c1ebfa8cf052d3cc6e053c72805e8153300078c0b1"}, + {file = "numexpr-2.8.4-cp37-cp37m-win32.whl", hash = "sha256:a38664e699526cb1687aefd9069e2b5b9387da7feac4545de446141f1ef86f46"}, + {file = "numexpr-2.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaec59e9bf70ff05615c34a8b8d6c7bd042bd9f55465d7b495ea5436f45319d0"}, + {file = "numexpr-2.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b318541bf3d8326682ebada087ba0050549a16d8b3fa260dd2585d73a83d20a7"}, + {file = "numexpr-2.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b076db98ca65eeaf9bd224576e3ac84c05e451c0bd85b13664b7e5f7b62e2c70"}, + {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f12cc851240f7911a47c91aaf223dba753e98e46dff3017282e633602e76a7"}, + {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c368aa35ae9b18840e78b05f929d3a7b3abccdba9630a878c7db74ca2368339"}, + {file = "numexpr-2.8.4-cp38-cp38-win32.whl", hash = "sha256:b96334fc1748e9ec4f93d5fadb1044089d73fb08208fdb8382ed77c893f0be01"}, + {file = "numexpr-2.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:a6d2d7740ae83ba5f3531e83afc4b626daa71df1ef903970947903345c37bd03"}, + {file = "numexpr-2.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:77898fdf3da6bb96aa8a4759a8231d763a75d848b2f2e5c5279dad0b243c8dfe"}, + {file = "numexpr-2.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df35324666b693f13a016bc7957de7cc4d8801b746b81060b671bf78a52b9037"}, + {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ac9cfe6d0078c5fc06ba1c1bbd20b8783f28c6f475bbabd3cad53683075cab"}, + {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3a1f6b24214a1ab826e9c1c99edf1686c8e307547a9aef33910d586f626d01"}, + {file = "numexpr-2.8.4-cp39-cp39-win32.whl", hash = "sha256:7d71add384adc9119568d7e9ffa8a35b195decae81e0abf54a2b7779852f0637"}, + {file = "numexpr-2.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:9f096d707290a6a00b6ffdaf581ee37331109fb7b6c8744e9ded7c779a48e517"}, + {file = "numexpr-2.8.4.tar.gz", hash = "sha256:d5432537418d18691b9115d615d6daa17ee8275baef3edf1afbbf8bc69806147"}, +] + +[package.dependencies] +numpy = ">=1.13.3" + +[[package]] +name = "numpy" +version = "1.24.3" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"}, + {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"}, + {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"}, + {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"}, + {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"}, + {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"}, + {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"}, + {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"}, + {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"}, + {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"}, + {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"}, +] + +[[package]] +name = "openai" +version = "0.27.6" +description = "Python client library for the OpenAI API" +category = "main" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-0.27.6-py3-none-any.whl", hash = "sha256:1f07ed06f1cfc6c25126107193726fe4cf476edcc4e1485cd9eb708f068f2606"}, + {file = "openai-0.27.6.tar.gz", hash = "sha256:63ca9f6ac619daef8c1ddec6d987fe6aa1c87a9bfdce31ff253204d077222375"}, +] + +[package.dependencies] +aiohttp = "*" +requests = ">=2.20" +tqdm = "*" + +[package.extras] +datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] +embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] +wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] + +[[package]] +name = "openapi-schema-pydantic" +version = "1.2.4" +description = "OpenAPI (v3) specification schema as pydantic class" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "openapi-schema-pydantic-1.2.4.tar.gz", hash = "sha256:3e22cf58b74a69f752cc7e5f1537f6e44164282db2700cbbcd3bb99ddd065196"}, + {file = "openapi_schema_pydantic-1.2.4-py3-none-any.whl", hash = "sha256:a932ecc5dcbb308950282088956e94dea069c9823c84e507d64f6b622222098c"}, +] + +[package.dependencies] +pydantic = ">=1.8.2" + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pandas" +version = "2.0.1" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70a996a1d2432dadedbb638fe7d921c88b0cc4dd90374eab51bb33dc6c0c2a12"}, + {file = "pandas-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:909a72b52175590debbf1d0c9e3e6bce2f1833c80c76d80bd1aa09188be768e5"}, + {file = "pandas-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe7914d8ddb2d54b900cec264c090b88d141a1eed605c9539a187dbc2547f022"}, + {file = "pandas-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a514ae436b23a92366fbad8365807fc0eed15ca219690b3445dcfa33597a5cc"}, + {file = "pandas-2.0.1-cp310-cp310-win32.whl", hash = "sha256:12bd6618e3cc737c5200ecabbbb5eaba8ab645a4b0db508ceeb4004bb10b060e"}, + {file = "pandas-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:2b6fe5f7ce1cba0e74188c8473c9091ead9b293ef0a6794939f8cc7947057abd"}, + {file = "pandas-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:00959a04a1d7bbc63d75a768540fb20ecc9e65fd80744c930e23768345a362a7"}, + {file = "pandas-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af2449e9e984dfad39276b885271ba31c5e0204ffd9f21f287a245980b0e4091"}, + {file = "pandas-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910df06feaf9935d05247db6de452f6d59820e432c18a2919a92ffcd98f8f79b"}, + {file = "pandas-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0067f2419f933101bdc6001bcea1d50812afbd367b30943417d67fbb99678"}, + {file = "pandas-2.0.1-cp311-cp311-win32.whl", hash = "sha256:7b8395d335b08bc8b050590da264f94a439b4770ff16bb51798527f1dd840388"}, + {file = "pandas-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:8db5a644d184a38e6ed40feeb12d410d7fcc36648443defe4707022da127fc35"}, + {file = "pandas-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7bbf173d364130334e0159a9a034f573e8b44a05320995127cf676b85fd8ce86"}, + {file = "pandas-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c0853d487b6c868bf107a4b270a823746175b1932093b537b9b76c639fc6f7e"}, + {file = "pandas-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25e23a03f7ad7211ffa30cb181c3e5f6d96a8e4cb22898af462a7333f8a74eb"}, + {file = "pandas-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e09a53a4fe8d6ae2149959a2d02e1ef2f4d2ceb285ac48f74b79798507e468b4"}, + {file = "pandas-2.0.1-cp38-cp38-win32.whl", hash = "sha256:a2564629b3a47b6aa303e024e3d84e850d36746f7e804347f64229f8c87416ea"}, + {file = "pandas-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:03e677c6bc9cfb7f93a8b617d44f6091613a5671ef2944818469be7b42114a00"}, + {file = "pandas-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d099ecaa5b9e977b55cd43cf842ec13b14afa1cfa51b7e1179d90b38c53ce6a"}, + {file = "pandas-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a37ee35a3eb6ce523b2c064af6286c45ea1c7ff882d46e10d0945dbda7572753"}, + {file = "pandas-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:320b180d125c3842c5da5889183b9a43da4ebba375ab2ef938f57bf267a3c684"}, + {file = "pandas-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18d22cb9043b6c6804529810f492ab09d638ddf625c5dea8529239607295cb59"}, + {file = "pandas-2.0.1-cp39-cp39-win32.whl", hash = "sha256:90d1d365d77d287063c5e339f49b27bd99ef06d10a8843cf00b1a49326d492c1"}, + {file = "pandas-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:99f7192d8b0e6daf8e0d0fd93baa40056684e4b4aaaef9ea78dff34168e1f2f0"}, + {file = "pandas-2.0.1.tar.gz", hash = "sha256:19b8e5270da32b41ebf12f0e7165efa7024492e9513fb46fb631c5022ae5709d"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] + +[[package]] +name = "pgvector" +version = "0.1.7" +description = "pgvector support for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pgvector-0.1.7-py2.py3-none-any.whl", hash = "sha256:b0da0289959372f916b96c1da7c57437725c7aa33fa0c75b4a53c3677369bdd5"}, +] + +[package.dependencies] +numpy = "*" + +[[package]] +name = "pillow" +version = "9.5.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, + {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, + {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, + {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, + {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, + {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, + {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, + {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, + {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, + {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, + {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, + {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, + {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, + {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, + {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, + {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pinecone-client" +version = "2.2.1" +description = "Pinecone client and SDK" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pinecone-client-2.2.1.tar.gz", hash = "sha256:0878dcaee447c46c8d1b3d71c854689daa7e548e5009a171780907c7d4e74789"}, + {file = "pinecone_client-2.2.1-py3-none-any.whl", hash = "sha256:6976a22aee57a9813378607506c8c36b0317dfa36a08a5397aaaeab2eef66c1b"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +loguru = ">=0.5.0" +numpy = "*" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4" +requests = ">=2.19.0" +tqdm = ">=4.64.1" +typing-extensions = ">=3.7.4" +urllib3 = ">=1.21.1" + +[package.extras] +grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv2 (==0.1.0)", "grpcio (>=1.44.0)", "lz4 (>=3.1.3)", "protobuf (==3.19.3)"] + +[[package]] +name = "pkginfo" +version = "1.9.6" +description = "Query metadata from sdists / bdists / installed packages." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, + {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "portalocker" +version = "2.7.0" +description = "Wraps the portalocker recipe for easy usage" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "portalocker-2.7.0-py2.py3-none-any.whl", hash = "sha256:a07c5b4f3985c3cf4798369631fb7011adb498e2a46d8440efc75a8f29a0f983"}, + {file = "portalocker-2.7.0.tar.gz", hash = "sha256:032e81d534a88ec1736d03f780ba073f047a06c478b06e2937486f334e955c51"}, +] + +[package.dependencies] +pywin32 = {version = ">=226", markers = "platform_system == \"Windows\""} + +[package.extras] +docs = ["sphinx (>=1.7.1)"] +redis = ["redis"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)"] + +[[package]] +name = "postgrest" +version = "0.10.6" +description = "PostgREST client for Python. This library provides an ORM interface to PostgREST." +category = "main" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "postgrest-0.10.6-py3-none-any.whl", hash = "sha256:7302068ce3cd80e761e35d6d665d3e65632442488258e3299c008013119d7fe6"}, + {file = "postgrest-0.10.6.tar.gz", hash = "sha256:ee145d53ea8642a16fa7f42848443baa08ae1e6f41e071865f5f54bcb3b24aa3"}, +] + +[package.dependencies] +deprecation = ">=2.1.0,<3.0.0" +httpx = ">=0.23.0,<0.24.0" +pydantic = ">=1.9.0,<2.0.0" +strenum = ">=0.4.9,<0.5.0" + +[[package]] +name = "posthog" +version = "3.0.1" +description = "Integrate PostHog into any python application." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "posthog-3.0.1-py2.py3-none-any.whl", hash = "sha256:9c7f92fecc713257d4b2710d05b456569c9156fbdd3e85655ba7ba5ba6c7b3ae"}, + {file = "posthog-3.0.1.tar.gz", hash = "sha256:57d2791ff5752ce56ba0f9bb8876faf3ca9208f1c2c6ceaeb5a2504c34493767"}, +] + +[package.dependencies] +backoff = ">=1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] +sentry = ["django", "sentry-sdk"] +test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest"] + +[[package]] +name = "protobuf" +version = "4.23.0" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-4.23.0-cp310-abi3-win32.whl", hash = "sha256:6c16657d6717a0c62d5d740cb354fbad1b0d8cb811669e06fc1caa0ff4799ddd"}, + {file = "protobuf-4.23.0-cp310-abi3-win_amd64.whl", hash = "sha256:baca40d067dddd62141a129f244703160d278648b569e90bb0e3753067644711"}, + {file = "protobuf-4.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2b94bd6df92d71bd1234a2ffe7ce96ddf6d10cf637a18d6b55ad0a89fbb7fc21"}, + {file = "protobuf-4.23.0-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:9f5a0fbfcdcc364f3986f9ed9f8bb1328fb84114fd790423ff3d7fdb0f85c2d1"}, + {file = "protobuf-4.23.0-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ebde3a023b8e11bfa6c890ef34cd6a8b47d586f26135e86c21344fe433daf2e2"}, + {file = "protobuf-4.23.0-cp37-cp37m-win32.whl", hash = "sha256:7cb5b9a05ce52c6a782bb97de52679bd3438ff2b7460eff5da348db65650f227"}, + {file = "protobuf-4.23.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6fe180b56e1169d72ecc4acbd39186339aed20af5384531b8e8979b02bbee159"}, + {file = "protobuf-4.23.0-cp38-cp38-win32.whl", hash = "sha256:d5a35ff54e3f62e8fc7be02bb0d2fbc212bba1a5a9cc2748090690093996f07b"}, + {file = "protobuf-4.23.0-cp38-cp38-win_amd64.whl", hash = "sha256:e62fb869762b4ba18666370e2f8a18f17f8ab92dd4467295c6d38be6f8fef60b"}, + {file = "protobuf-4.23.0-cp39-cp39-win32.whl", hash = "sha256:03eee35b60317112a72d19c54d0bff7bc58ff12fea4cd7b018232bd99758ffdf"}, + {file = "protobuf-4.23.0-cp39-cp39-win_amd64.whl", hash = "sha256:36f5370a930cb77c8ad2f4135590c672d0d2c72d4a707c7d0058dce4b4b4a598"}, + {file = "protobuf-4.23.0-py3-none-any.whl", hash = "sha256:9744e934ea5855d12191040ea198eaf704ac78665d365a89d9572e3b627c2688"}, + {file = "protobuf-4.23.0.tar.gz", hash = "sha256:5f1eba1da2a2f3f7df469fccddef3cc060b8a16cfe3cc65961ad36b4dbcf59c5"}, +] + +[[package]] +name = "psycopg2" +version = "2.9.6" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-2.9.6-cp310-cp310-win32.whl", hash = "sha256:f7a7a5ee78ba7dc74265ba69e010ae89dae635eea0e97b055fb641a01a31d2b1"}, + {file = "psycopg2-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:f75001a1cbbe523e00b0ef896a5a1ada2da93ccd752b7636db5a99bc57c44494"}, + {file = "psycopg2-2.9.6-cp311-cp311-win32.whl", hash = "sha256:53f4ad0a3988f983e9b49a5d9765d663bbe84f508ed655affdb810af9d0972ad"}, + {file = "psycopg2-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b81fcb9ecfc584f661b71c889edeae70bae30d3ef74fa0ca388ecda50b1222b7"}, + {file = "psycopg2-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:11aca705ec888e4f4cea97289a0bf0f22a067a32614f6ef64fcf7b8bfbc53744"}, + {file = "psycopg2-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:36c941a767341d11549c0fbdbb2bf5be2eda4caf87f65dfcd7d146828bd27f39"}, + {file = "psycopg2-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:869776630c04f335d4124f120b7fb377fe44b0a7645ab3c34b4ba42516951889"}, + {file = "psycopg2-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:a8ad4a47f42aa6aec8d061fdae21eaed8d864d4bb0f0cade5ad32ca16fcd6258"}, + {file = "psycopg2-2.9.6-cp38-cp38-win32.whl", hash = "sha256:2362ee4d07ac85ff0ad93e22c693d0f37ff63e28f0615a16b6635a645f4b9214"}, + {file = "psycopg2-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:d24ead3716a7d093b90b27b3d73459fe8cd90fd7065cf43b3c40966221d8c394"}, + {file = "psycopg2-2.9.6-cp39-cp39-win32.whl", hash = "sha256:1861a53a6a0fd248e42ea37c957d36950da00266378746588eab4f4b5649e95f"}, + {file = "psycopg2-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:ded2faa2e6dfb430af7713d87ab4abbfc764d8d7fb73eafe96a24155f906ebf5"}, + {file = "psycopg2-2.9.6.tar.gz", hash = "sha256:f15158418fd826831b28585e2ab48ed8df2d0d98f502a2b4fe619e7d5ca29011"}, +] + +[[package]] +name = "psycopg2cffi" +version = "2.9.0" +description = ".. image:: https://travis-ci.org/chtd/psycopg2cffi.svg?branch=master" +category = "main" +optional = true +python-versions = "*" +files = [ + {file = "psycopg2cffi-2.9.0.tar.gz", hash = "sha256:7e272edcd837de3a1d12b62185eb85c45a19feda9e62fa1b120c54f9e8d35c52"}, +] + +[package.dependencies] +cffi = ">=1.0" +six = "*" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.17" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodomex-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:12056c38e49d972f9c553a3d598425f8a1c1d35b2e4330f89d5ff1ffb70de041"}, + {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab33c2d9f275e05e235dbca1063753b5346af4a5cac34a51fa0da0d4edfb21d7"}, + {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:caa937ff29d07a665dfcfd7a84f0d4207b2ebf483362fa9054041d67fdfacc20"}, + {file = "pycryptodomex-3.17-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:db23d7341e21b273d2440ec6faf6c8b1ca95c8894da612e165be0b89a8688340"}, + {file = "pycryptodomex-3.17-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f854c8476512cebe6a8681cc4789e4fcff6019c17baa0fd72b459155dc605ab4"}, + {file = "pycryptodomex-3.17-cp27-cp27m-win32.whl", hash = "sha256:a57e3257bacd719769110f1f70dd901c5b6955e9596ad403af11a3e6e7e3311c"}, + {file = "pycryptodomex-3.17-cp27-cp27m-win_amd64.whl", hash = "sha256:d38ab9e53b1c09608ba2d9b8b888f1e75d6f66e2787e437adb1fecbffec6b112"}, + {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:3c2516b42437ae6c7a29ef3ddc73c8d4714e7b6df995b76be4695bbe4b3b5cd2"}, + {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5c23482860302d0d9883404eaaa54b0615eefa5274f70529703e2c43cc571827"}, + {file = "pycryptodomex-3.17-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:7a8dc3ee7a99aae202a4db52de5a08aa4d01831eb403c4d21da04ec2f79810db"}, + {file = "pycryptodomex-3.17-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:7cc28dd33f1f3662d6da28ead4f9891035f63f49d30267d3b41194c8778997c8"}, + {file = "pycryptodomex-3.17-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:2d4d395f109faba34067a08de36304e846c791808524614c731431ee048fe70a"}, + {file = "pycryptodomex-3.17-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:55eed98b4150a744920597c81b3965b632038781bab8a08a12ea1d004213c600"}, + {file = "pycryptodomex-3.17-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:7fa0b52df90343fafe319257b31d909be1d2e8852277fb0376ba89d26d2921db"}, + {file = "pycryptodomex-3.17-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78f0ddd4adc64baa39b416f3637aaf99f45acb0bcdc16706f0cc7ebfc6f10109"}, + {file = "pycryptodomex-3.17-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4fa037078e92c7cc49f6789a8bac3de06856740bb2038d05f2d9a2e4b165d59"}, + {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:88b0d5bb87eaf2a31e8a759302b89cf30c97f2f8ca7d83b8c9208abe8acb447a"}, + {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:6feedf4b0e36b395329b4186a805f60f900129cdf0170e120ecabbfcb763995d"}, + {file = "pycryptodomex-3.17-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a6651a07f67c28b6e978d63aa3a3fccea0feefed9a8453af3f7421a758461b7"}, + {file = "pycryptodomex-3.17-cp35-abi3-win32.whl", hash = "sha256:32e764322e902bbfac49ca1446604d2839381bbbdd5a57920c9daaf2e0b778df"}, + {file = "pycryptodomex-3.17-cp35-abi3-win_amd64.whl", hash = "sha256:4b51e826f0a04d832eda0790bbd0665d9bfe73e5a4d8ea93b6a9b38beeebe935"}, + {file = "pycryptodomex-3.17-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:d4cf0128da167562c49b0e034f09e9cedd733997354f2314837c2fa461c87bb1"}, + {file = "pycryptodomex-3.17-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:c92537b596bd5bffb82f8964cabb9fef1bca8a28a9e0a69ffd3ec92a4a7ad41b"}, + {file = "pycryptodomex-3.17-pp27-pypy_73-win32.whl", hash = "sha256:599bb4ae4bbd614ca05f49bd4e672b7a250b80b13ae1238f05fd0f09d87ed80a"}, + {file = "pycryptodomex-3.17-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4c4674f4b040321055c596aac926d12f7f6859dfe98cd12f4d9453b43ab6adc8"}, + {file = "pycryptodomex-3.17-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a3648025e4ddb72d43addab764336ba2e670c8377dba5dd752e42285440d31"}, + {file = "pycryptodomex-3.17-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8a11f578bd0851b02719c862d55d3ee18d906c8b68a9c09f8c564d6bb5b92"}, + {file = "pycryptodomex-3.17-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:23d83b610bd97704f0cd3acc48d99b76a15c8c1540d8665c94d514a49905bad7"}, + {file = "pycryptodomex-3.17-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd29d35ac80755e5c0a99d96b44fb9abbd7e871849581ea6a4cb826d24267537"}, + {file = "pycryptodomex-3.17-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64b876d57cb894b31056ad8dd6a6ae1099b117ae07a3d39707221133490e5715"}, + {file = "pycryptodomex-3.17-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8bf4fdcad7d66beb744957db8717afc12d176e3fd9c5d106835133881a049b"}, + {file = "pycryptodomex-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c84689c73358dfc23f9fdcff2cb9e7856e65e2ce3b5ed8ff630d4c9bdeb1867b"}, + {file = "pycryptodomex-3.17.tar.gz", hash = "sha256:0af93aad8d62e810247beedef0261c148790c52f3cd33643791cc6396dd217c1"}, +] + +[[package]] +name = "pydantic" +version = "1.10.7" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyjwt" +version = "2.7.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.7.0-py3-none-any.whl", hash = "sha256:ba2b425b15ad5ef12f200dc67dd56af4e26de2331f965c5439994dad075876e1"}, + {file = "PyJWT-2.7.0.tar.gz", hash = "sha256:bd6ca4a3c4285c1a2d4349e5a035fdf8fb94e04ccd0fcbe6ba289dae9cc3e074"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pymilvus" +version = "2.2.8" +description = "Python Sdk for Milvus" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymilvus-2.2.8-py3-none-any.whl", hash = "sha256:ccf427700c886b3cd73455b40fdac0bf2087243ba1eac5cb6d5745571d1ae467"}, + {file = "pymilvus-2.2.8.tar.gz", hash = "sha256:f866c44a17403a9b7ba681f0c0a28aa8cdecba0f18b8c43f31879d627bd7d1e8"}, +] + +[package.dependencies] +environs = "<=9.5.0" +grpcio = ">=1.49.1,<=1.53.0" +pandas = ">=1.2.4" +protobuf = ">=3.20.0" +ujson = ">=2.0.0" + +[[package]] +name = "pypdf2" +version = "3.0.1" +description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"}, + {file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"}, +] + +[package.extras] +crypto = ["PyCryptodome"] +dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"] +docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] +full = ["Pillow", "PyCryptodome"] +image = ["Pillow"] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.20.3" +description = "Pytest support for asyncio" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"}, + {file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"}, +] + +[package.dependencies] +pytest = ">=6.1.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-cov" +version = "4.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "0.21.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, + {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-gitlab" +version = "3.14.0" +description = "Interact with GitLab API" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "python-gitlab-3.14.0.tar.gz", hash = "sha256:ef3b8960faeee9880f82b0872d807e3fab94ace12b0d2a8418a97875c8812d3c"}, + {file = "python_gitlab-3.14.0-py3-none-any.whl", hash = "sha256:da614c014c6860147783dde8c216218d8fc6bd83a8bd2e3929dcdf11b211aa58"}, +] + +[package.dependencies] +requests = ">=2.25.0" +requests-toolbelt = ">=0.10.1" + +[package.extras] +autocompletion = ["argcomplete (>=1.10.0,<3)"] +yaml = ["PyYaml (>=5.2)"] + +[[package]] +name = "python-multipart" +version = "0.0.6" +description = "A streaming multipart parser for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python_multipart-0.0.6-py3-none-any.whl", hash = "sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"}, + {file = "python_multipart-0.0.6.tar.gz", hash = "sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132"}, +] + +[package.extras] +dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] + +[[package]] +name = "python-pptx" +version = "0.6.21" +description = "Generate and manipulate Open XML PowerPoint (.pptx) files" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "python-pptx-0.6.21.tar.gz", hash = "sha256:7798a2aaf89563565b3c7120c0acfe9aff775db0db3580544e3bf4840c2e378f"}, +] + +[package.dependencies] +lxml = ">=3.1.0" +Pillow = ">=3.3.2" +XlsxWriter = ">=0.5.7" + +[[package]] +name = "python-semantic-release" +version = "7.33.2" +description = "Automatic Semantic Versioning for Python projects" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "python-semantic-release-7.33.2.tar.gz", hash = "sha256:c23b4bb746e9ddbe1ba7497c48f7d81403e67a14ceb37928ef667c1fbee5e324"}, + {file = "python_semantic_release-7.33.2-py3-none-any.whl", hash = "sha256:9e4990cc0a4dc37482ac5ec7fe6f70f71681228f68f0fa39370415701fdcf632"}, +] + +[package.dependencies] +click = ">=7,<9" +click-log = ">=0.3,<1" +dotty-dict = ">=1.3.0,<2" +gitpython = ">=3.0.8,<4" +invoke = ">=1.4.1,<2" +packaging = "*" +python-gitlab = ">=2,<4" +requests = ">=2.25,<3" +semver = ">=2.10,<3" +tomlkit = ">=0.10,<1.0" +twine = ">=3,<4" +wheel = "*" + +[package.extras] +dev = ["black", "isort", "tox"] +docs = ["Jinja2 (==3.0.3)", "Sphinx (==1.3.6)"] +mypy = ["mypy", "types-requests"] +test = ["coverage (>=5,<6)", "mock (==1.3.0)", "pytest (>=7,<8)", "pytest-mock (>=2,<3)", "pytest-xdist (>=1,<2)", "responses (==0.13.3)"] + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "qdrant-client" +version = "1.1.7" +description = "Client library for the Qdrant vector search engine" +category = "main" +optional = false +python-versions = ">=3.7,<3.12" +files = [ + {file = "qdrant_client-1.1.7-py3-none-any.whl", hash = "sha256:4f5d883660b8193840d8982919ab813a0470ace9a7ff46ee730f909841be5319"}, + {file = "qdrant_client-1.1.7.tar.gz", hash = "sha256:686d86934bec2ebb70676fc0650c9a44a9e552e0149124ca5a22ee8533879deb"}, +] + +[package.dependencies] +grpcio = ">=1.41.0" +grpcio-tools = ">=1.41.0" +httpx = {version = ">=0.14.0", extras = ["http2"]} +numpy = {version = ">=1.21", markers = "python_version >= \"3.8\""} +portalocker = ">=2.7.0,<3.0.0" +pydantic = ">=1.8,<2.0" +typing-extensions = ">=4.0.0,<5.0.0" +urllib3 = ">=1.26.14,<2.0.0" + +[[package]] +name = "readme-renderer" +version = "37.3" +description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, + {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, +] + +[package.dependencies] +bleach = ">=2.1.0" +docutils = ">=0.13.1" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.8.0)"] + +[[package]] +name = "realtime" +version = "1.0.0" +description = "" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "realtime-1.0.0-py3-none-any.whl", hash = "sha256:ceab9e292211ab08b5792ac52b3fa25398440031d5b369bd5799b8125056e2d8"}, + {file = "realtime-1.0.0.tar.gz", hash = "sha256:14e540c4a0cc2736ae83e0cbd7efbbfb8b736df1681df2b9141556cb4848502d"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1,<3.0.0" +typing-extensions = ">=4.2.0,<5.0.0" +websockets = ">=10.3,<11.0" + +[[package]] +name = "redis" +version = "4.5.4" +description = "Python client for Redis database and key-value store" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-4.5.4-py3-none-any.whl", hash = "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2"}, + {file = "redis-4.5.4.tar.gz", hash = "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "regex" +version = "2023.5.5" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "regex-2023.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48c9ec56579d4ba1c88f42302194b8ae2350265cb60c64b7b9a88dcb7fbde309"}, + {file = "regex-2023.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f4541550459c08fdd6f97aa4e24c6f1932eec780d58a2faa2068253df7d6ff"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e22e4460f0245b468ee645156a4f84d0fc35a12d9ba79bd7d79bdcd2f9629d"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b870b6f632fc74941cadc2a0f3064ed8409e6f8ee226cdfd2a85ae50473aa94"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:171c52e320fe29260da550d81c6b99f6f8402450dc7777ef5ced2e848f3b6f8f"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad5524c2aedaf9aa14ef1bc9327f8abd915699dea457d339bebbe2f0d218f86"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a0f874ee8c0bc820e649c900243c6d1e6dc435b81da1492046716f14f1a2a96"}, + {file = "regex-2023.5.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e645c757183ee0e13f0bbe56508598e2d9cd42b8abc6c0599d53b0d0b8dd1479"}, + {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a4c5da39bca4f7979eefcbb36efea04471cd68db2d38fcbb4ee2c6d440699833"}, + {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5e3f4468b8c6fd2fd33c218bbd0a1559e6a6fcf185af8bb0cc43f3b5bfb7d636"}, + {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:59e4b729eae1a0919f9e4c0fc635fbcc9db59c74ad98d684f4877be3d2607dd6"}, + {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ba73a14e9c8f9ac409863543cde3290dba39098fc261f717dc337ea72d3ebad2"}, + {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0bbd5dcb19603ab8d2781fac60114fb89aee8494f4505ae7ad141a3314abb1f9"}, + {file = "regex-2023.5.5-cp310-cp310-win32.whl", hash = "sha256:40005cbd383438aecf715a7b47fe1e3dcbc889a36461ed416bdec07e0ef1db66"}, + {file = "regex-2023.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:59597cd6315d3439ed4b074febe84a439c33928dd34396941b4d377692eca810"}, + {file = "regex-2023.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f08276466fedb9e36e5193a96cb944928301152879ec20c2d723d1031cd4ddd"}, + {file = "regex-2023.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cd46f30e758629c3ee91713529cfbe107ac50d27110fdcc326a42ce2acf4dafc"}, + {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2910502f718828cecc8beff004917dcf577fc5f8f5dd40ffb1ea7612124547b"}, + {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:445d6f4fc3bd9fc2bf0416164454f90acab8858cd5a041403d7a11e3356980e8"}, + {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18196c16a584619c7c1d843497c069955d7629ad4a3fdee240eb347f4a2c9dbe"}, + {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33d430a23b661629661f1fe8395be2004006bc792bb9fc7c53911d661b69dd7e"}, + {file = "regex-2023.5.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72a28979cc667e5f82ef433db009184e7ac277844eea0f7f4d254b789517941d"}, + {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f764e4dfafa288e2eba21231f455d209f4709436baeebb05bdecfb5d8ddc3d35"}, + {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23d86ad2121b3c4fc78c58f95e19173790e22ac05996df69b84e12da5816cb17"}, + {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:690a17db524ee6ac4a27efc5406530dd90e7a7a69d8360235323d0e5dafb8f5b"}, + {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1ecf3dcff71f0c0fe3e555201cbe749fa66aae8d18f80d2cc4de8e66df37390a"}, + {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:811040d7f3dd9c55eb0d8b00b5dcb7fd9ae1761c454f444fd9f37fe5ec57143a"}, + {file = "regex-2023.5.5-cp311-cp311-win32.whl", hash = "sha256:c8c143a65ce3ca42e54d8e6fcaf465b6b672ed1c6c90022794a802fb93105d22"}, + {file = "regex-2023.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:586a011f77f8a2da4b888774174cd266e69e917a67ba072c7fc0e91878178a80"}, + {file = "regex-2023.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b6365703e8cf1644b82104cdd05270d1a9f043119a168d66c55684b1b557d008"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a56c18f21ac98209da9c54ae3ebb3b6f6e772038681d6cb43b8d53da3b09ee81"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8b942d8b3ce765dbc3b1dad0a944712a89b5de290ce8f72681e22b3c55f3cc8"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:844671c9c1150fcdac46d43198364034b961bd520f2c4fdaabfc7c7d7138a2dd"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2ce65bdeaf0a386bb3b533a28de3994e8e13b464ac15e1e67e4603dd88787fa"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fee0016cc35a8a91e8cc9312ab26a6fe638d484131a7afa79e1ce6165328a135"}, + {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:18f05d14f14a812fe9723f13afafefe6b74ca042d99f8884e62dbd34dcccf3e2"}, + {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:941b3f1b2392f0bcd6abf1bc7a322787d6db4e7457be6d1ffd3a693426a755f2"}, + {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:921473a93bcea4d00295799ab929522fc650e85c6b9f27ae1e6bb32a790ea7d3"}, + {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:e2205a81f815b5bb17e46e74cc946c575b484e5f0acfcb805fb252d67e22938d"}, + {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:385992d5ecf1a93cb85adff2f73e0402dd9ac29b71b7006d342cc920816e6f32"}, + {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:890a09cb0a62198bff92eda98b2b507305dd3abf974778bae3287f98b48907d3"}, + {file = "regex-2023.5.5-cp36-cp36m-win32.whl", hash = "sha256:821a88b878b6589c5068f4cc2cfeb2c64e343a196bc9d7ac68ea8c2a776acd46"}, + {file = "regex-2023.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:7918a1b83dd70dc04ab5ed24c78ae833ae8ea228cef84e08597c408286edc926"}, + {file = "regex-2023.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:338994d3d4ca4cf12f09822e025731a5bdd3a37aaa571fa52659e85ca793fb67"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a69cf0c00c4d4a929c6c7717fd918414cab0d6132a49a6d8fc3ded1988ed2ea"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f5e06df94fff8c4c85f98c6487f6636848e1dc85ce17ab7d1931df4a081f657"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8906669b03c63266b6a7693d1f487b02647beb12adea20f8840c1a087e2dfb5"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fda3e50abad8d0f48df621cf75adc73c63f7243cbe0e3b2171392b445401550"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ac2b7d341dc1bd102be849d6dd33b09701223a851105b2754339e390be0627a"}, + {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fb2b495dd94b02de8215625948132cc2ea360ae84fe6634cd19b6567709c8ae2"}, + {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aa7d032c1d84726aa9edeb6accf079b4caa87151ca9fabacef31fa028186c66d"}, + {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d45864693351c15531f7e76f545ec35000d50848daa833cead96edae1665559"}, + {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21e90a288e6ba4bf44c25c6a946cb9b0f00b73044d74308b5e0afd190338297c"}, + {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:10250a093741ec7bf74bcd2039e697f519b028518f605ff2aa7ac1e9c9f97423"}, + {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6b8d0c153f07a953636b9cdb3011b733cadd4178123ef728ccc4d5969e67f3c2"}, + {file = "regex-2023.5.5-cp37-cp37m-win32.whl", hash = "sha256:10374c84ee58c44575b667310d5bbfa89fb2e64e52349720a0182c0017512f6c"}, + {file = "regex-2023.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9b320677521aabf666cdd6e99baee4fb5ac3996349c3b7f8e7c4eee1c00dfe3a"}, + {file = "regex-2023.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:afb1c70ec1e594a547f38ad6bf5e3d60304ce7539e677c1429eebab115bce56e"}, + {file = "regex-2023.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf123225945aa58b3057d0fba67e8061c62d14cc8a4202630f8057df70189051"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99757ad7fe5c8a2bb44829fc57ced11253e10f462233c1255fe03888e06bc19"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a623564d810e7a953ff1357f7799c14bc9beeab699aacc8b7ab7822da1e952b8"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ced02e3bd55e16e89c08bbc8128cff0884d96e7f7a5633d3dc366b6d95fcd1d6"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cbe6b5be3b9b698d8cc4ee4dee7e017ad655e83361cd0ea8e653d65e469468"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a6e4b0e0531223f53bad07ddf733af490ba2b8367f62342b92b39b29f72735a"}, + {file = "regex-2023.5.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e9c4f778514a560a9c9aa8e5538bee759b55f6c1dcd35613ad72523fd9175b8"}, + {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:256f7f4c6ba145f62f7a441a003c94b8b1af78cee2cccacfc1e835f93bc09426"}, + {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bd7b68fd2e79d59d86dcbc1ccd6e2ca09c505343445daaa4e07f43c8a9cc34da"}, + {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4a5059bd585e9e9504ef9c07e4bc15b0a621ba20504388875d66b8b30a5c4d18"}, + {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:6893544e06bae009916a5658ce7207e26ed17385149f35a3125f5259951f1bbe"}, + {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c64d5abe91a3dfe5ff250c6bb267ef00dbc01501518225b45a5f9def458f31fb"}, + {file = "regex-2023.5.5-cp38-cp38-win32.whl", hash = "sha256:7923470d6056a9590247ff729c05e8e0f06bbd4efa6569c916943cb2d9b68b91"}, + {file = "regex-2023.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:4035d6945cb961c90c3e1c1ca2feb526175bcfed44dfb1cc77db4fdced060d3e"}, + {file = "regex-2023.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50fd2d9b36938d4dcecbd684777dd12a407add4f9f934f235c66372e630772b0"}, + {file = "regex-2023.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d19e57f888b00cd04fc38f5e18d0efbd91ccba2d45039453ab2236e6eec48d4d"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd966475e963122ee0a7118ec9024388c602d12ac72860f6eea119a3928be053"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db09e6c18977a33fea26fe67b7a842f706c67cf8bda1450974d0ae0dd63570df"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6164d4e2a82f9ebd7752a06bd6c504791bedc6418c0196cd0a23afb7f3e12b2d"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84397d3f750d153ebd7f958efaa92b45fea170200e2df5e0e1fd4d85b7e3f58a"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c3efee9bb53cbe7b285760c81f28ac80dc15fa48b5fe7e58b52752e642553f1"}, + {file = "regex-2023.5.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:144b5b017646b5a9392a5554a1e5db0000ae637be4971c9747566775fc96e1b2"}, + {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1189fbbb21e2c117fda5303653b61905aeeeea23de4a94d400b0487eb16d2d60"}, + {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f83fe9e10f9d0b6cf580564d4d23845b9d692e4c91bd8be57733958e4c602956"}, + {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:72aa4746993a28c841e05889f3f1b1e5d14df8d3daa157d6001a34c98102b393"}, + {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:de2f780c3242ea114dd01f84848655356af4dd561501896c751d7b885ea6d3a1"}, + {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:290fd35219486dfbc00b0de72f455ecdd63e59b528991a6aec9fdfc0ce85672e"}, + {file = "regex-2023.5.5-cp39-cp39-win32.whl", hash = "sha256:732176f5427e72fa2325b05c58ad0b45af341c459910d766f814b0584ac1f9ac"}, + {file = "regex-2023.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:1307aa4daa1cbb23823d8238e1f61292fd07e4e5d8d38a6efff00b67a7cdb764"}, + {file = "regex-2023.5.5.tar.gz", hash = "sha256:7d76a8a1fc9da08296462a18f16620ba73bcbf5909e42383b253ef34d9d5141e"}, +] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "scikit-learn" +version = "1.2.2" +description = "A set of python modules for machine learning and data mining" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scikit-learn-1.2.2.tar.gz", hash = "sha256:8429aea30ec24e7a8c7ed8a3fa6213adf3814a6efbea09e16e0a0c71e1a1a3d7"}, + {file = "scikit_learn-1.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99cc01184e347de485bf253d19fcb3b1a3fb0ee4cea5ee3c43ec0cc429b6d29f"}, + {file = "scikit_learn-1.2.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e6e574db9914afcb4e11ade84fab084536a895ca60aadea3041e85b8ac963edb"}, + {file = "scikit_learn-1.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fe83b676f407f00afa388dd1fdd49e5c6612e551ed84f3b1b182858f09e987d"}, + {file = "scikit_learn-1.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2642baa0ad1e8f8188917423dd73994bf25429f8893ddbe115be3ca3183584"}, + {file = "scikit_learn-1.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ad66c3848c0a1ec13464b2a95d0a484fd5b02ce74268eaa7e0c697b904f31d6c"}, + {file = "scikit_learn-1.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfeaf8be72117eb61a164ea6fc8afb6dfe08c6f90365bde2dc16456e4bc8e45f"}, + {file = "scikit_learn-1.2.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:fe0aa1a7029ed3e1dcbf4a5bc675aa3b1bc468d9012ecf6c6f081251ca47f590"}, + {file = "scikit_learn-1.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:065e9673e24e0dc5113e2dd2b4ca30c9d8aa2fa90f4c0597241c93b63130d233"}, + {file = "scikit_learn-1.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf036ea7ef66115e0d49655f16febfa547886deba20149555a41d28f56fd6d3c"}, + {file = "scikit_learn-1.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:8b0670d4224a3c2d596fd572fb4fa673b2a0ccfb07152688ebd2ea0b8c61025c"}, + {file = "scikit_learn-1.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9c710ff9f9936ba8a3b74a455ccf0dcf59b230caa1e9ba0223773c490cab1e51"}, + {file = "scikit_learn-1.2.2-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:2dd3ffd3950e3d6c0c0ef9033a9b9b32d910c61bd06cb8206303fb4514b88a49"}, + {file = "scikit_learn-1.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44b47a305190c28dd8dd73fc9445f802b6ea716669cfc22ab1eb97b335d238b1"}, + {file = "scikit_learn-1.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:953236889928d104c2ef14027539f5f2609a47ebf716b8cbe4437e85dce42744"}, + {file = "scikit_learn-1.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:7f69313884e8eb311460cc2f28676d5e400bd929841a2c8eb8742ae78ebf7c20"}, + {file = "scikit_learn-1.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8156db41e1c39c69aa2d8599ab7577af53e9e5e7a57b0504e116cc73c39138dd"}, + {file = "scikit_learn-1.2.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fe175ee1dab589d2e1033657c5b6bec92a8a3b69103e3dd361b58014729975c3"}, + {file = "scikit_learn-1.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d5312d9674bed14f73773d2acf15a3272639b981e60b72c9b190a0cffed5bad"}, + {file = "scikit_learn-1.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea061bf0283bf9a9f36ea3c5d3231ba2176221bbd430abd2603b1c3b2ed85c89"}, + {file = "scikit_learn-1.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:6477eed40dbce190f9f9e9d0d37e020815825b300121307942ec2110302b66a3"}, +] + +[package.dependencies] +joblib = ">=1.1.1" +numpy = ">=1.17.3" +scipy = ">=1.3.2" +threadpoolctl = ">=2.0.0" + +[package.extras] +benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.10.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=4.0.1)", "sphinx-gallery (>=0.7.0)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] +examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.10.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] +tests = ["black (>=22.3.0)", "flake8 (>=3.8.2)", "matplotlib (>=3.1.3)", "mypy (>=0.961)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=5.3.1)", "pytest-cov (>=2.9.0)", "scikit-image (>=0.16.2)"] + +[[package]] +name = "scipy" +version = "1.9.3" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, + {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, + {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, + {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, + {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, + {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, + {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, +] + +[package.dependencies] +numpy = ">=1.18.5,<1.26.0" + +[package.extras] +dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] +test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "semver" +version = "2.13.0" +description = "Python helper for Semantic Versioning (http://semver.org/)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, + {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, +] + +[[package]] +name = "sentence-transformers" +version = "2.2.2" +description = "Multilingual text embeddings" +category = "main" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "sentence-transformers-2.2.2.tar.gz", hash = "sha256:dbc60163b27de21076c9a30d24b5b7b6fa05141d68cf2553fa9a77bf79a29136"}, +] + +[package.dependencies] +huggingface-hub = ">=0.4.0" +nltk = "*" +numpy = "*" +scikit-learn = "*" +scipy = "*" +sentencepiece = "*" +torch = ">=1.6.0" +torchvision = "*" +tqdm = "*" +transformers = ">=4.6.0,<5.0.0" + +[[package]] +name = "sentencepiece" +version = "0.1.99" +description = "SentencePiece python wrapper" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "sentencepiece-0.1.99-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0eb528e70571b7c02723e5804322469b82fe7ea418c96051d0286c0fa028db73"}, + {file = "sentencepiece-0.1.99-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d7fafb2c4e4659cbdf303929503f37a26eabc4ff31d3a79bf1c5a1b338caa7"}, + {file = "sentencepiece-0.1.99-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be9cf5b9e404c245aeb3d3723c737ba7a8f5d4ba262ef233a431fa6c45f732a0"}, + {file = "sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baed1a26464998f9710d20e52607c29ffd4293e7c71c6a1f83f51ad0911ec12c"}, + {file = "sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9832f08bb372d4c8b567612f8eab9e36e268dff645f1c28f9f8e851be705f6d1"}, + {file = "sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:019e7535108e309dae2b253a75834fc3128240aa87c00eb80732078cdc182588"}, + {file = "sentencepiece-0.1.99-cp310-cp310-win32.whl", hash = "sha256:fa16a830416bb823fa2a52cbdd474d1f7f3bba527fd2304fb4b140dad31bb9bc"}, + {file = "sentencepiece-0.1.99-cp310-cp310-win_amd64.whl", hash = "sha256:14b0eccb7b641d4591c3e12ae44cab537d68352e4d3b6424944f0c447d2348d5"}, + {file = "sentencepiece-0.1.99-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6d3c56f24183a1e8bd61043ff2c58dfecdc68a5dd8955dc13bab83afd5f76b81"}, + {file = "sentencepiece-0.1.99-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed6ea1819fd612c989999e44a51bf556d0ef6abfb553080b9be3d347e18bcfb7"}, + {file = "sentencepiece-0.1.99-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2a0260cd1fb7bd8b4d4f39dc2444a8d5fd4e0a0c4d5c899810ef1abf99b2d45"}, + {file = "sentencepiece-0.1.99-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a1abff4d1ff81c77cac3cc6fefa34fa4b8b371e5ee51cb7e8d1ebc996d05983"}, + {file = "sentencepiece-0.1.99-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:004e6a621d4bc88978eecb6ea7959264239a17b70f2cbc348033d8195c9808ec"}, + {file = "sentencepiece-0.1.99-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db361e03342c41680afae5807590bc88aa0e17cfd1a42696a160e4005fcda03b"}, + {file = "sentencepiece-0.1.99-cp311-cp311-win32.whl", hash = "sha256:2d95e19168875b70df62916eb55428a0cbcb834ac51d5a7e664eda74def9e1e0"}, + {file = "sentencepiece-0.1.99-cp311-cp311-win_amd64.whl", hash = "sha256:f90d73a6f81248a909f55d8e6ef56fec32d559e1e9af045f0b0322637cb8e5c7"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:62e24c81e74bd87a6e0d63c51beb6527e4c0add67e1a17bac18bcd2076afcfeb"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57efcc2d51caff20d9573567d9fd3f854d9efe613ed58a439c78c9f93101384a"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a904c46197993bd1e95b93a6e373dca2f170379d64441041e2e628ad4afb16f"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89adf59854741c0d465f0e1525b388c0d174f611cc04af54153c5c4f36088c4"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-win32.whl", hash = "sha256:47c378146928690d1bc106fdf0da768cebd03b65dd8405aa3dd88f9c81e35dba"}, + {file = "sentencepiece-0.1.99-cp36-cp36m-win_amd64.whl", hash = "sha256:9ba142e7a90dd6d823c44f9870abdad45e6c63958eb60fe44cca6828d3b69da2"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b7b1a9ae4d7c6f1f867e63370cca25cc17b6f4886729595b885ee07a58d3cec3"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0f644c9d4d35c096a538507b2163e6191512460035bf51358794a78515b74f7"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8843d23a0f686d85e569bd6dcd0dd0e0cbc03731e63497ca6d5bacd18df8b85"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e6f690a1caebb4867a2e367afa1918ad35be257ecdb3455d2bbd787936f155"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-win32.whl", hash = "sha256:8a321866c2f85da7beac74a824b4ad6ddc2a4c9bccd9382529506d48f744a12c"}, + {file = "sentencepiece-0.1.99-cp37-cp37m-win_amd64.whl", hash = "sha256:c42f753bcfb7661c122a15b20be7f684b61fc8592c89c870adf52382ea72262d"}, + {file = "sentencepiece-0.1.99-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:85b476406da69c70586f0bb682fcca4c9b40e5059814f2db92303ea4585c650c"}, + {file = "sentencepiece-0.1.99-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfbcfe13c69d3f87b7fcd5da168df7290a6d006329be71f90ba4f56bc77f8561"}, + {file = "sentencepiece-0.1.99-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:445b0ec381af1cd4eef95243e7180c63d9c384443c16c4c47a28196bd1cda937"}, + {file = "sentencepiece-0.1.99-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6890ea0f2b4703f62d0bf27932e35808b1f679bdb05c7eeb3812b935ba02001"}, + {file = "sentencepiece-0.1.99-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb71af492b0eefbf9f2501bec97bcd043b6812ab000d119eaf4bd33f9e283d03"}, + {file = "sentencepiece-0.1.99-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b866b5bd3ddd54166bbcbf5c8d7dd2e0b397fac8537991c7f544220b1f67bc"}, + {file = "sentencepiece-0.1.99-cp38-cp38-win32.whl", hash = "sha256:b133e8a499eac49c581c3c76e9bdd08c338cc1939e441fee6f92c0ccb5f1f8be"}, + {file = "sentencepiece-0.1.99-cp38-cp38-win_amd64.whl", hash = "sha256:0eaf3591dd0690a87f44f4df129cf8d05d8a4029b5b6709b489b8e27f9a9bcff"}, + {file = "sentencepiece-0.1.99-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38efeda9bbfb55052d482a009c6a37e52f42ebffcea9d3a98a61de7aee356a28"}, + {file = "sentencepiece-0.1.99-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6c030b081dc1e1bcc9fadc314b19b740715d3d566ad73a482da20d7d46fd444c"}, + {file = "sentencepiece-0.1.99-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84dbe53e02e4f8a2e45d2ac3e430d5c83182142658e25edd76539b7648928727"}, + {file = "sentencepiece-0.1.99-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b0f55d0a0ee1719b4b04221fe0c9f0c3461dc3dabd77a035fa2f4788eb3ef9a"}, + {file = "sentencepiece-0.1.99-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e800f206cd235dc27dc749299e05853a4e4332e8d3dfd81bf13d0e5b9007d9"}, + {file = "sentencepiece-0.1.99-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae1c40cda8f9d5b0423cfa98542735c0235e7597d79caf318855cdf971b2280"}, + {file = "sentencepiece-0.1.99-cp39-cp39-win32.whl", hash = "sha256:c84ce33af12ca222d14a1cdd37bd76a69401e32bc68fe61c67ef6b59402f4ab8"}, + {file = "sentencepiece-0.1.99-cp39-cp39-win_amd64.whl", hash = "sha256:350e5c74d739973f1c9643edb80f7cc904dc948578bcb1d43c6f2b173e5d18dd"}, + {file = "sentencepiece-0.1.99.tar.gz", hash = "sha256:189c48f5cb2949288f97ccdb97f0473098d9c3dcf5a3d99d4eabe719ec27297f"}, +] + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "smmap" +version = "5.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.13" +description = "Database Abstraction Library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ad24c85f2a1caf0cd1ae8c2fdb668777a51a02246d9039420f94bd7dbfd37ed"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db24d2738add6db19d66ca820479d2f8f96d3f5a13c223f27fa28dd2f268a4bd"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72746ec17a7d9c5acf2c57a6e6190ceba3dad7127cd85bb17f24e90acc0e8e3f"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:755f653d693f9b8f4286d987aec0d4279821bf8d179a9de8e8a5c685e77e57d6"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0d20f27edfd6f35b388da2bdcd7769e4ffa374fef8994980ced26eb287e033a"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:37de4010f53f452e94e5ed6684480432cfe6a7a8914307ef819cd028b05b98d5"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-win32.whl", hash = "sha256:31f72bb300eed7bfdb373c7c046121d84fa0ae6f383089db9505ff553ac27cef"}, + {file = "SQLAlchemy-2.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:ec2f525273528425ed2f51861b7b88955160cb95dddb17af0914077040aff4a5"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2424a84f131901fbb20a99844d47b38b517174c6e964c8efb15ea6bb9ced8c2b"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f9832815257969b3ca9bf0501351e4c02c8d60cbd3ec9f9070d5b0f8852900e"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a30e4db983faa5145e00ef6eaf894a2d503b3221dbf40a595f3011930d3d0bac"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f717944aee40e9f48776cf85b523bb376aa2d9255a268d6d643c57ab387e7264"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9119795d2405eb23bf7e6707e228fe38124df029494c1b3576459aa3202ea432"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2ad9688debf1f0ae9c6e0706a4e2d33b1a01281317cee9bd1d7eef8020c5baac"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-win32.whl", hash = "sha256:c61b89803a87a3b2a394089a7dadb79a6c64c89f2e8930cc187fec43b319f8d2"}, + {file = "SQLAlchemy-2.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:0aa2cbde85a6eab9263ab480f19e8882d022d30ebcdc14d69e6a8d7c07b0a871"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9ad883ac4f5225999747f0849643c4d0ec809d9ffe0ddc81a81dd3e68d0af463"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e481e54db8cec1457ee7c05f6d2329e3298a304a70d3b5e2e82e77170850b385"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e08e3831671008888bad5d160d757ef35ce34dbb73b78c3998d16aa1334c97"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f234ba3bb339ad17803009c8251f5ee65dcf283a380817fe486823b08b26383d"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:375b7ba88f261dbd79d044f20cbcd919d88befb63f26af9d084614f10cdf97a6"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-win32.whl", hash = "sha256:9136d596111c742d061c0f99bab95c5370016c4101a32e72c2b634ad5e0757e6"}, + {file = "SQLAlchemy-2.0.13-cp37-cp37m-win_amd64.whl", hash = "sha256:7612a7366a0855a04430363fb4ab392dc6818aaece0b2e325ff30ee77af9b21f"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:49c138856035cb97f0053e5e57ba90ec936b28a0b8b0020d44965c7b0c0bf03a"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a5e9e78332a5d841422b88b8c490dfd7f761e64b3430249b66c05d02f72ceab0"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd0febae872a4042da44e972c070f0fd49a85a0a7727ab6b85425f74348be14e"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:566a0ac347cf4632f551e7b28bbd0d215af82e6ffaa2556f565a3b6b51dc3f81"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5e5dc300a0ca8755ada1569f5caccfcdca28607dfb98b86a54996b288a8ebd3"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a25b4c4fdd633501233924f873e6f6cd8970732859ecfe4ecfb60635881f70be"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-win32.whl", hash = "sha256:6777673d346071451bf7cccf8d0499024f1bd6a835fc90b4fe7af50373d92ce6"}, + {file = "SQLAlchemy-2.0.13-cp38-cp38-win_amd64.whl", hash = "sha256:2f0a355264af0952570f18457102984e1f79510f856e5e0ae652e63316d1ca23"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d93ebbff3dcf05274843ad8cf650b48ee634626e752c5d73614e5ec9df45f0ce"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fec56c7d1b6a22c8f01557de3975d962ee40270b81b60d1cfdadf2a105d10e84"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eb14a386a5b610305bec6639b35540b47f408b0a59f75999199aed5b3d40079"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b5236079bc3e318a92bab2cc3f669cc32127075ab03ff61cacbae1c392b8"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bf1aae95e80acea02a0a622e1c12d3fefc52ffd0fe7bda70a30d070373fbb6c3"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cdf80359b641185ae7e580afb9f88cf560298f309a38182972091165bfe1225d"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-win32.whl", hash = "sha256:f463598f9e51ccc04f0fe08500f9a0c3251a7086765350be418598b753b5561d"}, + {file = "SQLAlchemy-2.0.13-cp39-cp39-win_amd64.whl", hash = "sha256:881cc388dded44ae6e17a1666364b98bd76bcdc71b869014ae725f06ba298e0e"}, + {file = "SQLAlchemy-2.0.13-py3-none-any.whl", hash = "sha256:0d6979c9707f8b82366ba34b38b5a6fe32f75766b2e901f9820e271e95384070"}, + {file = "SQLAlchemy-2.0.13.tar.gz", hash = "sha256:8d97b37b4e60073c38bcf94e289e3be09ef9be870de88d163f16e08f2b9ded1a"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.2.0" + +[package.extras] +aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx-oracle (>=7)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3-binary"] + +[[package]] +name = "starlette" +version = "0.25.0" +description = "The little ASGI library that shines." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.25.0-py3-none-any.whl", hash = "sha256:774f1df1983fd594b9b6fb3ded39c2aa1979d10ac45caac0f4255cbe2acb8628"}, + {file = "starlette-0.25.0.tar.gz", hash = "sha256:854c71e73736c429c2bdb07801f2c76c9cba497e7c3cf4988fde5e95fe4cdb3c"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "storage3" +version = "0.5.2" +description = "Supabase Storage client for Python." +category = "main" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "storage3-0.5.2-py3-none-any.whl", hash = "sha256:3aaba8cebf89eef6b5fc48739b8c8c8539461f2eed9ea1dc4c763dea10c6d009"}, + {file = "storage3-0.5.2.tar.gz", hash = "sha256:e9932fca869a8f9cdab9a20e5249439928cfe2d07c4524141b15fef1882a7f61"}, +] + +[package.dependencies] +httpx = ">=0.23,<0.24" +python-dateutil = ">=2.8.2,<3.0.0" +typing-extensions = ">=4.2.0,<5.0.0" + +[[package]] +name = "strenum" +version = "0.4.10" +description = "An Enum that inherits from str." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "StrEnum-0.4.10-py3-none-any.whl", hash = "sha256:aebf04bba8e5af435937c452d69a86798b6f8d5ca5f20ba18561dbfad571ccdd"}, + {file = "StrEnum-0.4.10.tar.gz", hash = "sha256:898cc0ebb5054ee07400341ac1d75fdfee489d76d6df3fbc1c2eaf95971e3916"}, +] + +[package.extras] +docs = ["myst-parser[linkify]", "sphinx", "sphinx-rtd-theme"] +release = ["twine"] +test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] + +[[package]] +name = "supabase" +version = "1.0.3" +description = "Supabase client for Python." +category = "main" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "supabase-1.0.3-py3-none-any.whl", hash = "sha256:2418113b7f503522d33fafd442e587356636bad6cb803f7e406e614acf2611d7"}, + {file = "supabase-1.0.3.tar.gz", hash = "sha256:c6eac0144b4236a61ccc72024a8e88d8f08979e47ea635307afae7fb4fc24bc6"}, +] + +[package.dependencies] +gotrue = ">=1.0.1,<2.0.0" +httpx = ">=0.23.0,<0.24.0" +postgrest = ">=0.10.6,<0.11.0" +python-semantic-release = "7.33.2" +realtime = ">=1.0.0,<2.0.0" +storage3 = ">=0.5.2,<0.6.0" +supafunc = ">=0.2.2,<0.3.0" + +[[package]] +name = "supafunc" +version = "0.2.2" +description = "Library for Supabase Functions" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "supafunc-0.2.2-py3-none-any.whl", hash = "sha256:a292812532cca05afc08d2cc040eea5bd79a8909e46051630620b67508070795"}, + {file = "supafunc-0.2.2.tar.gz", hash = "sha256:84f1f8d47297b0c8b712f1d8e20843406c025a203bba00cb7216e2163f295c24"}, +] + +[package.dependencies] +httpx = ">=0.23.0,<0.24.0" + +[[package]] +name = "sympy" +version = "1.12" +description = "Computer algebra system (CAS) in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, + {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, +] + +[package.dependencies] +mpmath = ">=0.19" + +[[package]] +name = "tenacity" +version = "8.2.2" +description = "Retry code until it succeeds" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "tenacity-8.2.2-py3-none-any.whl", hash = "sha256:2f277afb21b851637e8f52e6a613ff08734c347dc19ade928e519d7d2d8569b0"}, + {file = "tenacity-8.2.2.tar.gz", hash = "sha256:43af037822bd0029025877f3b2d97cc4d7bb0c2991000a3d59d71517c5c969e0"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "threadpoolctl" +version = "3.1.0" +description = "threadpoolctl" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, + {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, +] + +[[package]] +name = "tiktoken" +version = "0.2.0" +description = "" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tiktoken-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d06705b55bb5f6c194285b6d15ad31bd7586d44fe433be31bc3694cf8c70169c"}, + {file = "tiktoken-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29f2969945fc430f817c907f59a2da9e7b797fe65527ba5b9442618643a0dc86"}, + {file = "tiktoken-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546455f27b6f7981d17de265b8b99e2fef980fbc3fde1d94b551f8354902000e"}, + {file = "tiktoken-0.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:54b5dc05f934ac68e8da4d2cc3acd77bc6968114b09669056f1bff12acc57049"}, + {file = "tiktoken-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d3c48cb5649ce6bb2b207377dfdaa855e1e771b2e7f59fb251182c227573619"}, + {file = "tiktoken-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a55f983735745df9a87161d9e0ce9ef7d216039d389246be98c6d416bbb2452f"}, + {file = "tiktoken-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175de868393039a85fdf4c7cfb9b8883d1b248b9a3d9d0129d30414f5a59c333"}, + {file = "tiktoken-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cd97b8cd14e3fe6647baa71c67f7f6b21a401fa996ccc3d93bf0ae02162af2"}, + {file = "tiktoken-0.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:806e2b8c0b9786c0e3212e8b3a6ac8f5840066c00a31b89e6c8d9ba0421e77d7"}, + {file = "tiktoken-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:57b753aa9813f06fa5a26da2622114bf9769a8d1dca1b276d3613ee15da5b09d"}, + {file = "tiktoken-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aa3c15b87bb2cea56ecc8fe4c7bf105c5c2dc4090c2df97c141100488297173a"}, + {file = "tiktoken-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bd98fc4a9ec967a089c62497f21277b53aa3e15a6fec731ac707eea4d5527938"}, + {file = "tiktoken-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab10ad3280f348a0d3bfea6d503c6aa84676b159692701bc7604e67129bd2135"}, + {file = "tiktoken-0.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:59296d495aa6aec375a75f07da44fabb9720632c9404b41b9cbfe95e17966345"}, + {file = "tiktoken-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:3b078e6109d522c5ffc52859520eef6c17a3b120ed52b79f48cae0badff08fe0"}, + {file = "tiktoken-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aef47e8037652b18d2665b77e1f9416d3a86ccd383b039d0dfcb7d92085cef6d"}, + {file = "tiktoken-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0f62f8349a5412962326dbc41c3823a1f381d8ab62afbee94480d8296499d8e"}, + {file = "tiktoken-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d0dbf7e1940427c11f0c8ab9046ad98d774850b21559b37ca60ff30d3a14620"}, + {file = "tiktoken-0.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f1a7c6bec42a2fb5309a161d1b891fe5e181d4b620a962923a925f45fe25697"}, + {file = "tiktoken-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3349fd809d17b722814a6a700e4bc0125527f39057b57a02ed42f53bb4e6e2f5"}, + {file = "tiktoken-0.2.0.tar.gz", hash = "sha256:df41a3d478499757b5b32eae5e97657cf159d8d9e6764049dd7c3abb49e1b40f"}, +] + +[package.dependencies] +blobfile = ">=2" +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[[package]] +name = "tokenizers" +version = "0.13.3" +description = "Fast and Customizable Tokenizers" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "tokenizers-0.13.3-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:f3835c5be51de8c0a092058a4d4380cb9244fb34681fd0a295fbf0a52a5fdf33"}, + {file = "tokenizers-0.13.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4ef4c3e821730f2692489e926b184321e887f34fb8a6b80b8096b966ba663d07"}, + {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5fd1a6a25353e9aa762e2aae5a1e63883cad9f4e997c447ec39d071020459bc"}, + {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee0b1b311d65beab83d7a41c56a1e46ab732a9eed4460648e8eb0bd69fc2d059"}, + {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ef4215284df1277dadbcc5e17d4882bda19f770d02348e73523f7e7d8b8d396"}, + {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4d53976079cff8a033f778fb9adca2d9d69d009c02fa2d71a878b5f3963ed30"}, + {file = "tokenizers-0.13.3-cp310-cp310-win32.whl", hash = "sha256:1f0e3b4c2ea2cd13238ce43548959c118069db7579e5d40ec270ad77da5833ce"}, + {file = "tokenizers-0.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:89649c00d0d7211e8186f7a75dfa1db6996f65edce4b84821817eadcc2d3c79e"}, + {file = "tokenizers-0.13.3-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:56b726e0d2bbc9243872b0144515ba684af5b8d8cd112fb83ee1365e26ec74c8"}, + {file = "tokenizers-0.13.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc5c022ce692e1f499d745af293ab9ee6f5d92538ed2faf73f9708c89ee59ce6"}, + {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55c981ac44ba87c93e847c333e58c12abcbb377a0c2f2ef96e1a266e4184ff2"}, + {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f247eae99800ef821a91f47c5280e9e9afaeed9980fc444208d5aa6ba69ff148"}, + {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b3e3215d048e94f40f1c95802e45dcc37c5b05eb46280fc2ccc8cd351bff839"}, + {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ba2b0bf01777c9b9bc94b53764d6684554ce98551fec496f71bc5be3a03e98b"}, + {file = "tokenizers-0.13.3-cp311-cp311-win32.whl", hash = "sha256:cc78d77f597d1c458bf0ea7c2a64b6aa06941c7a99cb135b5969b0278824d808"}, + {file = "tokenizers-0.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:ecf182bf59bd541a8876deccf0360f5ae60496fd50b58510048020751cf1724c"}, + {file = "tokenizers-0.13.3-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:0527dc5436a1f6bf2c0327da3145687d3bcfbeab91fed8458920093de3901b44"}, + {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cbb2c307627dc99b44b22ef05ff4473aa7c7cc1fec8f0a8b37d8a64b1a16d2"}, + {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4560dbdeaae5b7ee0d4e493027e3de6d53c991b5002d7ff95083c99e11dd5ac0"}, + {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64064bd0322405c9374305ab9b4c07152a1474370327499911937fd4a76d004b"}, + {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8c6e2ab0f2e3d939ca66aa1d596602105fe33b505cd2854a4c1717f704c51de"}, + {file = "tokenizers-0.13.3-cp37-cp37m-win32.whl", hash = "sha256:6cc29d410768f960db8677221e497226e545eaaea01aa3613fa0fdf2cc96cff4"}, + {file = "tokenizers-0.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fc2a7fdf864554a0dacf09d32e17c0caa9afe72baf9dd7ddedc61973bae352d8"}, + {file = "tokenizers-0.13.3-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:8791dedba834c1fc55e5f1521be325ea3dafb381964be20684b92fdac95d79b7"}, + {file = "tokenizers-0.13.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:d607a6a13718aeb20507bdf2b96162ead5145bbbfa26788d6b833f98b31b26e1"}, + {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3791338f809cd1bf8e4fee6b540b36822434d0c6c6bc47162448deee3f77d425"}, + {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2f35f30e39e6aab8716f07790f646bdc6e4a853816cc49a95ef2a9016bf9ce6"}, + {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310204dfed5aa797128b65d63538a9837cbdd15da2a29a77d67eefa489edda26"}, + {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0f9b92ea052305166559f38498b3b0cae159caea712646648aaa272f7160963"}, + {file = "tokenizers-0.13.3-cp38-cp38-win32.whl", hash = "sha256:9a3fa134896c3c1f0da6e762d15141fbff30d094067c8f1157b9fdca593b5806"}, + {file = "tokenizers-0.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:8e7b0cdeace87fa9e760e6a605e0ae8fc14b7d72e9fc19c578116f7287bb873d"}, + {file = "tokenizers-0.13.3-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:00cee1e0859d55507e693a48fa4aef07060c4bb6bd93d80120e18fea9371c66d"}, + {file = "tokenizers-0.13.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a23ff602d0797cea1d0506ce69b27523b07e70f6dda982ab8cf82402de839088"}, + {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ce07445050b537d2696022dafb115307abdffd2a5c106f029490f84501ef97"}, + {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:280ffe95f50eaaf655b3a1dc7ff1d9cf4777029dbbc3e63a74e65a056594abc3"}, + {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97acfcec592f7e9de8cadcdcda50a7134423ac8455c0166b28c9ff04d227b371"}, + {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7730c98a3010cd4f523465867ff95cd9d6430db46676ce79358f65ae39797b"}, + {file = "tokenizers-0.13.3-cp39-cp39-win32.whl", hash = "sha256:48625a108029cb1ddf42e17a81b5a3230ba6888a70c9dc14e81bc319e812652d"}, + {file = "tokenizers-0.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:bc0a6f1ba036e482db6453571c9e3e60ecd5489980ffd95d11dc9f960483d783"}, + {file = "tokenizers-0.13.3.tar.gz", hash = "sha256:2e546dbb68b623008a5442353137fbb0123d311a6d7ba52f2667c8862a75af2e"}, +] + +[package.extras] +dev = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.11.8" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, + {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, +] + +[[package]] +name = "torch" +version = "2.0.1" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +category = "main" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "torch-2.0.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:8ced00b3ba471856b993822508f77c98f48a458623596a4c43136158781e306a"}, + {file = "torch-2.0.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:359bfaad94d1cda02ab775dc1cc386d585712329bb47b8741607ef6ef4950747"}, + {file = "torch-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:7c84e44d9002182edd859f3400deaa7410f5ec948a519cc7ef512c2f9b34d2c4"}, + {file = "torch-2.0.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:567f84d657edc5582d716900543e6e62353dbe275e61cdc36eda4929e46df9e7"}, + {file = "torch-2.0.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:787b5a78aa7917465e9b96399b883920c88a08f4eb63b5a5d2d1a16e27d2f89b"}, + {file = "torch-2.0.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:e617b1d0abaf6ced02dbb9486803abfef0d581609b09641b34fa315c9c40766d"}, + {file = "torch-2.0.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b6019b1de4978e96daa21d6a3ebb41e88a0b474898fe251fd96189587408873e"}, + {file = "torch-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:dbd68cbd1cd9da32fe5d294dd3411509b3d841baecb780b38b3b7b06c7754434"}, + {file = "torch-2.0.1-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:ef654427d91600129864644e35deea761fb1fe131710180b952a6f2e2207075e"}, + {file = "torch-2.0.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:25aa43ca80dcdf32f13da04c503ec7afdf8e77e3a0183dd85cd3e53b2842e527"}, + {file = "torch-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5ef3ea3d25441d3957348f7e99c7824d33798258a2bf5f0f0277cbcadad2e20d"}, + {file = "torch-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0882243755ff28895e8e6dc6bc26ebcf5aa0911ed81b2a12f241fc4b09075b13"}, + {file = "torch-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:f66aa6b9580a22b04d0af54fcd042f52406a8479e2b6a550e3d9f95963e168c8"}, + {file = "torch-2.0.1-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:1adb60d369f2650cac8e9a95b1d5758e25d526a34808f7448d0bd599e4ae9072"}, + {file = "torch-2.0.1-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:1bcffc16b89e296826b33b98db5166f990e3b72654a2b90673e817b16c50e32b"}, + {file = "torch-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:e10e1597f2175365285db1b24019eb6f04d53dcd626c735fc502f1e8b6be9875"}, + {file = "torch-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:423e0ae257b756bb45a4b49072046772d1ad0c592265c5080070e0767da4e490"}, + {file = "torch-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8742bdc62946c93f75ff92da00e3803216c6cce9b132fbca69664ca38cfb3e18"}, + {file = "torch-2.0.1-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:c62df99352bd6ee5a5a8d1832452110435d178b5164de450831a3a8cc14dc680"}, + {file = "torch-2.0.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:671a2565e3f63b8fe8e42ae3e36ad249fe5e567435ea27b94edaa672a7d0c416"}, +] + +[package.dependencies] +filelock = "*" +jinja2 = "*" +networkx = "*" +sympy = "*" +typing-extensions = "*" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] + +[[package]] +name = "torchvision" +version = "0.15.2" +description = "image and video datasets and models for torch deep learning" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "torchvision-0.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7754088774e810c5672b142a45dcf20b1bd986a5a7da90f8660c43dc43fb850c"}, + {file = "torchvision-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37eb138e13f6212537a3009ac218695483a635c404b6cc1d8e0d0d978026a86d"}, + {file = "torchvision-0.15.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:54143f7cc0797d199b98a53b7d21c3f97615762d4dd17ad45a41c7e80d880e73"}, + {file = "torchvision-0.15.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:1eefebf5fbd01a95fe8f003d623d941601c94b5cec547b420da89cb369d9cf96"}, + {file = "torchvision-0.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:96fae30c5ca8423f4b9790df0f0d929748e32718d88709b7b567d2f630c042e3"}, + {file = "torchvision-0.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5f35f6bd5bcc4568e6522e4137fa60fcc72f4fa3e615321c26cd87e855acd398"}, + {file = "torchvision-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:757505a0ab2be7096cb9d2bf4723202c971cceddb72c7952a7e877f773de0f8a"}, + {file = "torchvision-0.15.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:012ad25cfd9019ff9b0714a168727e3845029be1af82296ff1e1482931fa4b80"}, + {file = "torchvision-0.15.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b02a7ffeaa61448737f39a4210b8ee60234bda0515a0c0d8562f884454105b0f"}, + {file = "torchvision-0.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:10be76ceded48329d0a0355ac33da131ee3993ff6c125e4a02ab34b5baa2472c"}, + {file = "torchvision-0.15.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f12415b686dba884fb086f53ac803f692be5a5cdd8a758f50812b30fffea2e4"}, + {file = "torchvision-0.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:31211c01f8b8ec33b8a638327b5463212e79a03e43c895f88049f97af1bd12fd"}, + {file = "torchvision-0.15.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c55f9889e436f14b4f84a9c00ebad0d31f5b4626f10cf8018e6c676f92a6d199"}, + {file = "torchvision-0.15.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9a192f2aa979438f23c20e883980b23d13268ab9f819498774a6d2eb021802c2"}, + {file = "torchvision-0.15.2-cp38-cp38-win_amd64.whl", hash = "sha256:c07071bc8d02aa8fcdfe139ab6a1ef57d3b64c9e30e84d12d45c9f4d89fb6536"}, + {file = "torchvision-0.15.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4790260fcf478a41c7ecc60a6d5200a88159fdd8d756e9f29f0f8c59c4a67a68"}, + {file = "torchvision-0.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:987ab62225b4151a11e53fd06150c5258ced24ac9d7c547e0e4ab6fbca92a5ce"}, + {file = "torchvision-0.15.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:63df26673e66cba3f17e07c327a8cafa3cce98265dbc3da329f1951d45966838"}, + {file = "torchvision-0.15.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b85f98d4cc2f72452f6792ab4463a3541bc5678a8cdd3da0e139ba2fe8b56d42"}, + {file = "torchvision-0.15.2-cp39-cp39-win_amd64.whl", hash = "sha256:07c462524cc1bba5190c16a9d47eac1fca024d60595a310f23c00b4ffff18b30"}, +] + +[package.dependencies] +numpy = "*" +pillow = ">=5.3.0,<8.3.0 || >=8.4.0" +requests = "*" +torch = "2.0.1" + +[package.extras] +scipy = ["scipy"] + +[[package]] +name = "tqdm" +version = "4.65.0" +description = "Fast, Extensible Progress Meter" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, + {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "transformers" +version = "4.29.1" +description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "transformers-4.29.1-py3-none-any.whl", hash = "sha256:75f851f2420c26410edbdf4a2a1a5b434ab2b96aea36eb5931d06cc3b2e7b509"}, + {file = "transformers-4.29.1.tar.gz", hash = "sha256:3dc9cd198918e140468edbf37d7edf3b7a75633655ce0771ce323bbf8c118c4d"}, +] + +[package.dependencies] +filelock = "*" +huggingface-hub = ">=0.14.1,<1.0" +numpy = ">=1.17" +packaging = ">=20.0" +pyyaml = ">=5.1" +regex = "!=2019.12.17" +requests = "*" +tokenizers = ">=0.11.1,<0.11.3 || >0.11.3,<0.14" +tqdm = ">=4.27" + +[package.extras] +accelerate = ["accelerate (>=0.19.0)"] +agents = ["Pillow", "accelerate (>=0.19.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch (>=1.9,!=1.12.0)"] +all = ["Pillow", "accelerate (>=0.19.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.6.9)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "numba (<0.57.0)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf (<=3.20.2)", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] +audio = ["kenlm", "librosa", "numba (<0.57.0)", "phonemizer", "pyctcdecode (>=0.4.0)"] +codecarbon = ["codecarbon (==1.2.0)"] +deepspeed = ["accelerate (>=0.19.0)", "deepspeed (>=0.8.3)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.19.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.8.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "optuna", "parameterized", "protobuf (<=3.20.2)", "psutil", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "sentencepiece (>=0.1.91,!=0.1.92)", "timeout-decorator"] +dev = ["GitPython (<3.1.19)", "Pillow", "accelerate (>=0.19.0)", "av (==9.2.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.6.9)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "numba (<0.57.0)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorflow (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "numba (<0.57.0)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "urllib3 (<2.0.0)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow", "accelerate (>=0.19.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "numba (<0.57.0)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +docs = ["Pillow", "accelerate (>=0.19.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.6.9)", "hf-doc-builder", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "numba (<0.57.0)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf (<=3.20.2)", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] +docs-specific = ["hf-doc-builder"] +fairscale = ["fairscale (>0.3)"] +flax = ["flax (>=0.4.1,<=0.6.9)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "optax (>=0.0.8,<=0.1.4)"] +flax-speech = ["kenlm", "librosa", "numba (<0.57.0)", "phonemizer", "pyctcdecode (>=0.4.0)"] +ftfy = ["ftfy"] +integrations = ["optuna", "ray[tune]", "sigopt"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +modelcreation = ["cookiecutter (==1.7.3)"] +natten = ["natten (>=0.14.6)"] +onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] +onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] +optuna = ["optuna"] +quality = ["GitPython (<3.1.19)", "black (>=23.1,<24.0)", "datasets (!=2.5.0)", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "ruff (>=0.0.241,<=0.0.259)", "urllib3 (<2.0.0)"] +ray = ["ray[tune]"] +retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] +sagemaker = ["sagemaker (>=2.31.0)"] +sentencepiece = ["protobuf (<=3.20.2)", "sentencepiece (>=0.1.91,!=0.1.92)"] +serving = ["fastapi", "pydantic", "starlette", "uvicorn"] +sigopt = ["sigopt"] +sklearn = ["scikit-learn"] +speech = ["kenlm", "librosa", "numba (<0.57.0)", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +testing = ["GitPython (<3.1.19)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "parameterized", "protobuf (<=3.20.2)", "psutil", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "timeout-decorator"] +tf = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx"] +tf-cpu = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow-cpu (>=2.4,<2.13)", "tensorflow-text (<2.13)", "tf2onnx"] +tf-speech = ["kenlm", "librosa", "numba (<0.57.0)", "phonemizer", "pyctcdecode (>=0.4.0)"] +timm = ["timm"] +tokenizers = ["tokenizers (>=0.11.1,!=0.11.3,<0.14)"] +torch = ["accelerate (>=0.19.0)", "torch (>=1.9,!=1.12.0)"] +torch-speech = ["kenlm", "librosa", "numba (<0.57.0)", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +torch-vision = ["Pillow", "torchvision"] +torchhub = ["filelock", "huggingface-hub (>=0.14.1,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf (<=3.20.2)", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "tqdm (>=4.27)"] +video = ["av (==9.2.0)", "decord (==0.6.0)"] +vision = ["Pillow"] + +[[package]] +name = "twine" +version = "3.8.0" +description = "Collection of utilities for publishing packages on PyPI" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"}, + {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"}, +] + +[package.dependencies] +colorama = ">=0.4.3" +importlib-metadata = ">=3.6" +keyring = ">=15.1" +pkginfo = ">=1.8.1" +readme-renderer = ">=21.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +tqdm = ">=4.14" +urllib3 = ">=1.26.0" + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "typing-inspect" +version = "0.8.0" +description = "Runtime inspection utilities for typing module." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "typing_inspect-0.8.0-py3-none-any.whl", hash = "sha256:5fbf9c1e65d4fa01e701fe12a5bca6c6e08a4ffd5bc60bfac028253a447c5188"}, + {file = "typing_inspect-0.8.0.tar.gz", hash = "sha256:8b1ff0c400943b6145df8119c41c244ca8207f1f10c9c057aeed1560e4806e3d"}, +] + +[package.dependencies] +mypy-extensions = ">=0.3.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "ujson" +version = "5.7.0" +description = "Ultra fast JSON encoder and decoder for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b"}, + {file = "ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c"}, + {file = "ujson-5.7.0-cp310-cp310-win32.whl", hash = "sha256:7df3fd35ebc14dafeea031038a99232b32f53fa4c3ecddb8bed132a43eefb8ad"}, + {file = "ujson-5.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:af4639f684f425177d09ae409c07602c4096a6287027469157bfb6f83e01448b"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f"}, + {file = "ujson-5.7.0-cp311-cp311-win32.whl", hash = "sha256:26c2b32b489c393106e9cb68d0a02e1a7b9d05a07429d875c46b94ee8405bdb7"}, + {file = "ujson-5.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed24406454bb5a31df18f0a423ae14beb27b28cdfa34f6268e7ebddf23da807e"}, + {file = "ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7"}, + {file = "ujson-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:6faf46fa100b2b89e4db47206cf8a1ffb41542cdd34dde615b2fc2288954f194"}, + {file = "ujson-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff0004c3f5a9a6574689a553d1b7819d1a496b4f005a7451f339dc2d9f4cf98c"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c"}, + {file = "ujson-5.7.0-cp38-cp38-win32.whl", hash = "sha256:bea8d30e362180aafecabbdcbe0e1f0b32c9fa9e39c38e4af037b9d3ca36f50c"}, + {file = "ujson-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:c96e3b872bf883090ddf32cc41957edf819c5336ab0007d0cf3854e61841726d"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2"}, + {file = "ujson-5.7.0-cp39-cp39-win32.whl", hash = "sha256:cd90027e6d93e8982f7d0d23acf88c896d18deff1903dd96140613389b25c0dd"}, + {file = "ujson-5.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:523ee146cdb2122bbd827f4dcc2a8e66607b3f665186bce9e4f78c9710b6d8ab"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ed22f9665327a981f288a4f758a432824dc0314e4195a0eaeb0da56a477da94d"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0e4e8981c6e7e9e637e637ad8ffe948a09e5434bc5f52ecbb82b4b4cfc092bfb"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c592eb91a5968058a561d358d0fef59099ed152cfb3e1cd14eee51a7a93879e"}, + {file = "ujson-5.7.0.tar.gz", hash = "sha256:e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "uvicorn" +version = "0.20.0" +description = "The lightning-fast ASGI server." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, + {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.17.0" +description = "Fast implementation of asyncio event loop on top of libuv" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, + {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, + {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, +] + +[package.extras] +dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] + +[[package]] +name = "validators" +version = "0.20.0" +description = "Python Data Validation for Humans™." +category = "main" +optional = false +python-versions = ">=3.4" +files = [ + {file = "validators-0.20.0.tar.gz", hash = "sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a"}, +] + +[package.dependencies] +decorator = ">=3.4.0" + +[package.extras] +test = ["flake8 (>=2.4.0)", "isort (>=4.2.2)", "pytest (>=2.2.3)"] + +[[package]] +name = "watchfiles" +version = "0.19.0" +description = "Simple, modern and high performance file watching and code reload in python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "watchfiles-0.19.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:91633e64712df3051ca454ca7d1b976baf842d7a3640b87622b323c55f3345e7"}, + {file = "watchfiles-0.19.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b6577b8c6c8701ba8642ea9335a129836347894b666dd1ec2226830e263909d3"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:18b28f6ad871b82df9542ff958d0c86bb0d8310bb09eb8e87d97318a3b5273af"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac19dc9cbc34052394dbe81e149411a62e71999c0a19e1e09ce537867f95ae0"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:09ea3397aecbc81c19ed7f025e051a7387feefdb789cf768ff994c1228182fda"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0376deac92377817e4fb8f347bf559b7d44ff556d9bc6f6208dd3f79f104aaf"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c75eff897786ee262c9f17a48886f4e98e6cfd335e011c591c305e5d083c056"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb5d45c4143c1dd60f98a16187fd123eda7248f84ef22244818c18d531a249d1"}, + {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:79c533ff593db861ae23436541f481ec896ee3da4e5db8962429b441bbaae16e"}, + {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3d7d267d27aceeeaa3de0dd161a0d64f0a282264d592e335fff7958cc0cbae7c"}, + {file = "watchfiles-0.19.0-cp37-abi3-win32.whl", hash = "sha256:176a9a7641ec2c97b24455135d58012a5be5c6217fc4d5fef0b2b9f75dbf5154"}, + {file = "watchfiles-0.19.0-cp37-abi3-win_amd64.whl", hash = "sha256:945be0baa3e2440151eb3718fd8846751e8b51d8de7b884c90b17d271d34cae8"}, + {file = "watchfiles-0.19.0-cp37-abi3-win_arm64.whl", hash = "sha256:0089c6dc24d436b373c3c57657bf4f9a453b13767150d17284fc6162b2791911"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cae3dde0b4b2078f31527acff6f486e23abed307ba4d3932466ba7cdd5ecec79"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f3920b1285a7d3ce898e303d84791b7bf40d57b7695ad549dc04e6a44c9f120"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9afd0d69429172c796164fd7fe8e821ade9be983f51c659a38da3faaaaac44dc"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68dce92b29575dda0f8d30c11742a8e2b9b8ec768ae414b54f7453f27bdf9545"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:5569fc7f967429d4bc87e355cdfdcee6aabe4b620801e2cf5805ea245c06097c"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5471582658ea56fca122c0f0d0116a36807c63fefd6fdc92c71ca9a4491b6b48"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b538014a87f94d92f98f34d3e6d2635478e6be6423a9ea53e4dd96210065e193"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20b44221764955b1e703f012c74015306fb7e79a00c15370785f309b1ed9aa8d"}, + {file = "watchfiles-0.19.0.tar.gz", hash = "sha256:d9b073073e048081e502b6c6b0b88714c026a1a4c890569238d04aca5f9ca74b"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "weaviate-client" +version = "3.18.0" +description = "A python native weaviate client" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "weaviate-client-3.18.0.tar.gz", hash = "sha256:423a526518a32505c5293328e5f252e6cbbf20e4b3124733f70d10fc0d6823c9"}, + {file = "weaviate_client-3.18.0-py3-none-any.whl", hash = "sha256:42b324286a4b4436317e5d2c6ba48c07da6cf01518efdd47ee097e7a8cc7584c"}, +] + +[package.dependencies] +authlib = ">=1.1.0" +requests = ">=2.28.0,<2.29.0" +tqdm = ">=4.59.0,<5.0.0" +validators = ">=0.18.2,<=0.21.0" + +[package.extras] +grpc = ["grpcio", "grpcio-tools"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "websockets" +version = "10.4" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, +] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + +[[package]] +name = "xlsxwriter" +version = "3.1.0" +description = "A Python module for creating Excel XLSX files." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "XlsxWriter-3.1.0-py3-none-any.whl", hash = "sha256:b70a147d36235d1ee835cfd037396f789db1f76740a0e5c917d54137169341de"}, + {file = "XlsxWriter-3.1.0.tar.gz", hash = "sha256:02913b50b74c00f165933d5da3e3a02cab4204cb4932722a1b342c5c71034122"}, +] + +[[package]] +name = "yarl" +version = "1.9.2" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, + {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, + {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, + {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, + {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "zstandard" +version = "0.21.0" +description = "Zstandard bindings for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zstandard-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:649a67643257e3b2cff1c0a73130609679a5673bf389564bc6d4b164d822a7ce"}, + {file = "zstandard-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:144a4fe4be2e747bf9c646deab212666e39048faa4372abb6a250dab0f347a29"}, + {file = "zstandard-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b72060402524ab91e075881f6b6b3f37ab715663313030d0ce983da44960a86f"}, + {file = "zstandard-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8257752b97134477fb4e413529edaa04fc0457361d304c1319573de00ba796b1"}, + {file = "zstandard-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c053b7c4cbf71cc26808ed67ae955836232f7638444d709bfc302d3e499364fa"}, + {file = "zstandard-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2769730c13638e08b7a983b32cb67775650024632cd0476bf1ba0e6360f5ac7d"}, + {file = "zstandard-0.21.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7d3bc4de588b987f3934ca79140e226785d7b5e47e31756761e48644a45a6766"}, + {file = "zstandard-0.21.0-cp310-cp310-win32.whl", hash = "sha256:67829fdb82e7393ca68e543894cd0581a79243cc4ec74a836c305c70a5943f07"}, + {file = "zstandard-0.21.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6048a287f8d2d6e8bc67f6b42a766c61923641dd4022b7fd3f7439e17ba5a4d"}, + {file = "zstandard-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7f2afab2c727b6a3d466faee6974a7dad0d9991241c498e7317e5ccf53dbc766"}, + {file = "zstandard-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff0852da2abe86326b20abae912d0367878dd0854b8931897d44cfeb18985472"}, + {file = "zstandard-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12fa383e315b62630bd407477d750ec96a0f438447d0e6e496ab67b8b451d39"}, + {file = "zstandard-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1b9703fe2e6b6811886c44052647df7c37478af1b4a1a9078585806f42e5b15"}, + {file = "zstandard-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df28aa5c241f59a7ab524f8ad8bb75d9a23f7ed9d501b0fed6d40ec3064784e8"}, + {file = "zstandard-0.21.0-cp311-cp311-win32.whl", hash = "sha256:0aad6090ac164a9d237d096c8af241b8dcd015524ac6dbec1330092dba151657"}, + {file = "zstandard-0.21.0-cp311-cp311-win_amd64.whl", hash = "sha256:48b6233b5c4cacb7afb0ee6b4f91820afbb6c0e3ae0fa10abbc20000acdf4f11"}, + {file = "zstandard-0.21.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e7d560ce14fd209db6adacce8908244503a009c6c39eee0c10f138996cd66d3e"}, + {file = "zstandard-0.21.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e6e131a4df2eb6f64961cea6f979cdff22d6e0d5516feb0d09492c8fd36f3bc"}, + {file = "zstandard-0.21.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1e0c62a67ff425927898cf43da2cf6b852289ebcc2054514ea9bf121bec10a5"}, + {file = "zstandard-0.21.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1545fb9cb93e043351d0cb2ee73fa0ab32e61298968667bb924aac166278c3fc"}, + {file = "zstandard-0.21.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe6c821eb6870f81d73bf10e5deed80edcac1e63fbc40610e61f340723fd5f7c"}, + {file = "zstandard-0.21.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ddb086ea3b915e50f6604be93f4f64f168d3fc3cef3585bb9a375d5834392d4f"}, + {file = "zstandard-0.21.0-cp37-cp37m-win32.whl", hash = "sha256:57ac078ad7333c9db7a74804684099c4c77f98971c151cee18d17a12649bc25c"}, + {file = "zstandard-0.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1243b01fb7926a5a0417120c57d4c28b25a0200284af0525fddba812d575f605"}, + {file = "zstandard-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ea68b1ba4f9678ac3d3e370d96442a6332d431e5050223626bdce748692226ea"}, + {file = "zstandard-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8070c1cdb4587a8aa038638acda3bd97c43c59e1e31705f2766d5576b329e97c"}, + {file = "zstandard-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4af612c96599b17e4930fe58bffd6514e6c25509d120f4eae6031b7595912f85"}, + {file = "zstandard-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff891e37b167bc477f35562cda1248acc115dbafbea4f3af54ec70821090965"}, + {file = "zstandard-0.21.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9fec02ce2b38e8b2e86079ff0b912445495e8ab0b137f9c0505f88ad0d61296"}, + {file = "zstandard-0.21.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdbe350691dec3078b187b8304e6a9c4d9db3eb2d50ab5b1d748533e746d099"}, + {file = "zstandard-0.21.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b69cccd06a4a0a1d9fb3ec9a97600055cf03030ed7048d4bcb88c574f7895773"}, + {file = "zstandard-0.21.0-cp38-cp38-win32.whl", hash = "sha256:9980489f066a391c5572bc7dc471e903fb134e0b0001ea9b1d3eff85af0a6f1b"}, + {file = "zstandard-0.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:0e1e94a9d9e35dc04bf90055e914077c80b1e0c15454cc5419e82529d3e70728"}, + {file = "zstandard-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2d61675b2a73edcef5e327e38eb62bdfc89009960f0e3991eae5cc3d54718de"}, + {file = "zstandard-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25fbfef672ad798afab12e8fd204d122fca3bc8e2dcb0a2ba73bf0a0ac0f5f07"}, + {file = "zstandard-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62957069a7c2626ae80023998757e27bd28d933b165c487ab6f83ad3337f773d"}, + {file = "zstandard-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e10ed461e4807471075d4b7a2af51f5234c8f1e2a0c1d37d5ca49aaaad49e8"}, + {file = "zstandard-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cff89a036c639a6a9299bf19e16bfb9ac7def9a7634c52c257166db09d950e7"}, + {file = "zstandard-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52b2b5e3e7670bd25835e0e0730a236f2b0df87672d99d3bf4bf87248aa659fb"}, + {file = "zstandard-0.21.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b1367da0dde8ae5040ef0413fb57b5baeac39d8931c70536d5f013b11d3fc3a5"}, + {file = "zstandard-0.21.0-cp39-cp39-win32.whl", hash = "sha256:db62cbe7a965e68ad2217a056107cc43d41764c66c895be05cf9c8b19578ce9c"}, + {file = "zstandard-0.21.0-cp39-cp39-win_amd64.whl", hash = "sha256:a8d200617d5c876221304b0e3fe43307adde291b4a897e7b0617a61611dfff6a"}, + {file = "zstandard-0.21.0.tar.gz", hash = "sha256:f08e3a10d01a247877e4cb61a82a319ea746c356a3786558bed2481e6c405546"}, +] + +[package.dependencies] +cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\""} + +[package.extras] +cffi = ["cffi (>=1.11)"] + +[extras] +postgresql = ["psycopg2cffi"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "33df6345a0d11114e42451f44618e95a9573dc15311e1e64a85a878cd3df773f" diff --git a/retrieval/pyproject.toml b/retrieval/pyproject.toml new file mode 100644 index 0000000..37d13be --- /dev/null +++ b/retrieval/pyproject.toml @@ -0,0 +1,66 @@ +[tool.poetry] +name = "chatgpt-retrieval-plugin" +version = "0.1.0" +description = "" +authors = ["isafulf "] +readme = "README.md" +packages = [{include = "server"}] + +[[tool.poetry.source]] +name = "azure-sdk-dev" +url = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" +secondary = true + +[tool.poetry.dependencies] +igittigitt = "^2.1.2" +python = "^3.10" +fastapi = "^0.92.0" +uvicorn = "^0.20.0" +openai = "^0.27.5" +python-dotenv = "^0.21.1" +pydantic = "^1.10.5" +tenacity = "^8.2.1" +tiktoken = "^0.2.0" +numpy = "^1.24.2" +docx2txt = "^0.8" +PyPDF2 = "^3.0.1" +python-pptx = "^0.6.21" +python-multipart = "^0.0.6" +arrow = "^1.2.3" +chromadb = "^0.3.11" +pinecone-client = "^2.1.0" +weaviate-client = "^3.12.0" +pymilvus = "^2.2.2" +qdrant-client = {version = "^1.0.4", python = "<3.12"} +redis = "4.5.4" +supabase = "^1.0.2" +psycopg2 = "^2.9.5" +llama-index = "0.5.4" +azure-identity = "^1.12.0" +azure-search-documents = {version = "11.4.0a20230509004", source = "azure-sdk-dev"} +pgvector = "^0.1.7" +psycopg2cffi = {version = "^2.9.0", optional = true} +loguru = "^0.7.0" + +[tool.poetry.scripts] +start = "server.main:start" +dev = "local_server.main:start" + +[tool.poetry.extras] +postgresql = ["psycopg2cffi"] + +[tool.poetry.group.dev.dependencies] +httpx = "^0.23.3" +pytest = "^7.2.1" +pytest-cov = "^4.0.0" +pytest-asyncio = "^0.20.3" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +pythonpath = [ + "." +] +asyncio_mode="auto" diff --git a/retrieval/scripts/process_json/README.md b/retrieval/scripts/process_json/README.md new file mode 100644 index 0000000..fe738c8 --- /dev/null +++ b/retrieval/scripts/process_json/README.md @@ -0,0 +1,24 @@ +## Process a JSON File + +This script is a utility to process a file dump of documents in a JSON format and store them in the vector database with some metadata. It can also optionally screen the documents for personally identifiable information (PII) using a language model, and skip them if detected. Additionally, the script can extract metadata from the document using a language model. You can customize the PII detection function in [`services/pii_detection`](../../services/pii_detection.py) and the metadata extraction function in [`services/extract_metadata`](../../services/extract_metadata.py) for your use case. + +## Usage + +To run this script from the terminal, navigate to this folder and use the following command: + +``` +python process_json.py --filepath path/to/file_dump.json --custom_metadata '{"source": "file"}' --screen_for_pii True --extract_metadata True +``` + +where: + +- `path/to/file_dump.json` is the name or path to the file dump to be processed. The format of this JSON file should be a list of JSON objects, where each object represents a document. The JSON object should have a subset of the following fields: `id`, `text`, `source`, `source_id`, `url`, `created_at`, and `author`. The `text` field is required, while the rest are optional and will be used to populate the metadata of the document. If the `id` field is not specified, a random UUID will be generated for the document. +- `--custom_metadata` is an optional JSON string of key-value pairs to update the metadata of the documents. For example, `{"source": "file"}` will add a `source` field with the value `file` to the metadata of each document. The default value is an empty JSON object (`{}`). +- `--screen_for_pii` is an optional boolean flag to indicate whether to use the PII detection function or not. If set to `True`, the script will use the `screen_text_for_pii` function from the [`services/pii_detection`](../../services/pii_detection.py) module to check if the document text contains any PII using a language model. If PII is detected, the script will print a warning and skip the document. The default value is `False`. +- `--extract_metadata` is an optional boolean flag to indicate whether to try to extract metadata from the document using a language model. If set to `True`, the script will use the `extract_metadata_from_document` function from the [`services/extract_metadata`](../../services/extract_metadata.py) module to extract metadata from the document text and update the metadata object accordingly. The default value is`False`. + +The script will load the JSON file as a list of dictionaries, iterate over the data, create document objects, and batch upsert them into the database. It will also print some progress messages and error messages if any, as well as the number and content of the skipped items due to errors or PII detection. + +You can use `python process_json.py -h` to get a summary of the options and their descriptions. + +Test the script with the example file, [example.json](example.json). diff --git a/retrieval/scripts/process_json/example.json b/retrieval/scripts/process_json/example.json new file mode 100644 index 0000000..dc8f12b --- /dev/null +++ b/retrieval/scripts/process_json/example.json @@ -0,0 +1,25 @@ +[ + { + "id": "123", + "text": "This is a document about something", + "source": "file", + "source_id": "https://example.com/doc1", + "url": "https://example.com/doc1", + "created_at": "2021-01-01T12:00:00Z", + "author": "Alice" + }, + { + "text": "This is another document about something else", + "source": "file", + "source_id": "doc2.txt", + "author": "Bob" + }, + { + "id": "456", + "text": "This is Alice's phone number: 123-456-7890", + "source": "email", + "source_id": "567", + "created_at": "2021-01-02T13:00:00Z", + "author": "Alice" + } +] \ No newline at end of file diff --git a/retrieval/scripts/process_json/process_json.py b/retrieval/scripts/process_json/process_json.py new file mode 100644 index 0000000..8b9624c --- /dev/null +++ b/retrieval/scripts/process_json/process_json.py @@ -0,0 +1,147 @@ +import uuid +import json +import argparse +import asyncio + +from loguru import logger +from models.models import Document, DocumentMetadata +from datastore.datastore import DataStore +from datastore.factory import get_datastore +from services.extract_metadata import extract_metadata_from_document +from services.pii_detection import screen_text_for_pii + +DOCUMENT_UPSERT_BATCH_SIZE = 50 + + +async def process_json_dump( + filepath: str, + datastore: DataStore, + custom_metadata: dict, + screen_for_pii: bool, + extract_metadata: bool, +): + # load the json file as a list of dictionaries + with open(filepath) as json_file: + data = json.load(json_file) + + documents = [] + skipped_items = [] + # iterate over the data and create document objects + for item in data: + if len(documents) % 20 == 0: + logger.info(f"Processed {len(documents)} documents") + + try: + # get the id, text, source, source_id, url, created_at and author from the item + # use default values if not specified + id = item.get("id", None) + text = item.get("text", None) + source = item.get("source", None) + source_id = item.get("source_id", None) + url = item.get("url", None) + created_at = item.get("created_at", None) + author = item.get("author", None) + + if not text: + logger.info("No document text, skipping...") + continue + + # create a metadata object with the source, source_id, url, created_at and author + metadata = DocumentMetadata( + source=source, + source_id=source_id, + url=url, + created_at=created_at, + author=author, + ) + logger.info("metadata: ", str(metadata)) + + # update metadata with custom values + for key, value in custom_metadata.items(): + if hasattr(metadata, key): + setattr(metadata, key, value) + + # screen for pii if requested + if screen_for_pii: + pii_detected = screen_text_for_pii(text) + # if pii detected, print a warning and skip the document + if pii_detected: + logger.info("PII detected in document, skipping") + skipped_items.append(item) # add the skipped item to the list + continue + + # extract metadata if requested + if extract_metadata: + # extract metadata from the document text + extracted_metadata = extract_metadata_from_document( + f"Text: {text}; Metadata: {str(metadata)}" + ) + # get a Metadata object from the extracted metadata + metadata = DocumentMetadata(**extracted_metadata) + + # create a document object with the id or a random id, text and metadata + document = Document( + id=id or str(uuid.uuid4()), + text=text, + metadata=metadata, + ) + documents.append(document) + except Exception as e: + # log the error and continue with the next item + logger.error(f"Error processing {item}: {e}") + skipped_items.append(item) # add the skipped item to the list + + # do this in batches, the upsert method already batches documents but this allows + # us to add more descriptive logging + for i in range(0, len(documents), DOCUMENT_UPSERT_BATCH_SIZE): + # Get the text of the chunks in the current batch + batch_documents = documents[i : i + DOCUMENT_UPSERT_BATCH_SIZE] + logger.info(f"Upserting batch of {len(batch_documents)} documents, batch {i}") + logger.info("documents: ", documents) + await datastore.upsert(batch_documents) + + # print the skipped items + logger.info(f"Skipped {len(skipped_items)} items due to errors or PII detection") + for item in skipped_items: + logger.info(item) + + +async def main(): + # parse the command-line arguments + parser = argparse.ArgumentParser() + parser.add_argument("--filepath", required=True, help="The path to the json dump") + parser.add_argument( + "--custom_metadata", + default="{}", + help="A JSON string of key-value pairs to update the metadata of the documents", + ) + parser.add_argument( + "--screen_for_pii", + default=False, + type=bool, + help="A boolean flag to indicate whether to try the PII detection function (using a language model)", + ) + parser.add_argument( + "--extract_metadata", + default=False, + type=bool, + help="A boolean flag to indicate whether to try to extract metadata from the document (using a language model)", + ) + args = parser.parse_args() + + # get the arguments + filepath = args.filepath + custom_metadata = json.loads(args.custom_metadata) + screen_for_pii = args.screen_for_pii + extract_metadata = args.extract_metadata + + # initialize the db instance once as a global variable + datastore = await get_datastore() + # process the json dump + await process_json_dump( + filepath, datastore, custom_metadata, screen_for_pii, extract_metadata + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/retrieval/scripts/process_jsonl/README.md b/retrieval/scripts/process_jsonl/README.md new file mode 100644 index 0000000..df40739 --- /dev/null +++ b/retrieval/scripts/process_jsonl/README.md @@ -0,0 +1,24 @@ +## Process a JSONL File + +This script is a utility to process a file dump of documents in a JSONL format and store them in the vector database with some metadata. It can also optionally screen the documents for personally identifiable information (PII) using a language model, and skip them if detected. Additionally, the script can extract metadata from the document using a language model. You can customize the PII detection function in [`services/pii_detection`](../../services/pii_detection.py) and the metadata extraction function in [`services/extract_metadata`](../../services/extract_metadata.py) for your use case. + +## Usage + +To run this script from the terminal, navigate to this folder and use the following command: + +``` +python process_jsonl.py --filepath path/to/file_dump.jsonl --custom_metadata '{"source": "email"}' --screen_for_pii True --extract_metadata True +``` + +where: + +- `path/to/file_dump.jsonl` is the name or path to the file dump to be processed. The format of this JSONL file should be a newline-delimited JSON file, where each line is a valid JSON object representing a document. The JSON object should have a subset of the following fields: `id`, `text`, `source`, `source_id`, `url`, `created_at`, and `author`. The `text` field is required, while the rest are optional and will be used to populate the metadata of the document. If the `id` field is not specified, a random UUID will be generated for the document. +- `--custom_metadata` is an optional JSON string of key-value pairs to update the metadata of the documents. For example, `{"source": "file"}` will add a `source` field with the value `file` to the metadata of each document. The default value is an empty JSON object (`{}`). +- `--screen_for_pii` is an optional boolean flag to indicate whether to use the PII detection function or not. If set to `True`, the script will use the `screen_text_for_pii` function from the [`services/pii_detection`](../../services/pii_detection.py) module to check if the document text contains any PII using a language model. If PII is detected, the script will print a warning and skip the document. The default value is `False`. +- `--extract_metadata` is an optional boolean flag to indicate whether to try to extract metadata from the document using a language model. If set to `True`, the script will use the `extract_metadata_from_document` function from the [`services/extract_metadata`](../../services/extract_metadata.py) module to extract metadata from the document text and update the metadata object accordingly. The default value is`False`. + +The script will open the JSONL file as a generator of dictionaries, iterate over the data, create document objects, and batch upsert them into the database. It will also print some progress messages and error messages if any, as well as the number and content of the skipped items due to errors, PII detection, or metadata extraction issues. + +You can use `python process_jsonl.py -h` to get a summary of the options and their descriptions. + +Test the script with the example file, [example.jsonl](example.jsonl). diff --git a/retrieval/scripts/process_jsonl/example.jsonl b/retrieval/scripts/process_jsonl/example.jsonl new file mode 100644 index 0000000..6335bc4 --- /dev/null +++ b/retrieval/scripts/process_jsonl/example.jsonl @@ -0,0 +1,6 @@ +{"id": "4", "text": "This document only has an ID and text. The other fields are missing."} +{"text": "This document has no ID, but it has text and a source.", "source": "email"} +{"id": "6", "text": "This document has an ID, text, and author, but no source information.", "author": "John Doe"} +{"text": "This document has text, a source, and a URL, but no ID or author.", "source": "file", "url": "https://example.com/file/2"} +{"id": "8", "text": "This document has an ID, text, source, and created_at timestamp, but no author or URL.", "source": "chat", "created_at": "2022-01-04T00:00:00"} +{"id": "9", "text": "This document contains PII. John Smith's email address is john.smith@example.com and his phone number is +1 (555) 123-4567.", "source": "email", "source_id": "email_2", "url": "https://example.com/email/2", "created_at": "2022-01-05T00:00:00", "author": "John Smith"} \ No newline at end of file diff --git a/retrieval/scripts/process_jsonl/process_jsonl.py b/retrieval/scripts/process_jsonl/process_jsonl.py new file mode 100644 index 0000000..463871b --- /dev/null +++ b/retrieval/scripts/process_jsonl/process_jsonl.py @@ -0,0 +1,145 @@ +import uuid +import json +import argparse +import asyncio + +from loguru import logger +from models.models import Document, DocumentMetadata +from datastore.datastore import DataStore +from datastore.factory import get_datastore +from services.extract_metadata import extract_metadata_from_document +from services.pii_detection import screen_text_for_pii + +DOCUMENT_UPSERT_BATCH_SIZE = 50 + + +async def process_jsonl_dump( + filepath: str, + datastore: DataStore, + custom_metadata: dict, + screen_for_pii: bool, + extract_metadata: bool, +): + # open the jsonl file as a generator of dictionaries + with open(filepath) as jsonl_file: + data = [json.loads(line) for line in jsonl_file] + + documents = [] + skipped_items = [] + # iterate over the data and create document objects + for item in data: + if len(documents) % 20 == 0: + logger.info(f"Processed {len(documents)} documents") + + try: + # get the id, text, source, source_id, url, created_at and author from the item + # use default values if not specified + id = item.get("id", None) + text = item.get("text", None) + source = item.get("source", None) + source_id = item.get("source_id", None) + url = item.get("url", None) + created_at = item.get("created_at", None) + author = item.get("author", None) + + if not text: + logger.info("No document text, skipping...") + continue + + # create a metadata object with the source, source_id, url, created_at and author + metadata = DocumentMetadata( + source=source, + source_id=source_id, + url=url, + created_at=created_at, + author=author, + ) + + # update metadata with custom values + for key, value in custom_metadata.items(): + if hasattr(metadata, key): + setattr(metadata, key, value) + + # screen for pii if requested + if screen_for_pii: + pii_detected = screen_text_for_pii(text) + # if pii detected, print a warning and skip the document + if pii_detected: + logger.info("PII detected in document, skipping") + skipped_items.append(item) # add the skipped item to the list + continue + + # extract metadata if requested + if extract_metadata: + # extract metadata from the document text + extracted_metadata = extract_metadata_from_document( + f"Text: {text}; Metadata: {str(metadata)}" + ) + # get a Metadata object from the extracted metadata + metadata = DocumentMetadata(**extracted_metadata) + + # create a document object with the id, text and metadata + document = Document( + id=id, + text=text, + metadata=metadata, + ) + documents.append(document) + except Exception as e: + # log the error and continue with the next item + logger.error(f"Error processing {item}: {e}") + skipped_items.append(item) # add the skipped item to the list + + # do this in batches, the upsert method already batches documents but this allows + # us to add more descriptive logging + for i in range(0, len(documents), DOCUMENT_UPSERT_BATCH_SIZE): + # Get the text of the chunks in the current batch + batch_documents = documents[i : i + DOCUMENT_UPSERT_BATCH_SIZE] + logger.info(f"Upserting batch of {len(batch_documents)} documents, batch {i}") + await datastore.upsert(batch_documents) + + # print the skipped items + logger.info(f"Skipped {len(skipped_items)} items due to errors or PII detection") + for item in skipped_items: + logger.info(item) + + +async def main(): + # parse the command-line arguments + parser = argparse.ArgumentParser() + parser.add_argument("--filepath", required=True, help="The path to the jsonl dump") + parser.add_argument( + "--custom_metadata", + default="{}", + help="A JSON string of key-value pairs to update the metadata of the documents", + ) + parser.add_argument( + "--screen_for_pii", + default=False, + type=bool, + help="A boolean flag to indicate whether to try the PII detection function (using a language model)", + ) + parser.add_argument( + "--extract_metadata", + default=False, + type=bool, + help="A boolean flag to indicate whether to try to extract metadata from the document (using a language model)", + ) + args = parser.parse_args() + + # get the arguments + filepath = args.filepath + custom_metadata = json.loads(args.custom_metadata) + screen_for_pii = args.screen_for_pii + extract_metadata = args.extract_metadata + + # initialize the db instance once as a global variable + datastore = await get_datastore() + # process the jsonl dump + await process_jsonl_dump( + filepath, datastore, custom_metadata, screen_for_pii, extract_metadata + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/retrieval/scripts/process_zip/README.md b/retrieval/scripts/process_zip/README.md new file mode 100644 index 0000000..181a733 --- /dev/null +++ b/retrieval/scripts/process_zip/README.md @@ -0,0 +1,24 @@ +## Process a ZIP File + +This script is a utility to process a file dump of documents in a zip file and store them in the vector database with some metadata. It can also optionally screen the documents for personally identifiable information (PII) using a language model, and skip them if detected. Additionally, the script can extract metadata from the document using a language model. You can customize the PII detection function in [`services/pii_detection`](../../services/pii_detection.py) and the metadata extraction function in [`services/extract_metadata`](../../services/extract_metadata.py) for your use case. + +## Usage + +To run this script from the terminal, navigate to this folder and use the following command: + +``` +python process_zip.py --filepath path/to/file_dump.zip --custom_metadata '{"source": "email"}' --screen_for_pii True --extract_metadata True +``` + +where: + +- `path/to/file_dump.zip` is the name or path to the file dump to be processed. The format of this zip file should be a zip file containing of docx, pdf, txt, md and pptx files (any internal folder structure is acceptable). +- `--custom_metadata` is an optional JSON string of key-value pairs to update the metadata of the documents. For example, `{"source": "file"}` will add a `source` field with the value `file` to the metadata of each document. The default value is an empty JSON object (`{}`). +- `--screen_for_pii` is an optional boolean flag to indicate whether to use the PII detection function or not. If set to `True`, the script will use the `screen_text_for_pii` function from the [`services/pii_detection`](../../services/pii_detection.py) module to check if the document text contains any PII using a language model. If PII is detected, the script will print a warning and skip the document. The default value is `False`. +- `--extract_metadata` is an optional boolean flag to indicate whether to try to extract metadata from the document using a language model. If set to `True`, the script will use the `extract_metadata_from_document` function from the [`services/extract_metadata`](../../services/extract_metadata.py) module to extract metadata from the document text and update the metadata object accordingly. The default value is`False`. + +The script will extract the files from the zip file into a temporary directory named `dump`, process each file and store the document text and metadata in the database, and then delete the temporary directory and its contents. It will also print some progress messages and error messages if any. + +You can use `python process_zip.py -h` to get a summary of the options and their descriptions. + +Test the script with the example file, [example.zip](example.zip). diff --git a/retrieval/scripts/process_zip/example.zip b/retrieval/scripts/process_zip/example.zip new file mode 100644 index 0000000000000000000000000000000000000000..d35d27639087c73a9eb5148aa507f35b862d44b5 GIT binary patch literal 53296 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 cf9LZLv`q List[str]: + """ + Split a text into chunks of ~CHUNK_SIZE tokens, based on punctuation and newline boundaries. + + Args: + text: The text to split into chunks. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A list of text chunks, each of which is a string of ~CHUNK_SIZE tokens. + """ + # Return an empty list if the text is empty or whitespace + if not text or text.isspace(): + return [] + + # Tokenize the text + tokens = tokenizer.encode(text, disallowed_special=()) + + # Initialize an empty list of chunks + chunks = [] + + # Use the provided chunk token size or the default one + chunk_size = chunk_token_size or CHUNK_SIZE + + # Initialize a counter for the number of chunks + num_chunks = 0 + + # Loop until all tokens are consumed + while tokens and num_chunks < MAX_NUM_CHUNKS: + # Take the first chunk_size tokens as a chunk + chunk = tokens[:chunk_size] + + # Decode the chunk into text + chunk_text = tokenizer.decode(chunk) + + # Skip the chunk if it is empty or whitespace + if not chunk_text or chunk_text.isspace(): + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(chunk) :] + # Continue to the next iteration of the loop + continue + + # Find the last period or punctuation mark in the chunk + last_punctuation = max( + chunk_text.rfind("."), + chunk_text.rfind("?"), + chunk_text.rfind("!"), + chunk_text.rfind("\n"), + ) + + # If there is a punctuation mark, and the last punctuation index is before MIN_CHUNK_SIZE_CHARS + if last_punctuation != -1 and last_punctuation > MIN_CHUNK_SIZE_CHARS: + # Truncate the chunk text at the punctuation mark + chunk_text = chunk_text[: last_punctuation + 1] + + # Remove any newline characters and strip any leading or trailing whitespace + chunk_text_to_append = chunk_text.replace("\n", " ").strip() + + if len(chunk_text_to_append) > MIN_CHUNK_LENGTH_TO_EMBED: + # Append the chunk text to the list of chunks + chunks.append(chunk_text_to_append) + + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(tokenizer.encode(chunk_text, disallowed_special=())) :] + + # Increment the number of chunks + num_chunks += 1 + + # Handle the remaining tokens + if tokens: + remaining_text = tokenizer.decode(tokens).replace("\n", " ").strip() + if len(remaining_text) > MIN_CHUNK_LENGTH_TO_EMBED: + chunks.append(remaining_text) + + return chunks + + +def create_document_chunks( + doc: Document, chunk_token_size: Optional[int] +) -> Tuple[List[DocumentChunk], str]: + """ + Create a list of document chunks from a document object and return the document id. + + Args: + doc: The document object to create chunks from. It should have a text attribute and optionally an id and a metadata attribute. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A tuple of (doc_chunks, doc_id), where doc_chunks is a list of document chunks, each of which is a DocumentChunk object with an id, a document_id, a text, and a metadata attribute, + and doc_id is the id of the document object, generated if not provided. The id of each chunk is generated from the document id and a sequential number, and the metadata is copied from the document object. + """ + # Check if the document text is empty or whitespace + if not doc.text or doc.text.isspace(): + return [], doc.id or str(uuid.uuid4()) + + # Generate a document id if not provided + doc_id = doc.id or str(uuid.uuid4()) + + # Split the document text into chunks + text_chunks = get_text_chunks(doc.text, chunk_token_size) + + metadata = ( + DocumentChunkMetadata(**doc.metadata.__dict__) + if doc.metadata is not None + else DocumentChunkMetadata() + ) + + metadata.document_id = doc_id + + # Initialize an empty list of chunks for this document + doc_chunks = [] + + # Assign each chunk a sequential number and create a DocumentChunk object + for i, text_chunk in enumerate(text_chunks): + chunk_id = f"{doc_id}_{i}" + doc_chunk = DocumentChunk( + id=chunk_id, + text=text_chunk, + metadata=metadata, + ) + # Append the chunk object to the list of chunks for this document + doc_chunks.append(doc_chunk) + + # Return the list of chunks and the document id + return doc_chunks, doc_id + + +def get_document_chunks( + documents: List[Document], chunk_token_size: Optional[int] +) -> Dict[str, List[DocumentChunk]]: + """ + Convert a list of documents into a dictionary from document id to list of document chunks. + + Args: + documents: The list of documents to convert. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A dictionary mapping each document id to a list of document chunks, each of which is a DocumentChunk object + with text, metadata, and embedding attributes. + """ + # Initialize an empty dictionary of lists of chunks + chunks: Dict[str, List[DocumentChunk]] = {} + + # Initialize an empty list of all chunks + all_chunks: List[DocumentChunk] = [] + + # Loop over each document and create chunks + for doc in documents: + doc_chunks, doc_id = create_document_chunks(doc, chunk_token_size) + + # Append the chunks for this document to the list of all chunks + all_chunks.extend(doc_chunks) + + # Add the list of chunks for this document to the dictionary with the document id as the key + chunks[doc_id] = doc_chunks + + # Check if there are no chunks + if not all_chunks: + return {} + + # Get all the embeddings for the document chunks in batches, using get_embeddings + embeddings: List[List[float]] = [] + for i in range(0, len(all_chunks), EMBEDDINGS_BATCH_SIZE): + # Get the text of the chunks in the current batch + batch_texts = [ + chunk.text for chunk in all_chunks[i : i + EMBEDDINGS_BATCH_SIZE] + ] + + # Get the embeddings for the batch texts + batch_embeddings = get_embeddings(batch_texts) + + # Append the batch embeddings to the embeddings list + embeddings.extend(batch_embeddings) + + # Update the document chunk objects with the embeddings + for i, chunk in enumerate(all_chunks): + # Assign the embedding from the embeddings list to the chunk object + chunk.embedding = embeddings[i] + + return chunks diff --git a/retrieval/services/date.py b/retrieval/services/date.py new file mode 100644 index 0000000..476c7ae --- /dev/null +++ b/retrieval/services/date.py @@ -0,0 +1,24 @@ +import arrow +from loguru import logger + + +def to_unix_timestamp(date_str: str) -> int: + """ + Convert a date string to a unix timestamp (seconds since epoch). + + Args: + date_str: The date string to convert. + + Returns: + The unix timestamp corresponding to the date string. + + If the date string cannot be parsed as a valid date format, returns the current unix timestamp and prints a warning. + """ + # Try to parse the date string using arrow, which supports many common date formats + try: + date_obj = arrow.get(date_str) + return int(date_obj.timestamp()) + except arrow.parser.ParserError: + # If the parsing fails, return the current unix timestamp and print a warning + logger.info(f"Invalid date format: {date_str}") + return int(arrow.now().timestamp()) diff --git a/retrieval/services/extract_metadata.py b/retrieval/services/extract_metadata.py new file mode 100644 index 0000000..8c8d4ae --- /dev/null +++ b/retrieval/services/extract_metadata.py @@ -0,0 +1,43 @@ +from models.models import Source +from services.openai import get_chat_completion +import json +from typing import Dict +import os +from loguru import logger + +def extract_metadata_from_document(text: str) -> Dict[str, str]: + sources = Source.__members__.keys() + sources_string = ", ".join(sources) + # This prompt is just an example, change it to fit your use case + messages = [ + { + "role": "system", + "content": f""" + Given a document from a user, try to extract the following metadata: + - source: string, one of {sources_string} + - url: string or don't specify + - created_at: string or don't specify + - author: string or don't specify + + Respond with a JSON containing the extracted metadata in key value pairs. If you don't find a metadata field, don't specify it. + """, + }, + {"role": "user", "content": text}, + ] + + # NOTE: Azure Open AI requires deployment id + # Read environment variable - if not set - not used + completion = get_chat_completion( + messages, + "gpt-4", + os.environ.get("OPENAI_METADATA_EXTRACTIONMODEL_DEPLOYMENTID") + ) # TODO: change to your preferred model name + + logger.info(f"completion: {completion}") + + try: + metadata = json.loads(completion) + except: + metadata = {} + + return metadata diff --git a/retrieval/services/file.py b/retrieval/services/file.py new file mode 100644 index 0000000..136fc17 --- /dev/null +++ b/retrieval/services/file.py @@ -0,0 +1,117 @@ +import os +from io import BufferedReader +from typing import Optional +from fastapi import UploadFile +import mimetypes +from PyPDF2 import PdfReader +import docx2txt +import csv +import pptx +from loguru import logger + +from models.models import Document, DocumentMetadata + + +async def get_document_from_file( + file: UploadFile, metadata: DocumentMetadata +) -> Document: + extracted_text = await extract_text_from_form_file(file) + + doc = Document(text=extracted_text, metadata=metadata) + + return doc + + +def extract_text_from_filepath(filepath: str, mimetype: Optional[str] = None) -> str: + """Return the text content of a file given its filepath.""" + + if mimetype is None: + # Get the mimetype of the file based on its extension + mimetype, _ = mimetypes.guess_type(filepath) + + if not mimetype: + if filepath.endswith(".md"): + mimetype = "text/markdown" + else: + raise Exception("Unsupported file type") + + try: + with open(filepath, "rb") as file: + extracted_text = extract_text_from_file(file, mimetype) + except Exception as e: + logger.error(e) + raise e + + return extracted_text + + +def extract_text_from_file(file: BufferedReader, mimetype: str) -> str: + if mimetype == "application/pdf": + # Extract text from pdf using PyPDF2 + reader = PdfReader(file) + extracted_text = " ".join([page.extract_text() for page in reader.pages]) + elif mimetype == "text/plain" or mimetype == "text/markdown": + # Read text from plain text file + extracted_text = file.read().decode("utf-8") + elif ( + mimetype + == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ): + # Extract text from docx using docx2txt + extracted_text = docx2txt.process(file) + elif mimetype == "text/csv": + # Extract text from csv using csv module + extracted_text = "" + decoded_buffer = (line.decode("utf-8") for line in file) + reader = csv.reader(decoded_buffer) + for row in reader: + extracted_text += " ".join(row) + "\n" + elif ( + mimetype + == "application/vnd.openxmlformats-officedocument.presentationml.presentation" + ): + # Extract text from pptx using python-pptx + extracted_text = "" + presentation = pptx.Presentation(file) + for slide in presentation.slides: + for shape in slide.shapes: + if shape.has_text_frame: + for paragraph in shape.text_frame.paragraphs: + for run in paragraph.runs: + extracted_text += run.text + " " + extracted_text += "\n" + else: + # Unsupported file type + raise ValueError("Unsupported file type: {}".format(mimetype)) + + return extracted_text + + +# Extract text from a file based on its mimetype +async def extract_text_from_form_file(file: UploadFile): + """Return the text content of a file.""" + # get the file body from the upload file object + mimetype = file.content_type + logger.info(f"mimetype: {mimetype}") + logger.info(f"file.file: {file.file}") + logger.info("file: ", file) + + file_stream = await file.read() + + temp_file_path = "/tmp/temp_file" + + # write the file to a temporary location + with open(temp_file_path, "wb") as f: + f.write(file_stream) + + try: + extracted_text = extract_text_from_filepath(temp_file_path, mimetype) + except Exception as e: + logger.error(e) + os.remove(temp_file_path) + raise e + + # remove file from temp location + os.remove(temp_file_path) + + return extracted_text diff --git a/retrieval/services/openai.py b/retrieval/services/openai.py new file mode 100644 index 0000000..ddc2855 --- /dev/null +++ b/retrieval/services/openai.py @@ -0,0 +1,77 @@ +from typing import List +import openai +import os +from loguru import logger + +from tenacity import retry, wait_random_exponential, stop_after_attempt + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(3)) +def get_embeddings(texts: List[str]) -> List[List[float]]: + """ + Embed texts using OpenAI's ada model. + + Args: + texts: The list of texts to embed. + + Returns: + A list of embeddings, each of which is a list of floats. + + Raises: + Exception: If the OpenAI API call fails. + """ + # Call the OpenAI API to get the embeddings + # NOTE: Azure Open AI requires deployment id + deployment = os.environ.get("OPENAI_EMBEDDINGMODEL_DEPLOYMENTID") + + response = {} + if deployment == None: + response = openai.Embedding.create(input=texts, model="text-embedding-ada-002") + else: + response = openai.Embedding.create(input=texts, deployment_id=deployment) + + # Extract the embedding data from the response + data = response["data"] # type: ignore + + # Return the embeddings as a list of lists of floats + return [result["embedding"] for result in data] + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(3)) +def get_chat_completion( + messages, + model="gpt-3.5-turbo", # use "gpt-4" for better results + deployment_id = None +): + """ + Generate a chat completion using OpenAI's chat completion API. + + Args: + messages: The list of messages in the chat history. + model: The name of the model to use for the completion. Default is gpt-3.5-turbo, which is a fast, cheap and versatile model. Use gpt-4 for higher quality but slower results. + + Returns: + A string containing the chat completion. + + Raises: + Exception: If the OpenAI API call fails. + """ + # call the OpenAI chat completion API with the given messages + # Note: Azure Open AI requires deployment id + response = {} + if deployment_id == None: + response = openai.ChatCompletion.create( + model=model, + messages=messages, + ) + else: + response = openai.ChatCompletion.create( + deployment_id = deployment_id, + messages=messages, + ) + + + choices = response["choices"] # type: ignore + completion = choices[0].message.content.strip() + logger.info(f"Completion: {completion}") + return completion diff --git a/retrieval/services/pii_detection.py b/retrieval/services/pii_detection.py new file mode 100644 index 0000000..7d90151 --- /dev/null +++ b/retrieval/services/pii_detection.py @@ -0,0 +1,32 @@ +import os +from services.openai import get_chat_completion + + +def screen_text_for_pii(text: str) -> bool: + # This prompt is just an example, change it to fit your use case + messages = [ + { + "role": "system", + "content": f""" + You can only respond with the word "True" or "False", where your answer indicates whether the text in the user's message contains PII. + Do not explain your answer, and do not use punctuation. + Your task is to identify whether the text extracted from your company files + contains sensitive PII information that should not be shared with the broader company. Here are some things to look out for: + - An email address that identifies a specific person in either the local-part or the domain + - The postal address of a private residence (must include at least a street name) + - The postal address of a public place (must include either a street name or business name) + - Notes about hiring decisions with mentioned names of candidates. The user will send a document for you to analyze. + """, + }, + {"role": "user", "content": text}, + ] + + completion = get_chat_completion( + messages, + deployment_id=os.environ.get("OPENAI_COMPLETIONMODEL_DEPLOYMENTID") + ) + + if completion.startswith("True"): + return True + + return False diff --git a/retrieval/tests/__init__.py b/retrieval/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retrieval/tests/datastore/providers/analyticdb/test_analyticdb_datastore.py b/retrieval/tests/datastore/providers/analyticdb/test_analyticdb_datastore.py new file mode 100644 index 0000000..9a79b9f --- /dev/null +++ b/retrieval/tests/datastore/providers/analyticdb/test_analyticdb_datastore.py @@ -0,0 +1,323 @@ +import pytest +from models.models import ( + DocumentChunkMetadata, + DocumentMetadataFilter, + DocumentChunk, + QueryWithEmbedding, + Source, +) +from datastore.providers.analyticdb_datastore import ( + OUTPUT_DIM, + AnalyticDBDataStore, +) + + +@pytest.fixture +def analyticdb_datastore(): + return AnalyticDBDataStore() + + +@pytest.fixture +def document_chunk_one(): + doc_id = "zerp" + doc_chunks = [] + + ids = ["abc_123", "def_456", "ghi_789"] + texts = [ + "lorem ipsum dolor sit amet", + "consectetur adipiscing elit", + "sed do eiusmod tempor incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "1929-10-28T09:30:00-05:00", + "2009-01-03T16:39:57-08:00", + "2021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + embeddings = [[x] * OUTPUT_DIM for x in range(3)] + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks.append(chunk) + + return {doc_id: doc_chunks} + + +@pytest.fixture +def document_chunk_two(): + doc_id_1 = "zerp" + doc_chunks_1 = [] + + ids = ["abc_123", "def_456", "ghi_789"] + texts = [ + "1lorem ipsum dolor sit amet", + "2consectetur adipiscing elit", + "3sed do eiusmod tempor incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "1929-10-28T09:30:00-05:00", + "2009-01-03T16:39:57-08:00", + "3021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + embeddings = [[x] * OUTPUT_DIM for x in range(3)] + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id_1, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks_1.append(chunk) + + doc_id_2 = "merp" + doc_chunks_2 = [] + + ids = ["jkl_123", "lmn_456", "opq_789"] + texts = [ + "3sdsc efac feas sit qweas", + "4wert sdfas fdsc", + "52dsc fdsf eiusmod asdasd incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "4929-10-28T09:30:00-05:00", + "5009-01-03T16:39:57-08:00", + "6021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + embeddings = [[x] * OUTPUT_DIM for x in range(3, 6)] + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id_2, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks_2.append(chunk) + + return {doc_id_1: doc_chunks_1, doc_id_2: doc_chunks_2} + + +@pytest.mark.asyncio +async def test_upsert(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + query = QueryWithEmbedding( + query="lorem", + top_k=10, + embedding=[0.5] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + assert 3 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_reload(analyticdb_datastore, document_chunk_one, document_chunk_two): + await analyticdb_datastore.delete(delete_all=True) + + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + + query = QueryWithEmbedding( + query="lorem", + top_k=10, + embedding=[0.5] * OUTPUT_DIM, + ) + + query_results = await analyticdb_datastore._query(queries=[query]) + assert 3 == len(query_results[0].results) + new_store = AnalyticDBDataStore() + another_in = {i: document_chunk_two[i] for i in document_chunk_two if i != res[0]} + res = await new_store._upsert(another_in) + + query_results = await analyticdb_datastore._query(queries=[query]) + assert 1 == len(query_results) + assert 6 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_upsert_query_all(analyticdb_datastore, document_chunk_two): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_two) + assert res == list(document_chunk_two.keys()) + # Num entities currently doesn't track deletes + query = QueryWithEmbedding( + query="lorem", + top_k=10, + embedding=[0.5] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 6 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_query_accuracy(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + query = QueryWithEmbedding( + query="lorem", + top_k=1, + embedding=[0] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert 0 == query_results[0].results[0].score + assert "abc_123" == query_results[0].results[0].id + + +@pytest.mark.asyncio +async def test_query_filter(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + query = QueryWithEmbedding( + query="lorem", + top_k=1, + embedding=[0] * OUTPUT_DIM, + filter=DocumentMetadataFilter( + start_date="2000-01-03T16:39:57-08:00", end_date="2010-01-03T16:39:57-08:00" + ), + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert 0 != query_results[0].results[0].score + assert "def_456" == query_results[0].results[0].id + + +@pytest.mark.asyncio +async def test_delete_with_date_filter(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + await analyticdb_datastore.delete( + filter=DocumentMetadataFilter( + end_date="2009-01-03T16:39:57-08:00", + ) + ) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=[0] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert "ghi_789" == query_results[0].results[0].id + + +@pytest.mark.asyncio +async def test_delete_with_source_filter(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + await analyticdb_datastore.delete( + filter=DocumentMetadataFilter( + source=Source.email, + ) + ) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=[0] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 2 == len(query_results[0].results) + assert "def_456" == query_results[0].results[0].id + + +@pytest.mark.asyncio +async def test_delete_with_document_id_filter(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + await analyticdb_datastore.delete( + filter=DocumentMetadataFilter( + document_id=res[0], + ) + ) + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=[0] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 0 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_delete_with_document_id(analyticdb_datastore, document_chunk_one): + await analyticdb_datastore.delete(delete_all=True) + res = await analyticdb_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + await analyticdb_datastore.delete([res[0]]) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=[0] * OUTPUT_DIM, + ) + query_results = await analyticdb_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 0 == len(query_results[0].results) + + +# if __name__ == '__main__': +# import sys +# import pytest +# pytest.main(sys.argv) diff --git a/retrieval/tests/datastore/providers/azuresearch/test_azuresearch_datastore.py b/retrieval/tests/datastore/providers/azuresearch/test_azuresearch_datastore.py new file mode 100644 index 0000000..0a777d3 --- /dev/null +++ b/retrieval/tests/datastore/providers/azuresearch/test_azuresearch_datastore.py @@ -0,0 +1,139 @@ +import pytest +import os +import time +from typing import Union +from azure.search.documents.indexes import SearchIndexClient +from models.models import DocumentMetadataFilter, Query, Source, Document, DocumentMetadata + +AZURESEARCH_TEST_INDEX = "testindex" +os.environ["AZURESEARCH_INDEX"] = AZURESEARCH_TEST_INDEX +if os.environ.get("AZURESEARCH_SERVICE") == None: + os.environ["AZURESEARCH_SERVICE"] = "invalid service name" # Will fail anyway if not set to a real service, but allows tests to be discovered + +import datastore.providers.azuresearch_datastore +from datastore.providers.azuresearch_datastore import AzureSearchDataStore + +@pytest.fixture(scope="module") +def azuresearch_mgmt_client(): + service = os.environ["AZURESEARCH_SERVICE"] + return SearchIndexClient( + endpoint=f"https://{service}.search.windows.net", + credential=AzureSearchDataStore._create_credentials(False) + ) + +def test_translate_filter(): + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter() + ) == None + + for field in ["document_id", "source", "source_id", "author"]: + value = Source.file if field == "source" else f"test_{field}" + needs_escaping_value = None if field == "source" else f"test'_{field}" + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter(**{field: value}) + ) == f"{field} eq '{value}'" + if needs_escaping_value != None: + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter(**{field: needs_escaping_value}) + ) == f"{field} eq 'test''_{field}'" + + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter( + document_id = "test_document_id", + source = Source.file, + source_id = "test_source_id", + author = "test_author" + ) + ) == "document_id eq 'test_document_id' and source eq 'file' and source_id eq 'test_source_id' and author eq 'test_author'" + + with pytest.raises(ValueError): + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter(start_date="2023-01-01") + ) + with pytest.raises(ValueError): + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter(end_date="2023-01-01") + ) + + assert AzureSearchDataStore._translate_filter( + DocumentMetadataFilter(start_date="2023-01-01T00:00:00Z", end_date="2023-01-02T00:00:00Z", document_id = "test_document_id") + ) == "document_id eq 'test_document_id' and created_at ge 2023-01-01T00:00:00Z and created_at le 2023-01-02T00:00:00Z" + +@pytest.mark.asyncio +async def test_lifecycle_hybrid(azuresearch_mgmt_client: SearchIndexClient): + datastore.providers.azuresearch_datastore.AZURESEARCH_DISABLE_HYBRID = None + datastore.providers.azuresearch_datastore.AZURESEARCH_SEMANTIC_CONFIG = None + await lifecycle(azuresearch_mgmt_client) + +@pytest.mark.asyncio +async def test_lifecycle_vectors_only(azuresearch_mgmt_client: SearchIndexClient): + datastore.providers.azuresearch_datastore.AZURESEARCH_DISABLE_HYBRID = "1" + datastore.providers.azuresearch_datastore.AZURESEARCH_SEMANTIC_CONFIG = None + await lifecycle(azuresearch_mgmt_client) + +@pytest.mark.asyncio +async def test_lifecycle_semantic(azuresearch_mgmt_client: SearchIndexClient): + datastore.providers.azuresearch_datastore.AZURESEARCH_DISABLE_HYBRID = None + datastore.providers.azuresearch_datastore.AZURESEARCH_SEMANTIC_CONFIG = "testsemconfig" + await lifecycle(azuresearch_mgmt_client) + +async def lifecycle(azuresearch_mgmt_client: SearchIndexClient): + if AZURESEARCH_TEST_INDEX in azuresearch_mgmt_client.list_index_names(): + azuresearch_mgmt_client.delete_index(AZURESEARCH_TEST_INDEX) + assert AZURESEARCH_TEST_INDEX not in azuresearch_mgmt_client.list_index_names() + try: + store = AzureSearchDataStore() + index = azuresearch_mgmt_client.get_index(AZURESEARCH_TEST_INDEX) + assert index is not None + + result = await store.upsert([ + Document( + id="test_id_1", + text="test text", + metadata=DocumentMetadata(source=Source.file, source_id="test_source_id", author="test_author", created_at="2023-01-01T00:00:00Z", url="http://some-test-url/path")), + Document( + id="test_id_2+", + text="different", + metadata=DocumentMetadata(source=Source.file, source_id="test_source_id", author="test_author", created_at="2023-01-01T00:00:00Z", url="http://some-test-url/path"))]) + assert len(result) == 2 and result[0] == "test_id_1" and result[1] == "test_id_2+" + + # query in a loop in case we need to retry since documents aren't searchable synchronosuly after updates + for _ in range(4): + time.sleep(0.25) + result = await store.query([Query(query="text")]) + if len(result) > 0 and len(result[0].results) > 0: + break + assert len(result) == 1 and len(result[0].results) == 2 + assert result[0].results[0].metadata.document_id == "test_id_1" and result[0].results[1].metadata.document_id == "test_id_2+" + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(source_id="test_source_id"))]) + assert len(result) == 1 and len(result[0].results) == 2 + assert result[0].results[0].metadata.document_id == "test_id_1" and result[0].results[1].metadata.document_id == "test_id_2+" + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(source_id="nonexisting_id"))]) + assert len(result) == 1 and len(result[0].results) == 0 + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(start_date="2023-01-02T00:00:00Z"))]) + assert len(result) == 1 and len(result[0].results) == 0 + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(start_date="2023-01-01T00:00:00Z"))]) + assert len(result) == 1 and len(result[0].results) == 2 + assert result[0].results[0].metadata.document_id == "test_id_1" and result[0].results[1].metadata.document_id == "test_id_2+" + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(end_date="2022-12-31T00:00:00Z"))]) + assert len(result) == 1 and len(result[0].results) == 0 + + result = await store.query([Query(query="text", filter=DocumentMetadataFilter(end_date="2023-01-02T00:00:00Z"))]) + assert len(result) == 1 and len(result[0].results) == 2 + assert result[0].results[0].metadata.document_id == "test_id_1" and result[0].results[1].metadata.document_id == "test_id_2+" + + # query in a loop in case we need to retry since documents aren't searchable synchronosuly after updates + assert await store.delete(["test_id_1", "test_id_2+"]) + for _ in range(4): + time.sleep(0.25) + result = await store.query([Query(query="text")]) + if len(result) > 0 and len(result[0].results) == 0: + break + assert len(result) == 1 and len(result[0].results) == 0 + finally: + azuresearch_mgmt_client.delete_index(AZURESEARCH_TEST_INDEX) diff --git a/retrieval/tests/datastore/providers/chroma/test_chroma_datastore.py b/retrieval/tests/datastore/providers/chroma/test_chroma_datastore.py new file mode 100644 index 0000000..eacc22e --- /dev/null +++ b/retrieval/tests/datastore/providers/chroma/test_chroma_datastore.py @@ -0,0 +1,293 @@ +import shutil +from typing import Dict, List +import pytest +import random + +from datastore.providers.chroma_datastore import ChromaDataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentMetadataFilter, + QueryWithEmbedding, + Source, +) + +TEST_PERSISTENCE_DIR = "chroma_test_datastore" +COLLECTION_NAME = "documents" + + +def ephemeral_chroma_datastore() -> ChromaDataStore: + # Initialize an ephemeral in-memory ChromaDB instance + return ChromaDataStore( + collection_name=COLLECTION_NAME, in_memory=True, persistence_dir=None + ) + + +def persisted_chroma_datastore() -> ChromaDataStore: + # Initialize an in-memory ChromaDB instance with persistence + return ChromaDataStore( + collection_name=COLLECTION_NAME, + in_memory=True, + persistence_dir=TEST_PERSISTENCE_DIR, + ) + + +def get_chroma_datastore() -> ChromaDataStore: + yield ephemeral_chroma_datastore() + yield persisted_chroma_datastore() + # Delete the persistence directory after the test + + +@pytest.fixture(autouse=True) +def cleanup(): + yield + shutil.rmtree(TEST_PERSISTENCE_DIR, ignore_errors=True) + + +# Seed for deterministic testing +random.seed(0) + + +def create_embedding(dim: int) -> List[float]: + return [random.random() for _ in range(dim)] + + +# Data fixtures +TEST_EMBEDDING_DIM = 5 +N_TEST_CHUNKS = 5 + + +@pytest.fixture +def initial_document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc-{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata(), + embedding=create_embedding(TEST_EMBEDDING_DIM), + ) + for i in range(N_TEST_CHUNKS) + ] + return { + "first-doc": first_doc_chunks, + } + + +@pytest.fixture +def document_chunks(initial_document_chunks) -> Dict[str, List[DocumentChunk]]: + doc_chunks = initial_document_chunks + + for k, v in doc_chunks.items(): + for chunk in v: + chunk.metadata = DocumentChunkMetadata( + source=Source.email, created_at="2023-04-03", document_id="first-doc" + ) + chunk.embedding = create_embedding(TEST_EMBEDDING_DIM) + + doc_chunks["second-doc"] = [ + DocumentChunk( + id=f"second-doc-{i}", + text=f"Dolor sit amet {i}", + metadata=DocumentChunkMetadata( + created_at="2023-04-04", document_id="second-doc" + ), + embedding=create_embedding(TEST_EMBEDDING_DIM), + ) + for i in range(N_TEST_CHUNKS) + ] + + return doc_chunks + + +@pytest.mark.asyncio +async def test_add_chunks(document_chunks: Dict[str, List[DocumentChunk]]): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + assert datastore._collection.count() == 0 + + print(document_chunks) + + assert await datastore._upsert(document_chunks) == list(document_chunks.keys()) + assert datastore._collection.count() == sum( + len(v) for v in document_chunks.values() + ) + + +@pytest.mark.asyncio +async def test_upsert( + initial_document_chunks: Dict[str, List[DocumentChunk]], + document_chunks: Dict[str, List[DocumentChunk]], +): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + assert await datastore._upsert(initial_document_chunks) == list( + initial_document_chunks.keys() + ) + assert datastore._collection.count() == sum( + len(v) for v in initial_document_chunks.values() + ) + + assert await datastore._upsert(document_chunks) == list(document_chunks.keys()) + assert datastore._collection.count() == sum( + len(v) for v in document_chunks.values() + ) + + +@pytest.mark.asyncio +async def test_add_and_query_all(document_chunks): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + await datastore._upsert(document_chunks) == list(document_chunks.keys()) + + query = QueryWithEmbedding( + query="", + embedding=create_embedding(TEST_EMBEDDING_DIM), + top_k=10, + ) + query_results = await datastore._query(queries=[query]) + assert 1 == len(query_results) + assert 10 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_query_accuracy(document_chunks): + for _, v in document_chunks.items(): + for chunk in v: + print(f"id: {chunk.id} emb: {chunk.embedding}") + + def add_noise_to_embedding(embedding: List[float], eps: float = 0) -> List[float]: + return [x + eps * (1.0 - 2 * random.random()) for x in embedding] + + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + print(datastore._collection.get(include=["embeddings"])) + + res = await datastore._upsert(document_chunks) + + res = datastore._collection.get(include=["embeddings"]) + for id, emb in zip(res["ids"], res["embeddings"]): + print(f"id: {id} emb: {emb}") + + for _, v in document_chunks.items(): + for chunk in v: + print(f"chunk: {chunk}") + query = QueryWithEmbedding( + query="", + embedding=add_noise_to_embedding(chunk.embedding), + top_k=1, + ) + query_results = await datastore._query(queries=[query]) + print(query_results) + assert query_results[0].results[0].id == chunk.id + + +@pytest.mark.asyncio +async def test_query_filter_by_id(document_chunks): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + await datastore._upsert(document_chunks) + + for doc_id, chunks in document_chunks.items(): + query = QueryWithEmbedding( + query="", + embedding=chunks[0].embedding, + top_k=N_TEST_CHUNKS, + filter=DocumentMetadataFilter(document_id=doc_id), + ) + query_results = await datastore._query(queries=[query]) + # Assert that all document chunks are returned + assert len(query_results[0].results) == len(chunks) + assert all( + [ + result.id in [chunk.id for chunk in chunks] + for result in query_results[0].results + ] + ) + + +@pytest.mark.asyncio +async def test_query_filter_by_date(document_chunks): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + await datastore._upsert(document_chunks) + + # Filter by dates for only the first document + query = QueryWithEmbedding( + query="", + embedding=document_chunks["first-doc"][0].embedding, + top_k=N_TEST_CHUNKS, + filter=DocumentMetadataFilter( + start_date="2023-04-03", end_date="2023-04-03" + ), + ) + + query_results = await datastore._query(queries=[query]) + + # Assert that only the first document is returned + assert len(query_results[0].results) == len(document_chunks["first-doc"]) + assert all( + [ + result.id in [chunk.id for chunk in document_chunks["first-doc"]] + for result in query_results[0].results + ] + ) + + # Filter for the entire date span + query = QueryWithEmbedding( + query="", + embedding=document_chunks["first-doc"][0].embedding, + top_k=N_TEST_CHUNKS * len(document_chunks), + filter=DocumentMetadataFilter( + start_date="2023-04-03", end_date="2023-04-04" + ), + ) + + query_results = await datastore._query(queries=[query]) + + # Assert that both documents are returned + assert len(query_results[0].results) == len(document_chunks["first-doc"]) + len( + document_chunks["second-doc"] + ) + assert all( + [ + result.id + in [chunk.id for chunk in document_chunks["first-doc"]] + + [chunk.id for chunk in document_chunks["second-doc"]] + for result in query_results[0].results + ] + ) + + +@pytest.mark.asyncio +async def test_delete_by_id(document_chunks): + for datastore in get_chroma_datastore(): + await datastore.delete(delete_all=True) + + await datastore._upsert(document_chunks) + + # Delete the first document + await datastore.delete(ids=["first-doc"]) + + # Assert that the first document is deleted + query = QueryWithEmbedding( + query="", + embedding=document_chunks["first-doc"][0].embedding, + top_k=N_TEST_CHUNKS, + ) + query_results = await datastore._query(queries=[query]) + + # Assert that only the second document is still there + query_results = await datastore._query(queries=[query]) + assert len(query_results[0].results) == len(document_chunks["second-doc"]) + + assert all( + [ + result.id in [chunk.id for chunk in document_chunks["second-doc"]] + for result in query_results[0].results + ] + ) diff --git a/retrieval/tests/datastore/providers/llama/test_llama_datastore.py b/retrieval/tests/datastore/providers/llama/test_llama_datastore.py new file mode 100644 index 0000000..3039caf --- /dev/null +++ b/retrieval/tests/datastore/providers/llama/test_llama_datastore.py @@ -0,0 +1,95 @@ +from typing import Dict, List +import pytest +from datastore.providers.llama_datastore import LlamaDataStore +from models.models import DocumentChunk, DocumentChunkMetadata, QueryWithEmbedding + + +def create_embedding(non_zero_pos: int, size: int) -> List[float]: + vector = [0.0] * size + vector[non_zero_pos % size] = 1.0 + return vector + + +@pytest.fixture +def initial_document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc-{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata(), + embedding=create_embedding(i, 5), + ) + for i in range(4, 7) + ] + return { + "first-doc": first_doc_chunks, + } + + +@pytest.fixture +def queries() -> List[QueryWithEmbedding]: + queries = [ + QueryWithEmbedding( + query='Query 1', + top_k=1, + embedding=create_embedding(4, 5), + ), + QueryWithEmbedding( + query='Query 2', + top_k=2, + embedding=create_embedding(5, 5), + ), + ] + return queries + + +@pytest.fixture +def llama_datastore() -> LlamaDataStore: + return LlamaDataStore() + +@pytest.mark.asyncio +async def test_upsert( + llama_datastore: LlamaDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]] +) -> None: + """Test basic upsert.""" + doc_ids = await llama_datastore._upsert(initial_document_chunks) + assert doc_ids == [doc_id for doc_id in initial_document_chunks] + + +@pytest.mark.asyncio +async def test_query( + llama_datastore: LlamaDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], + queries: List[QueryWithEmbedding], +) -> None: + """Test basic query.""" + # insert to prepare for test + await llama_datastore._upsert(initial_document_chunks) + + query_results = await llama_datastore._query(queries) + assert len(query_results) == len(queries) + + query_0_results = query_results[0].results + query_1_results = query_results[1].results + + assert len(query_0_results) == 1 + assert len(query_1_results) == 2 + + # NOTE: this is the correct behavior + assert query_0_results[0].id == 'first-doc-4' + assert query_1_results[0].id == 'first-doc-5' + assert query_1_results[1].id == 'first-doc-4' + + +@pytest.mark.asyncio +async def test_delete( + llama_datastore: LlamaDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], +) -> None: + # insert to prepare for test + await llama_datastore._upsert(initial_document_chunks) + + is_success = llama_datastore.delete(['first-doc']) + assert is_success + diff --git a/retrieval/tests/datastore/providers/milvus/test_milvus_datastore.py b/retrieval/tests/datastore/providers/milvus/test_milvus_datastore.py new file mode 100644 index 0000000..d15b1a0 --- /dev/null +++ b/retrieval/tests/datastore/providers/milvus/test_milvus_datastore.py @@ -0,0 +1,354 @@ +# from pathlib import Path +# from dotenv import find_dotenv, load_dotenv +# env_path = Path(".") / "milvus.env" +# load_dotenv(dotenv_path=env_path, verbose=True) + +import pytest +from models.models import ( + DocumentChunkMetadata, + DocumentMetadataFilter, + DocumentChunk, + QueryWithEmbedding, + Source, +) +from datastore.providers.milvus_datastore import ( + OUTPUT_DIM, + MilvusDataStore, +) + + +@pytest.fixture +def milvus_datastore(): + return MilvusDataStore(consistency_level = "Strong") + + +def sample_embedding(one_element_poz: int): + embedding = [0] * OUTPUT_DIM + embedding[one_element_poz % OUTPUT_DIM] = 1 + return embedding + +def sample_embeddings(num: int, one_element_start: int = 0): + # since metric type is consine, we create vector contains only one element 1, others 0 + embeddings = [] + for x in range(num): + embedding = [0] * OUTPUT_DIM + embedding[(x + one_element_start) % OUTPUT_DIM] = 1 + embeddings.append(embedding) + return embeddings + +@pytest.fixture +def document_chunk_one(): + doc_id = "zerp" + doc_chunks = [] + + ids = ["abc_123", "def_456", "ghi_789"] + texts = [ + "lorem ipsum dolor sit amet", + "consectetur adipiscing elit", + "sed do eiusmod tempor incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "1929-10-28T09:30:00-05:00", + "2009-01-03T16:39:57-08:00", + "2021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + + embeddings = sample_embeddings(len(texts)) + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks.append(chunk) + + return {doc_id: doc_chunks} + + +@pytest.fixture +def document_chunk_two(): + doc_id_1 = "zerp" + doc_chunks_1 = [] + + ids = ["abc_123", "def_456", "ghi_789"] + texts = [ + "1lorem ipsum dolor sit amet", + "2consectetur adipiscing elit", + "3sed do eiusmod tempor incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "1929-10-28T09:30:00-05:00", + "2009-01-03T16:39:57-08:00", + "3021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + embeddings = sample_embeddings(len(texts)) + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id_1, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks_1.append(chunk) + + doc_id_2 = "merp" + doc_chunks_2 = [] + + ids = ["jkl_123", "lmn_456", "opq_789"] + texts = [ + "3sdsc efac feas sit qweas", + "4wert sdfas fdsc", + "52dsc fdsf eiusmod asdasd incididunt", + ] + sources = [Source.email, Source.file, Source.chat] + source_ids = ["foo", "bar", "baz"] + urls = ["foo.com", "bar.net", "baz.org"] + created_ats = [ + "4929-10-28T09:30:00-05:00", + "5009-01-03T16:39:57-08:00", + "6021-01-21T10:00:00-02:00", + ] + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + embeddings = sample_embeddings(len(texts), 3) + + for i in range(3): + chunk = DocumentChunk( + id=ids[i], + text=texts[i], + metadata=DocumentChunkMetadata( + document_id=doc_id_2, + source=sources[i], + source_id=source_ids[i], + url=urls[i], + created_at=created_ats[i], + author=authors[i], + ), + embedding=embeddings[i], # type: ignore + ) + + doc_chunks_2.append(chunk) + + return {doc_id_1: doc_chunks_1, doc_id_2: doc_chunks_2} + + +@pytest.mark.asyncio +async def test_upsert(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + assert 3 == milvus_datastore.col.num_entities + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_reload(milvus_datastore, document_chunk_one, document_chunk_two): + await milvus_datastore.delete(delete_all=True) + + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + assert 3 == milvus_datastore.col.num_entities + + new_store = MilvusDataStore() + another_in = {i: document_chunk_two[i] for i in document_chunk_two if i != res[0]} + res = await new_store._upsert(another_in) + new_store.col.flush() + assert 6 == new_store.col.num_entities + query = QueryWithEmbedding( + query="lorem", + top_k=10, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + assert 1 == len(query_results) + new_store.col.drop() + + +@pytest.mark.asyncio +async def test_upsert_query_all(milvus_datastore, document_chunk_two): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_two) + assert res == list(document_chunk_two.keys()) + milvus_datastore.col.flush() + + # Num entities currently doesn't track deletes + query = QueryWithEmbedding( + query="lorem", + top_k=10, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 6 == len(query_results[0].results) + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_query_accuracy(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + query = QueryWithEmbedding( + query="lorem", + top_k=1, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert 1.0 == query_results[0].results[0].score + assert "abc_123" == query_results[0].results[0].id + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_query_filter(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + query = QueryWithEmbedding( + query="lorem", + top_k=1, + embedding=sample_embedding(0), + filter=DocumentMetadataFilter( + start_date="2000-01-03T16:39:57-08:00", end_date="2010-01-03T16:39:57-08:00" + ), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert 1.0 != query_results[0].results[0].score + assert "def_456" == query_results[0].results[0].id + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_delete_with_date_filter(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + await milvus_datastore.delete( + filter=DocumentMetadataFilter( + end_date="2009-01-03T16:39:57-08:00", + ) + ) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 1 == len(query_results[0].results) + assert "ghi_789" == query_results[0].results[0].id + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_delete_with_source_filter(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + await milvus_datastore.delete( + filter=DocumentMetadataFilter( + source=Source.email, + ) + ) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 2 == len(query_results[0].results) + assert "def_456" == query_results[0].results[0].id + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_delete_with_document_id_filter(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + await milvus_datastore.delete( + filter=DocumentMetadataFilter( + document_id=res[0], + ) + ) + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 0 == len(query_results[0].results) + milvus_datastore.col.drop() + + +@pytest.mark.asyncio +async def test_delete_with_document_id(milvus_datastore, document_chunk_one): + await milvus_datastore.delete(delete_all=True) + res = await milvus_datastore._upsert(document_chunk_one) + assert res == list(document_chunk_one.keys()) + milvus_datastore.col.flush() + await milvus_datastore.delete([res[0]]) + + query = QueryWithEmbedding( + query="lorem", + top_k=9, + embedding=sample_embedding(0), + ) + query_results = await milvus_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 0 == len(query_results[0].results) + milvus_datastore.col.drop() + + +# if __name__ == '__main__': +# import sys +# import pytest +# pytest.main(sys.argv) diff --git a/retrieval/tests/datastore/providers/postgres/test_postgres_datastore.py b/retrieval/tests/datastore/providers/postgres/test_postgres_datastore.py new file mode 100644 index 0000000..1e4f0a8 --- /dev/null +++ b/retrieval/tests/datastore/providers/postgres/test_postgres_datastore.py @@ -0,0 +1,291 @@ +from typing import Dict, List +import pytest +from datastore.providers.postgres_datastore import PostgresDataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentMetadataFilter, + QueryWithEmbedding, +) + + +def create_embedding(non_zero_pos: int) -> List[float]: + # create a vector with a single non-zero value of dimension 1535 + vector = [0.0] * 1536 + vector[non_zero_pos - 1] = 1.0 + return vector + + +@pytest.fixture +def initial_document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc-{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata(), + embedding=create_embedding(i), + ) + for i in range(4, 7) + ] + return { + "first-doc": first_doc_chunks, + } + + +@pytest.fixture +def queries() -> List[QueryWithEmbedding]: + queries = [ + QueryWithEmbedding( + query="Query 1", + top_k=1, + embedding=create_embedding(4), + ), + QueryWithEmbedding( + query="Query 2", + top_k=2, + embedding=create_embedding(5), + ), + ] + return queries + + +@pytest.fixture +def postgres_datastore() -> PostgresDataStore: + return PostgresDataStore() + + +@pytest.mark.asyncio +async def test_upsert( + postgres_datastore: PostgresDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], +) -> None: + """Test basic upsert.""" + doc_ids = await postgres_datastore._upsert(initial_document_chunks) + assert doc_ids == [doc_id for doc_id in initial_document_chunks] + + +@pytest.mark.asyncio +async def test_query( + postgres_datastore: PostgresDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], + queries: List[QueryWithEmbedding], +) -> None: + """Test basic query.""" + # insert to prepare for test + await postgres_datastore._upsert(initial_document_chunks) + + query_results = await postgres_datastore._query(queries) + assert len(query_results) == len(queries) + + query_0_results = query_results[0].results + query_1_results = query_results[1].results + + assert len(query_0_results) == 1 + assert len(query_1_results) == 2 + + # NOTE: this is the correct behavior + assert query_0_results[0].id == "first-doc-4" + assert query_1_results[0].id == "first-doc-5" + assert query_1_results[1].id == "first-doc-4" + + +@pytest.mark.asyncio +async def test_delete( + postgres_datastore: PostgresDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], +) -> None: + # insert to prepare for test + await postgres_datastore._upsert(initial_document_chunks) + + is_success = await postgres_datastore.delete(["first-doc"]) + assert is_success + + +@pytest.mark.asyncio +async def test_upsert_new_chunk(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await postgres_datastore._upsert({"doc1": [chunk]}) + assert len(ids) == 1 + + +@pytest.mark.asyncio +async def test_upsert_existing_chunk(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await postgres_datastore._upsert({"doc1": [chunk]}) + + chunk = DocumentChunk( + id="chunk1", + text="New text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await postgres_datastore._upsert({"doc1": [chunk]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + top_k=1, + ) + results = await postgres_datastore._query([query]) + + assert len(ids) == 1 + assert len(results[0].results) == 1 + assert results[0].results[0].id == "chunk1" + assert results[0].results[0].text == "New text" + + +@pytest.mark.asyncio +async def test_query_score(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[-1 if i % 2 == 0 else 1 for i in range(1536)], + metadata=DocumentChunkMetadata(), + ) + await postgres_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + ) + results = await postgres_datastore._query([query]) + + assert results[0].results[0].id == "chunk1" + assert int(results[0].results[0].score) == 1536 + + +@pytest.mark.asyncio +async def test_query_filter(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata( + source="email", created_at="2021-01-01", author="John" + ), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata( + source="chat", created_at="2022-02-02", author="Mike" + ), + ) + await postgres_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + # Test author filter -- string + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(author="John"), + ) + results = await postgres_datastore._query([query]) + assert results[0].results[0].id == "chunk1" + + # Test source filter -- enum + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(source="chat"), + ) + results = await postgres_datastore._query([query]) + assert results[0].results[0].id == "chunk2" + + # Test created_at filter -- date + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(start_date="2022-01-01"), + ) + results = await postgres_datastore._query([query]) + assert results[0].results[0].id == "chunk2" + + +@pytest.mark.asyncio +async def test_delete(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + await postgres_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Another query", + embedding=query_embedding, + ) + results = await postgres_datastore._query([query]) + + assert len(results[0].results) == 2 + assert results[0].results[0].id == "chunk1" + assert results[0].results[1].id == "chunk2" + + await postgres_datastore.delete(ids=["doc1"]) + results_after_delete = await postgres_datastore._query([query]) + + assert len(results_after_delete[0].results) == 1 + assert results_after_delete[0].results[0].id == "chunk2" + + +@pytest.mark.asyncio +async def test_delete_all(postgres_datastore): + await postgres_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + await postgres_datastore._upsert({"doc": [chunk]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Another query", + embedding=query_embedding, + top_k=1, + ) + results = await postgres_datastore._query([query]) + + assert len(results) == 1 + assert len(results[0].results) == 1 + assert results[0].results[0].id == "chunk" + + await postgres_datastore.delete(delete_all=True) + results_after_delete = await postgres_datastore._query([query]) + + assert len(results_after_delete[0].results) == 0 diff --git a/retrieval/tests/datastore/providers/qdrant/test_qdrant_datastore.py b/retrieval/tests/datastore/providers/qdrant/test_qdrant_datastore.py new file mode 100644 index 0000000..850e0a9 --- /dev/null +++ b/retrieval/tests/datastore/providers/qdrant/test_qdrant_datastore.py @@ -0,0 +1,280 @@ +from typing import Dict, List + +import pytest +import qdrant_client +from qdrant_client.http.models import PayloadSchemaType + +from datastore.providers.qdrant_datastore import QdrantDataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + QueryWithEmbedding, + DocumentMetadataFilter, + Source, +) + + +def create_embedding(non_zero_pos: int, size: int) -> List[float]: + vector = [0.0] * size + vector[non_zero_pos % size] = 1.0 + return vector + + +@pytest.fixture +def qdrant_datastore() -> QdrantDataStore: + return QdrantDataStore( + collection_name="documents", vector_size=5, recreate_collection=True + ) + + +@pytest.fixture +def client() -> qdrant_client.QdrantClient: + return qdrant_client.QdrantClient() + + +@pytest.fixture +def initial_document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc-{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata(), + embedding=create_embedding(i, 5), + ) + for i in range(4, 7) + ] + return { + "first-doc": first_doc_chunks, + } + + +@pytest.fixture +def document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc_{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata( + source=Source.email, created_at="2023-03-05", document_id="first-doc" + ), + embedding=create_embedding(i, 5), + ) + for i in range(3) + ] + second_doc_chunks = [ + DocumentChunk( + id=f"second-doc_{i}", + text=f"Dolor sit amet {i}", + metadata=DocumentChunkMetadata( + created_at="2023-03-04", document_id="second-doc" + ), + embedding=create_embedding(i + len(first_doc_chunks), 5), + ) + for i in range(2) + ] + return { + "first-doc": first_doc_chunks, + "second-doc": second_doc_chunks, + } + + +@pytest.mark.asyncio +async def test_datastore_creates_payload_indexes( + qdrant_datastore, + client, +): + collection_info = client.get_collection(collection_name="documents") + + assert 2 == len(collection_info.payload_schema) + assert "created_at" in collection_info.payload_schema + created_at = collection_info.payload_schema["created_at"] + assert PayloadSchemaType.INTEGER == created_at.data_type + assert "metadata.document_id" in collection_info.payload_schema + document_id = collection_info.payload_schema["metadata.document_id"] + assert PayloadSchemaType.KEYWORD == document_id.data_type + + +@pytest.mark.asyncio +async def test_upsert_creates_all_points( + qdrant_datastore, + client, + document_chunks, +): + document_ids = await qdrant_datastore._upsert(document_chunks) + + assert 2 == len(document_ids) + assert 5 == client.count(collection_name="documents").count + + +@pytest.mark.asyncio +async def test_upsert_does_not_remove_existing_documents_but_store_new( + qdrant_datastore, + client, + initial_document_chunks, + document_chunks, +): + """ + This test ensures calling ._upsert no longer removes the existing document chunks, + as they are currently removed in the .upsert method directly. + """ + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(initial_document_chunks) + + await qdrant_datastore._upsert(document_chunks) + + assert 8 == client.count(collection_name="documents").count + + +@pytest.mark.asyncio +async def test_query_returns_all_on_single_query(qdrant_datastore, document_chunks): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + query = QueryWithEmbedding( + query="lorem", + top_k=5, + embedding=[0.5, 0.5, 0.5, 0.5, 0.5], + ) + query_results = await qdrant_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert "lorem" == query_results[0].query + assert 5 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_query_returns_closest_entry(qdrant_datastore, document_chunks): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + query = QueryWithEmbedding( + query="ipsum", + top_k=1, + embedding=[0.0, 0.0, 0.5, 0.0, 0.0], + ) + query_results = await qdrant_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert "ipsum" == query_results[0].query + assert 1 == len(query_results[0].results) + first_document_chunk = query_results[0].results[0] + assert 0.0 <= first_document_chunk.score <= 1.0 + assert Source.email == first_document_chunk.metadata.source + assert "2023-03-05" == first_document_chunk.metadata.created_at + assert "first-doc" == first_document_chunk.metadata.document_id + + +@pytest.mark.asyncio +async def test_query_filter_by_document_id_returns_this_document_chunks( + qdrant_datastore, document_chunks +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + first_query = QueryWithEmbedding( + query="dolor", + filter=DocumentMetadataFilter(document_id="first-doc"), + top_k=5, + embedding=[0.0, 0.0, 0.5, 0.0, 0.0], + ) + second_query = QueryWithEmbedding( + query="dolor", + filter=DocumentMetadataFilter(document_id="second-doc"), + top_k=5, + embedding=[0.0, 0.0, 0.5, 0.0, 0.0], + ) + query_results = await qdrant_datastore._query(queries=[first_query, second_query]) + + assert 2 == len(query_results) + assert "dolor" == query_results[0].query + assert "dolor" == query_results[1].query + assert 3 == len(query_results[0].results) + assert 2 == len(query_results[1].results) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("start_date", ["2023-03-05T00:00:00", "2023-03-05"]) +async def test_query_start_date_converts_datestring( + qdrant_datastore, + document_chunks, + start_date, +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + query = QueryWithEmbedding( + query="sit amet", + filter=DocumentMetadataFilter(start_date=start_date), + top_k=5, + embedding=[0.0, 0.0, 0.5, 0.0, 0.0], + ) + query_results = await qdrant_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 3 == len(query_results[0].results) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("end_date", ["2023-03-04T00:00:00", "2023-03-04"]) +async def test_query_end_date_converts_datestring( + qdrant_datastore, + document_chunks, + end_date, +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + query = QueryWithEmbedding( + query="sit amet", + filter=DocumentMetadataFilter(end_date=end_date), + top_k=5, + embedding=[0.0, 0.0, 0.5, 0.0, 0.0], + ) + query_results = await qdrant_datastore._query(queries=[query]) + + assert 1 == len(query_results) + assert 2 == len(query_results[0].results) + + +@pytest.mark.asyncio +async def test_delete_removes_by_ids( + qdrant_datastore, + client, + document_chunks, +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + await qdrant_datastore.delete(ids=["first-doc"]) + + assert 2 == client.count(collection_name="documents").count + + +@pytest.mark.asyncio +async def test_delete_removes_by_document_id_filter( + qdrant_datastore, + client, + document_chunks, +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + await qdrant_datastore.delete( + filter=DocumentMetadataFilter(document_id="first-doc") + ) + + assert 2 == client.count(collection_name="documents").count + + +@pytest.mark.asyncio +async def test_delete_removes_all( + qdrant_datastore, + client, + document_chunks, +): + # Fill the database with document chunks before running the actual test + await qdrant_datastore._upsert(document_chunks) + + await qdrant_datastore.delete(delete_all=True) + + assert 0 == client.count(collection_name="documents").count diff --git a/retrieval/tests/datastore/providers/redis/test_redis_datastore.py b/retrieval/tests/datastore/providers/redis/test_redis_datastore.py new file mode 100644 index 0000000..6d89988 --- /dev/null +++ b/retrieval/tests/datastore/providers/redis/test_redis_datastore.py @@ -0,0 +1,64 @@ +from datastore.providers.redis_datastore import RedisDataStore +from models.models import DocumentChunk, DocumentChunkMetadata, QueryWithEmbedding, Source, DocumentMetadataFilter +import pytest +import redis.asyncio as redis +import numpy as np + +NUM_TEST_DOCS = 10 + +@pytest.fixture +async def redis_datastore(): + return await RedisDataStore.init(dim=5) + +def create_embedding(i, dim): + vec = np.array([0.1] * dim).astype(np.float64).tolist() + vec[dim-1] = i+1/10 + return vec + +def create_document_chunk(i, dim): + return DocumentChunk( + id=f"first-doc_{i}", + text=f"Lorem ipsum {i}", + embedding=create_embedding(i, dim), + metadata=DocumentChunkMetadata( + source=Source.file, created_at="1970-01-01", document_id="docs" + ), + ) + +def create_document_chunks(n, dim): + docs = [create_document_chunk(i, dim) for i in range(n)] + return {"docs": docs} + +@pytest.mark.asyncio +async def test_redis_upsert_query(redis_datastore): + docs = create_document_chunks(NUM_TEST_DOCS, 5) + await redis_datastore._upsert(docs) + query = QueryWithEmbedding( + query="Lorem ipsum 0", + top_k=5, + embedding= create_embedding(0, 5), + ) + query_results = await redis_datastore._query(queries=[query]) + assert 1 == len(query_results) + for i in range(5): + assert f"Lorem ipsum {i}" == query_results[0].results[i].text + assert "docs" == query_results[0].results[i].id + +@pytest.mark.asyncio +async def test_redis_filter_query(redis_datastore): + query = QueryWithEmbedding( + query="Lorem ipsum 0", + filter=DocumentMetadataFilter(document_id="docs"), + top_k=5, + embedding= create_embedding(0, 5), + ) + query_results = await redis_datastore._query(queries=[query]) + print(query_results) + assert 1 == len(query_results) + assert "docs" == query_results[0].results[0].id + + +@pytest.mark.asyncio +async def test_redis_delete_docs(redis_datastore): + res = await redis_datastore.delete(ids=["docs"]) + assert res diff --git a/retrieval/tests/datastore/providers/supabase/test_supabase_datastore.py b/retrieval/tests/datastore/providers/supabase/test_supabase_datastore.py new file mode 100644 index 0000000..0fff425 --- /dev/null +++ b/retrieval/tests/datastore/providers/supabase/test_supabase_datastore.py @@ -0,0 +1,291 @@ +from typing import Dict, List +import pytest +from datastore.providers.supabase_datastore import SupabaseDataStore +from models.models import ( + DocumentChunk, + DocumentChunkMetadata, + DocumentMetadataFilter, + QueryWithEmbedding, +) + + +def create_embedding(non_zero_pos: int) -> List[float]: + # create a vector with a single non-zero value of dimension 1535 + vector = [0.0] * 1536 + vector[non_zero_pos - 1] = 1.0 + return vector + + +@pytest.fixture +def initial_document_chunks() -> Dict[str, List[DocumentChunk]]: + first_doc_chunks = [ + DocumentChunk( + id=f"first-doc-{i}", + text=f"Lorem ipsum {i}", + metadata=DocumentChunkMetadata(), + embedding=create_embedding(i), + ) + for i in range(4, 7) + ] + return { + "first-doc": first_doc_chunks, + } + + +@pytest.fixture +def queries() -> List[QueryWithEmbedding]: + queries = [ + QueryWithEmbedding( + query="Query 1", + top_k=1, + embedding=create_embedding(4), + ), + QueryWithEmbedding( + query="Query 2", + top_k=2, + embedding=create_embedding(5), + ), + ] + return queries + + +@pytest.fixture +def supabase_datastore() -> SupabaseDataStore: + return SupabaseDataStore() + + +@pytest.mark.asyncio +async def test_upsert( + supabase_datastore: SupabaseDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], +) -> None: + """Test basic upsert.""" + doc_ids = await supabase_datastore._upsert(initial_document_chunks) + assert doc_ids == [doc_id for doc_id in initial_document_chunks] + + +@pytest.mark.asyncio +async def test_query( + supabase_datastore: SupabaseDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], + queries: List[QueryWithEmbedding], +) -> None: + """Test basic query.""" + # insert to prepare for test + await supabase_datastore._upsert(initial_document_chunks) + + query_results = await supabase_datastore._query(queries) + assert len(query_results) == len(queries) + + query_0_results = query_results[0].results + query_1_results = query_results[1].results + + assert len(query_0_results) == 1 + assert len(query_1_results) == 2 + + # NOTE: this is the correct behavior + assert query_0_results[0].id == "first-doc-4" + assert query_1_results[0].id == "first-doc-5" + assert query_1_results[1].id == "first-doc-4" + + +@pytest.mark.asyncio +async def test_delete( + supabase_datastore: SupabaseDataStore, + initial_document_chunks: Dict[str, List[DocumentChunk]], +) -> None: + # insert to prepare for test + await supabase_datastore._upsert(initial_document_chunks) + + is_success = await supabase_datastore.delete(["first-doc"]) + assert is_success + + +@pytest.mark.asyncio +async def test_upsert_new_chunk(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await supabase_datastore._upsert({"doc1": [chunk]}) + assert len(ids) == 1 + + +@pytest.mark.asyncio +async def test_upsert_existing_chunk(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await supabase_datastore._upsert({"doc1": [chunk]}) + + chunk = DocumentChunk( + id="chunk1", + text="New text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + ids = await supabase_datastore._upsert({"doc1": [chunk]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + top_k=1, + ) + results = await supabase_datastore._query([query]) + + assert len(ids) == 1 + assert len(results[0].results) == 1 + assert results[0].results[0].id == "chunk1" + assert results[0].results[0].text == "New text" + + +@pytest.mark.asyncio +async def test_query_score(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[-1 if i % 2 == 0 else 1 for i in range(1536)], + metadata=DocumentChunkMetadata(), + ) + await supabase_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + ) + results = await supabase_datastore._query([query]) + + assert results[0].results[0].id == "chunk1" + assert int(results[0].results[0].score) == 1536 + + +@pytest.mark.asyncio +async def test_query_filter(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata( + source="email", created_at="2021-01-01", author="John" + ), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata( + source="chat", created_at="2022-02-02", author="Mike" + ), + ) + await supabase_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + # Test author filter -- string + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(author="John"), + ) + results = await supabase_datastore._query([query]) + assert results[0].results[0].id == "chunk1" + + # Test source filter -- enum + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(source="chat"), + ) + results = await supabase_datastore._query([query]) + assert results[0].results[0].id == "chunk2" + + # Test created_at filter -- date + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Query", + embedding=query_embedding, + filter=DocumentMetadataFilter(start_date="2022-01-01"), + ) + results = await supabase_datastore._query([query]) + assert results[0].results[0].id == "chunk2" + + +@pytest.mark.asyncio +async def test_delete(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk1 = DocumentChunk( + id="chunk1", + text="Sample text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + chunk2 = DocumentChunk( + id="chunk2", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + await supabase_datastore._upsert({"doc1": [chunk1], "doc2": [chunk2]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Another query", + embedding=query_embedding, + ) + results = await supabase_datastore._query([query]) + + assert len(results[0].results) == 2 + assert results[0].results[0].id == "chunk1" + assert results[0].results[1].id == "chunk2" + + await supabase_datastore.delete(ids=["doc1"]) + results_after_delete = await supabase_datastore._query([query]) + + assert len(results_after_delete[0].results) == 1 + assert results_after_delete[0].results[0].id == "chunk2" + + +@pytest.mark.asyncio +async def test_delete_all(supabase_datastore): + await supabase_datastore.delete(delete_all=True) + chunk = DocumentChunk( + id="chunk", + text="Another text", + embedding=[1] * 1536, + metadata=DocumentChunkMetadata(), + ) + await supabase_datastore._upsert({"doc": [chunk]}) + + query_embedding = [1] * 1536 + query = QueryWithEmbedding( + query="Another query", + embedding=query_embedding, + top_k=1, + ) + results = await supabase_datastore._query([query]) + + assert len(results) == 1 + assert len(results[0].results) == 1 + assert results[0].results[0].id == "chunk" + + await supabase_datastore.delete(delete_all=True) + results_after_delete = await supabase_datastore._query([query]) + + assert len(results_after_delete[0].results) == 0 diff --git a/retrieval/tests/datastore/providers/weaviate/docker-compose.yml b/retrieval/tests/datastore/providers/weaviate/docker-compose.yml new file mode 100644 index 0000000..1900aa5 --- /dev/null +++ b/retrieval/tests/datastore/providers/weaviate/docker-compose.yml @@ -0,0 +1,25 @@ +--- +version: '3.4' +services: + weaviate: + command: + - --host + - 0.0.0.0 + - --port + - '8080' + - --scheme + - http + image: semitechnologies/weaviate:1.18.0 + ports: + - 8080:8080 + restart: on-failure:0 + environment: + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + DEFAULT_VECTORIZER_MODULE: 'none' + ENABLE_MODULES: '' + CLUSTER_HOSTNAME: 'node1' + LOG_LEVEL: debug + AUTOSCHEMA_ENABLED: 'false' +... \ No newline at end of file diff --git a/retrieval/tests/datastore/providers/weaviate/test_weaviate_datastore.py b/retrieval/tests/datastore/providers/weaviate/test_weaviate_datastore.py new file mode 100644 index 0000000..300eb48 --- /dev/null +++ b/retrieval/tests/datastore/providers/weaviate/test_weaviate_datastore.py @@ -0,0 +1,538 @@ +import logging +import os + +import pytest +import weaviate +from _pytest.logging import LogCaptureFixture +from fastapi.testclient import TestClient +from loguru import logger +from weaviate import Client + +from datastore.providers.weaviate_datastore import ( + SCHEMA, + WeaviateDataStore, + extract_schema_properties, +) +from models.models import DocumentMetadataFilter, Source +from server.main import app + +BEARER_TOKEN = os.getenv("BEARER_TOKEN") + +client = TestClient(app) +client.headers["Authorization"] = f"Bearer {BEARER_TOKEN}" + + +@pytest.fixture +def weaviate_client(): + host = os.getenv("WEAVIATE_HOST", "http://localhost") + port = os.getenv("WEAVIATE_PORT", "8080") + client = Client(f"{host}:{port}") + + yield client + + client.schema.delete_all() + + +@pytest.fixture +def test_db(weaviate_client, documents): + weaviate_client.schema.delete_all() + weaviate_client.schema.create_class(SCHEMA) + + response = client.post("/upsert", json={"documents": documents}) + + if response.status_code != 200: + raise Exception( + f"Could not upsert to test client.\nStatus Code: {response.status_code}\nResponse:\n{response.json()}" + ) + + yield client + + +@pytest.fixture +def documents(): + documents = [] + + authors = ["Max Mustermann", "John Doe", "Jane Doe"] + texts = [ + "lorem ipsum dolor sit amet", + "consectetur adipiscing elit", + "sed do eiusmod tempor incididunt", + ] + ids = ["abc_123", "def_456", "ghi_789"] + sources = ["chat", "email", "email"] + created_at = [ + "1929-10-28T09:30:00-05:00", + "2009-01-03T16:39:57-08:00", + "2021-01-21T10:00:00-02:00", + ] + + for i in range(3): + documents.append( + { + "id": ids[i], + "text": texts[i], + "metadata": { + "source": sources[i], + "source_id": "5325", + "url": "http://example.com", + "created_at": created_at[i], + "author": authors[i], + }, + } + ) + + no_metadata_doc = { + "id": "jkl_012", + "text": "no metadata", + } + + documents.append(no_metadata_doc) + + partial_metadata_doc = { + "id": "mno_345", + "text": "partial metadata", + "metadata": { + "source": "file", + }, + } + + documents.append(partial_metadata_doc) + + yield documents + + +@pytest.fixture +def caplog(caplog: LogCaptureFixture): + handler_id = logger.add(caplog.handler, format="{message}") + yield caplog + logger.remove(handler_id) + + +@pytest.mark.parametrize( + "document_id", [("abc_123"), ("9a253e0b-d2df-5c2e-be6d-8e9b1f4ae345")] +) +def test_upsert(weaviate_client, document_id): + weaviate_client.schema.delete_all() + weaviate_client.schema.create_class(SCHEMA) + + text = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in ipsum eget dolor malesuada fermentum at ac massa. + Aliquam erat volutpat. Sed eu velit est. Morbi semper quam id urna fringilla lacinia. Vivamus sit amet velit id lorem + pretium molestie. Nulla tincidunt sapien eu nulla consequat, a lacinia justo facilisis. Maecenas euismod urna sapien, + sit amet tincidunt est dapibus ac. Sed in lorem in nunc tincidunt bibendum. Nullam vel urna vitae nulla iaculis rutrum. + Suspendisse varius, massa a dignissim vehicula, urna ligula tincidunt orci, id fringilla velit tellus eu metus. Sed + vestibulum, nisl in malesuada tempor, nisi turpis facilisis nibh, nec dictum velit velit vel ex. Donec euismod, + leo ut sollicitudin tempor, dolor augue blandit nunc, eu lacinia ipsum turpis vitae nulla. Aenean bibendum + tincidunt magna in pulvinar. Sed tincidunt vel nisi ac maximus. + """ + source = "email" + source_id = "5325" + url = "http://example.com" + created_at = "2022-12-16T08:00:00+01:00" + author = "Max Mustermann" + + documents = { + "documents": [ + { + "id": document_id, + "text": text, + "metadata": { + "source": source, + "source_id": source_id, + "url": url, + "created_at": created_at, + "author": author, + }, + } + ] + } + + response = client.post("/upsert", json=documents) + + assert response.status_code == 200 + assert response.json() == {"ids": [document_id]} + + properties = [ + "chunk_id", + "document_id", + "source", + "source_id", + "url", + "created_at", + "author", + ] + + where_filter = { + "path": ["document_id"], + "operator": "Equal", + "valueString": document_id, + } + + weaviate_doc = ( + weaviate_client.query.get("OpenAIDocument", properties) + .with_additional("vector") + .with_where(where_filter) + .with_sort({"path": ["chunk_id"], "order": "asc"}) + .do() + ) + + weaviate_docs = weaviate_doc["data"]["Get"]["OpenAIDocument"] + + assert len(weaviate_docs) == 2 + + for i, weaviate_doc in enumerate(weaviate_docs): + assert weaviate_doc["chunk_id"] == f"{document_id}_{i}" + + assert weaviate_doc["document_id"] == document_id + + assert weaviate_doc["source"] == source + assert weaviate_doc["source_id"] == source_id + assert weaviate_doc["url"] == url + assert weaviate_doc["created_at"] == created_at + assert weaviate_doc["author"] == author + + assert weaviate_doc["_additional"]["vector"] + + +def test_upsert_no_metadata(weaviate_client): + weaviate_client.schema.delete_all() + weaviate_client.schema.create_class(SCHEMA) + + no_metadata_doc = { + "id": "jkl_012", + "text": "no metadata", + } + + metadata_properties = [ + "source", + "source_id", + "url", + "created_at", + "author", + ] + + response = client.post("/upsert", json={"documents": [no_metadata_doc]}) + + assert response.status_code == 200 + + weaviate_doc = weaviate_client.query.get("OpenAIDocument", metadata_properties).do() + + weaviate_doc = weaviate_doc["data"]["Get"]["OpenAIDocument"][0] + + for _, metadata_value in weaviate_doc.items(): + assert metadata_value is None + + +@pytest.mark.parametrize( + "test_document, expected_status_code", + [ + ({"id": "abc_123", "text": "some text"}, 200), + ({"id": "abc_123"}, 422), + ({"text": "some text"}, 200), + ], +) +def test_upsert_invalid_documents(weaviate_client, test_document, expected_status_code): + weaviate_client.schema.delete_all() + weaviate_client.schema.create_class(SCHEMA) + + response = client.post("/upsert", json={"documents": [test_document]}) + + assert response.status_code == expected_status_code + + +@pytest.mark.parametrize( + "query, expected_num_results", + [ + ({"query": "consectetur adipiscing", "top_k": 3}, 3), + ({"query": "consectetur adipiscing elit", "filter": {"source": "email"}}, 2), + ( + { + "query": "sed do eiusmod tempor", + "filter": { + "start_date": "2020-01-01T00:00:00Z", + "end_date": "2022-12-31T00:00:00Z", + }, + }, + 1, + ), + ( + { + "query": "some random query", + "filter": {"start_date": "2009-01-01T00:00:00Z"}, + "top_k": 3, + }, + 2, + ), + ( + { + "query": "another random query", + "filter": {"end_date": "1929-12-31T00:00:00Z"}, + "top_k": 3, + }, + 1, + ), + ], +) +def test_query(test_db, query, expected_num_results): + queries = {"queries": [query]} + + response = client.post("/query", json=queries) + assert response.status_code == 200 + + num_docs = response.json()["results"][0]["results"] + assert len(num_docs) == expected_num_results + + +def test_delete(test_db, weaviate_client, caplog): + caplog.set_level(logging.DEBUG) + + delete_request = {"ids": ["def_456"]} + + response = client.request(method="delete", url="/delete", json=delete_request) + assert response.status_code == 200 + assert response.json()["success"] + assert weaviate_client.data_object.get()["totalResults"] == 4 + + client.request(method="delete", url="/delete", json=delete_request) + assert "Failed to delete" in caplog.text + caplog.clear() + + delete_request = {"filter": {"source": "email"}} + + response = client.request(method="delete", url="/delete", json=delete_request) + assert response.status_code == 200 + assert response.json()["success"] + assert weaviate_client.data_object.get()["totalResults"] == 3 + + client.request(method="delete", url="/delete", json=delete_request) + assert "Failed to delete" in caplog.text + + delete_request = {"delete_all": True} + + response = client.request(method="delete", url="/delete", json=delete_request) + assert response.status_code == 200 + assert response.json()["success"] + assert not weaviate_client.data_object.get()["objects"] + + +def test_build_auth_credentials(monkeypatch): + # Test when WEAVIATE_URL ends with weaviate.network and WEAVIATE_API_KEY is set + with monkeypatch.context() as m: + m.setenv("WEAVIATE_URL", "https://example.weaviate.network") + m.setenv("WEAVIATE_API_KEY", "your_api_key") + auth_credentials = WeaviateDataStore._build_auth_credentials() + assert auth_credentials is not None + assert isinstance(auth_credentials, weaviate.auth.AuthApiKey) + assert auth_credentials.api_key == "your_api_key" + + # Test when WEAVIATE_URL ends with weaviate.network and WEAVIATE_API_KEY is not set + with monkeypatch.context() as m: + m.setenv("WEAVIATE_URL", "https://example.weaviate.network") + m.delenv("WEAVIATE_API_KEY", raising=False) + with pytest.raises( + ValueError, match="WEAVIATE_API_KEY environment variable is not set" + ): + WeaviateDataStore._build_auth_credentials() + + # Test when WEAVIATE_URL does not end with weaviate.network + with monkeypatch.context() as m: + m.setenv("WEAVIATE_URL", "https://example.notweaviate.network") + m.setenv("WEAVIATE_API_KEY", "your_api_key") + auth_credentials = WeaviateDataStore._build_auth_credentials() + assert auth_credentials is None + + # Test when WEAVIATE_URL is not set + with monkeypatch.context() as m: + m.delenv("WEAVIATE_URL", raising=False) + m.setenv("WEAVIATE_API_KEY", "your_api_key") + auth_credentials = WeaviateDataStore._build_auth_credentials() + assert auth_credentials is None + + +def test_extract_schema_properties(): + class_schema = { + "class": "Question", + "description": "Information from a Jeopardy! question", + "properties": [ + { + "dataType": ["text"], + "description": "The question", + "name": "question", + }, + { + "dataType": ["text"], + "description": "The answer", + "name": "answer", + }, + { + "dataType": ["text"], + "description": "The category", + "name": "category", + }, + ], + "vectorizer": "text2vec-openai", + } + results = extract_schema_properties(class_schema) + assert results == {"question", "answer", "category"} + + +def test_reuse_schema(weaviate_client, caplog): + caplog.set_level(logging.DEBUG) + + weaviate_client.schema.delete_all() + + WeaviateDataStore() + assert "Creating index" in caplog.text + + WeaviateDataStore() + assert "Will reuse this schema" in caplog.text + + +def test_build_date_filters(): + filter = DocumentMetadataFilter( + document_id=None, + source=None, + source_id=None, + author=None, + start_date="2020-01-01T00:00:00Z", + end_date="2022-12-31T00:00:00Z", + ) + actual_result = WeaviateDataStore.build_filters(filter) + expected_result = { + "operator": "And", + "operands": [ + { + "path": ["created_at"], + "operator": "GreaterThanEqual", + "valueDate": "2020-01-01T00:00:00Z", + }, + { + "path": ["created_at"], + "operator": "LessThanEqual", + "valueDate": "2022-12-31T00:00:00Z", + }, + ], + } + + assert actual_result == expected_result + + +@pytest.mark.parametrize( + "test_input, expected_result", + [ + ("abc_123", False), + ("b2e4133c-c956-5684-bbf5-584e50ec3647", True), # version 5 + ("f6179953-11d8-4ee0-9af8-e51e00dbf727", True), # version 4 + ("16fe8165-3c08-348f-a015-a8bb31e26b5c", True), # version 3 + ("bda85f97-be72-11ed-9291-00000000000a", False), # version 1 + ], +) +def test_is_valid_weaviate_id(test_input, expected_result): + actual_result = WeaviateDataStore._is_valid_weaviate_id(test_input) + assert actual_result == expected_result + + +def test_upsert_same_docid(test_db, weaviate_client): + def get_doc_by_document_id(document_id): + properties = [ + "chunk_id", + "document_id", + "source", + "source_id", + "url", + "created_at", + "author", + ] + where_filter = { + "path": ["document_id"], + "operator": "Equal", + "valueString": document_id, + } + + results = ( + weaviate_client.query.get("OpenAIDocument", properties) + .with_additional("id") + .with_where(where_filter) + .with_sort({"path": ["chunk_id"], "order": "asc"}) + .do() + ) + + return results["data"]["Get"]["OpenAIDocument"] + + def build_upsert_payload(document): + return {"documents": [document]} + + # upsert a new document + # this is a document that has 2 chunks and + # the source is email + doc_id = "abc_123" + text = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in ipsum eget dolor malesuada fermentum at ac massa. + Aliquam erat volutpat. Sed eu velit est. Morbi semper quam id urna fringilla lacinia. Vivamus sit amet velit id lorem + pretium molestie. Nulla tincidunt sapien eu nulla consequat, a lacinia justo facilisis. Maecenas euismod urna sapien, + sit amet tincidunt est dapibus ac. Sed in lorem in nunc tincidunt bibendum. Nullam vel urna vitae nulla iaculis rutrum. + Suspendisse varius, massa a dignissim vehicula, urna ligula tincidunt orci, id fringilla velit tellus eu metus. Sed + vestibulum, nisl in malesuada tempor, nisi turpis facilisis nibh, nec dictum velit velit vel ex. Donec euismod, + leo ut sollicitudin tempor, dolor augue blandit nunc, eu lacinia ipsum turpis vitae nulla. Aenean bibendum + tincidunt magna in pulvinar. Sed tincidunt vel nisi ac maximus. + """ + + document = { + "id": doc_id, + "text": text, + "metadata": {"source": Source.email}, + } + + response = client.post("/upsert", json=build_upsert_payload(document)) + assert response.status_code == 200 + + weaviate_doc = get_doc_by_document_id(doc_id) + assert len(weaviate_doc) == 2 + for chunk in weaviate_doc: + assert chunk["source"] == Source.email + + # now update the source to file + # user still has to specify the text + # because test is a required field + document["metadata"]["source"] = Source.file + response = client.post("/upsert", json=build_upsert_payload(document)) + assert response.status_code == 200 + + weaviate_doc = get_doc_by_document_id(doc_id) + assert len(weaviate_doc) == 2 + for chunk in weaviate_doc: + assert chunk["source"] == "file" + + # now update the text so that it is only 1 chunk + # user does not need to specify metadata + # since it is optional + document["text"] = "This is a short text" + document.pop("metadata") + + response = client.post("/upsert", json=build_upsert_payload(document)) + assert response.status_code == 200 + weaviate_doc = get_doc_by_document_id(doc_id) + assert len(weaviate_doc) == 1 + + # TODO: Implement update function + # but the source should still be file + # but it is None right now because an + # update function is out of scope + assert weaviate_doc[0]["source"] is None + + +@pytest.mark.parametrize( + "url, expected_result", + [ + ("https://example.weaviate.network", True), + ("https://example.weaviate.network/", True), + ("https://example.weaviate.cloud", True), + ("https://example.weaviate.cloud/", True), + ("https://example.notweaviate.network", False), + ("https://weaviate.network.example.com", False), + ("https://example.weaviate.network/somepage", False), + ("", False), + ], +) +def test_is_wcs_domain(url, expected_result): + assert WeaviateDataStore._is_wcs_domain(url) == expected_result diff --git a/retrieval/tests/datastore/providers/zilliz/test_zilliz_datastore.py b/retrieval/tests/datastore/providers/zilliz/test_zilliz_datastore.py new file mode 100644 index 0000000..f790797 --- /dev/null +++ b/retrieval/tests/datastore/providers/zilliz/test_zilliz_datastore.py @@ -0,0 +1,29 @@ +# from pathlib import Path +# from dotenv import find_dotenv, load_dotenv +# env_path = Path(".") / "zilliz.env" +# load_dotenv(dotenv_path=env_path, verbose=True) + +import pytest + +from datastore.providers.zilliz_datastore import ( + ZillizDataStore, +) + +from datastore.providers.milvus_datastore import ( + EMBEDDING_FIELD, +) + +# Note: Only do basic test here, the ZillizDataStore is derived from MilvusDataStore. + +@pytest.fixture +def zilliz_datastore(): + return ZillizDataStore() + + +@pytest.mark.asyncio +async def test_zilliz(zilliz_datastore): + assert True == zilliz_datastore.col.has_index() + index_list = [x.to_dict() for x in zilliz_datastore.col.indexes] + for index in index_list: + if index['index_name'] == EMBEDDING_FIELD: + assert 'AUTOINDEX' == index['index_param']['index_type'] \ No newline at end of file diff --git a/retrieval/xtras/chat_utils.py b/retrieval/xtras/chat_utils.py new file mode 100644 index 0000000..5fdb459 --- /dev/null +++ b/retrieval/xtras/chat_utils.py @@ -0,0 +1,80 @@ +from typing import Any, List, Dict +import openai +import requests +import os +from secrets import DATABASE_INTERFACE_BEAR_TOKEN +from secrets import OPENAI_API_KEY +import logging + + +def query_database(query_prompt: str) -> Dict[str, Any]: + """ + Query vector database to retrieve chunk with user's input questions. + """ + url = "http://0.0.0.0:8000/query" + headers = { + "Content-Type": "application/json", + "accept": "application/json", + "Authorization": f"Bearer {DATABASE_INTERFACE_BEAR_TOKEN}", + } + data = {"queries": [{"query": query_prompt, "top_k": 5}]} + + response = requests.post(url, json=data, headers=headers) + + if response.status_code == 200: + result = response.json() + # process the result + return result + else: + raise ValueError(f"Error: {response.status_code} : {response.content}") + + +def apply_prompt_template(question: str) -> str: + """ + A helper function that applies additional template on user's question. + Prompt engineering could be done here to improve the result. Here I will just use a minimal example. + """ + prompt = f""" + By considering above input from me, answer the question: {question} + """ + return prompt + +def call_chatgpt_api(user_question: str, chunks: List[str]) -> Dict[str, Any]: + """ + Call chatgpt api with user's question and retrieved chunks. + """ + # Send a request to the GPT-3 API + messages = list( + map(lambda chunk: { + "role": "user", + "content": chunk + }, chunks)) + question = apply_prompt_template(user_question) + messages.append({"role": "user", "content": question}) + response = openai.ChatCompletion.create( + model=os.environ.get('GPT_MODEL') or "gpt-3.5-turbo", + messages=messages, + max_tokens=1024, + temperature=0.7, # High temperature leads to a more creative response. + ) + return response + + +def ask(user_question: str) -> Dict[str, Any]: + """ + Handle user's questions. + """ + # Get chunks from database. + chunks_response = query_database(user_question) + chunks = [] + for result in chunks_response["results"]: + for inner_result in result["results"]: + chunks.append(inner_result["text"]) + + logging.info("User's questions: %s", user_question) + logging.info("Retrieved chunks: %s", chunks) + + response = call_chatgpt_api(user_question, chunks) + logging.info("Response: %s", response) + + return response["choices"][0]["message"]["content"] \ No newline at end of file diff --git a/retrieval/xtras/database_utils.py b/retrieval/xtras/database_utils.py new file mode 100644 index 0000000..32be187 --- /dev/null +++ b/retrieval/xtras/database_utils.py @@ -0,0 +1,97 @@ +from typing import Any, Dict +import requests +import os +import pathlib +from secrets import DATABASE_INTERFACE_BEARER_TOKEN +from main import project_home +import igittigitt +parser = igittigitt.IgnoreParser() +project_path = pathlib.Path(project_home) + +parser.parse_rule_file(project_path.join('.gitignore')) + + +SEARCH_TOP_K = 3 + +def should_ignore(file_path: str): + """ + Determine if file path should be ignored + """ + parser.match(project_path.join(file_path)) + # False + +def upsert_file(directory: str): + """ + Upload all files under a directory to the vector database. + """ + url = "http://0.0.0.0:8000/upsert-file" + headers = {"Authorization": "Bearer " + DATABASE_INTERFACE_BEARER_TOKEN} + files = [] + for filename in os.listdir(directory): + if os.path.isfile(os.path.join(directory, filename)): + file_path = os.path.join(directory, filename) + + if should_ignore(file_path): + continue + + with open(file_path, "rb") as f: + file_content = f.read() + files.append(("file", (filename, file_content, "text/plain"))) + response = requests.post(url, + headers=headers, + files=files, + timeout=600) + if response.status_code == 200: + print(filename + " uploaded successfully.") + else: + print( + f"Error: {response.status_code} {response.content} for uploading " + + filename) + + +def upsert(id: str, content: str): + """ + Upload one piece of text to the database. + """ + url = "http://0.0.0.0:8000/upsert" + headers = { + "accept": "application/json", + "Content-Type": "application/json", + "Authorization": "Bearer " + DATABASE_INTERFACE_BEARER_TOKEN, + } + + data = { + "documents": [{ + "id": id, + "text": content, + }] + } + response = requests.post(url, json=data, headers=headers, timeout=600) + + if response.status_code == 200: + print("uploaded successfully.") + else: + print(f"Error: {response.status_code} {response.content}") + + +def query_database(query_prompt: str) -> Dict[str, Any]: + """ + Query vector database to retrieve chunk with user's input question. + """ + url = "http://0.0.0.0:8000/query" + headers = { + "Content-Type": "application/json", + "accept": "application/json", + "Authorization": f"Bearer {DATABASE_INTERFACE_BEARER_TOKEN}", + } + data = {"queries": [{"query": query_prompt, "top_k": SEARCH_TOP_K}]} + + response = requests.post(url, json=data, headers=headers, timeout=600) + + if response.status_code == 200: + result = response.json() + # process the result + return result + else: + raise ValueError(f"Error: {response.status_code} : {response.content}") + \ No newline at end of file diff --git a/retrieval/xtras/main.py b/retrieval/xtras/main.py new file mode 100644 index 0000000..7ad1d3f --- /dev/null +++ b/retrieval/xtras/main.py @@ -0,0 +1,17 @@ +import logging +import openai +from chat_utils import ask +from database_utils import upsert_file +from secrets import OPENAI_API_KEY + +project_home = '.' + +if __name__ == "__main__": + project_home = input("Enter your project directory: ") + upsert_file(project_directory) + while True: + user_query = input("Enter your question: ") + openai.api_key = OPENAI_API_KEY + logging.basicConfig(level=logging.WARNING, + format="%(asctime)s %(levelname)s %(message)s") + print(ask(user_query)) \ No newline at end of file diff --git a/retrieval/xtras/secrets.py b/retrieval/xtras/secrets.py new file mode 100644 index 0000000..49eebeb --- /dev/null +++ b/retrieval/xtras/secrets.py @@ -0,0 +1,2 @@ +OPENAI_API_KEY = "" +DATABASE_INTERFACE_BEARER_TOKEN = "" \ No newline at end of file diff --git a/src/file-agent.js b/src/file-agent.js new file mode 100644 index 0000000..ef1471b --- /dev/null +++ b/src/file-agent.js @@ -0,0 +1,141 @@ +var ignoringWatcher = require('ignoring-watcher').createWatcher({ + // Directory to watch. Defaults to process.cwd() + dir: __dirname, + + // Watch multiple directories instead of a single dir + dirs: ['some/dir', 'another/dir'], + + // One or more ignore patterns + ignorePatterns: [ + '/node_modules', + ], + + // The ignore patterns from these ignore files will all + // be loaded and joined together + ignoreFiles: [ + '.gitignore', + '.npmignore' + ], + + // Only the first existing ignore file (if any) will be loaded and merged + selectIgnoreFile: [ + '.gitignore', + '.npmignore' + ], + + // If no ignore patterns were found via the other properties + // then these ignore patterns will be used + defaultIgnorePatterns: [ + '.*' + ], + + // The following patterns will always be loaded and not impact + // the loading of the `defaultIgnorePatterns` + ignoreAlwaysPatterns: [ + 'log.*', + '/temp/*' + ] +}); + +const baseApiUrl = process.env.BASE_API_URL || 'http://0.0.0.0:8000'; + +const requestUpsertFile = async (filePath, fileOpts = {}, headers = {}) => { + try { + const file = fs.createReadStream(filePath); + const filePath = filePath; + + const form = new FormData(); + form.append('title', title); + form.append('filePath', filePath); + // iterate opts with extra metadata + + const resp = await axios.post(`${baseApiUrl}/upsert-file`, form, { + headers: { + ...form.getHeaders(), + ...headers || {} + } + }); + + if (resp.status === 200) { + return 'Upload complete'; + } + } catch(err) { + return new Error(err.message); + } +}; + +// ids or request.filter or request.delete_all +const requestDelete = async (filePath, fileOpts = {}, headers = {}) => { + try { + const filePath = filePath; + + const form = new FormData(); + // + // ids: Optional[List[str]] = None + // filter: Optional[DocumentMetadataFilter] = None + // delete_all: Optional[bool] = False + + // DocumentMetadataFilter + // document_id: Optional[str] = None + // source: Optional[Source] = None + // source_id: Optional[str] = None + + form.append('ids', [filePath]); + // form.append('filter', filePath); + // iterate opts with extra metadata + + const resp = await axios.post(`${baseApiUrl}/delete`, form, { + headers: { + ...form.getHeaders(), + ...headers || {} + } + }); + + if (resp.status === 200) { + return 'Delete complete'; + } + } catch(err) { + return new Error(err.message); + } +}; + + + +const accessToken = process.env.DATABASE_INTERFACE_BEARER_TOKEN; + +const headers = { + "Authorization": "Bearer " + accessToken // token +}; + +async function upsertFile (filePath) { + const response = await requestUpsertFile(filePath, {}, headers); + console.log(response); +}; + +async function deleteFile (filePath) { + const response = await requestDelete(filePath, {}, headers); + console.log(response); +}; + + +ignoringWatcher + .on('modified', function(eventArgs) { // Fired for any change event (add, delete, etc.) + var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + var path = eventArgs.path; // The full file system path of the modified file + upsertFile(filePath); + }); +ignoringWatcher + .on('add', function(eventArgs) { // Fired for any change event (add, delete, etc.) + var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + var path = eventArgs.path; // The full file system path of the modified file + upsertFile(filePath); + }); + +ignoringWatcher + .on('unlink', function(eventArgs) { // Fired for any change event (add, delete, etc.) + var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + var path = eventArgs.path; // The full file system path of the modified file + deleteFile(filePath); + }); + +ignoringWatcher.startWatching(); \ No newline at end of file diff --git a/src/langchain.ts b/src/langchain.ts new file mode 100644 index 0000000..e99e841 --- /dev/null +++ b/src/langchain.ts @@ -0,0 +1,17 @@ +import { OpenAI } from "langchain/llms/openai"; +import { Document } from "langchain/document"; +import { TypescriptTextSplitter } from "./typescript_textsplitter"; +import { TextSplitterChunkHeaderOptions } from "langchain/text_splitter"; + +export const createDocsFromCode = async (codeFiles: string[], metadatas?: Record[], chunkHeaderOptions?: TextSplitterChunkHeaderOptions): Promise => { + const typescriptSplitter = new TypescriptTextSplitter().build({ + chunkSize: 60, + chunkOverlap: 20, + ...chunkHeaderOptions || {} + }); + return await typescriptSplitter.createDocuments(codeFiles, metadatas, chunkHeaderOptions); +}; + + + + diff --git a/src/typescript_textsplitter.ts b/src/typescript_textsplitter.ts new file mode 100644 index 0000000..9a19ca0 --- /dev/null +++ b/src/typescript_textsplitter.ts @@ -0,0 +1,36 @@ +import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; + +export class TypescriptTextSplitter { + separators = [ + // typescript + "\type ", + "\ninterface ", + "\namespace ", + // javascript + // Split along function definitions + "\nfunction ", + "\nconst ", + "\nlet ", + "\nvar ", + "\nclass ", + // Split along control flow statements + "\nif ", + "\nfor ", + "\nwhile ", + "\nswitch ", + "\ncase ", + "\ndefault ", + // Split by the normal type of lines + "\n\n", + "\n", + " ", + "", + ]; + + build(opts: any = {}) { + return new RecursiveCharacterTextSplitter({ + ...opts, + separators: this.separators + }); + } + } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2de2904..6004151 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@anthropic-ai/sdk@^0.4.3": + "integrity" "sha512-Z/39nQi1sSUCeLII3lsAbL1u+0JF6cR2XmUEX9sLH0VtxmIjY6cjOUYjCkYh4oapTxOkhAFnVSAFJ6cxml2qXg==" + "resolved" "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.4.4.tgz" + "version" "0.4.4" + dependencies: + "@fortaine/fetch-event-source" "^3.0.6" + "cross-fetch" "^3.1.5" + "@babel/code-frame@^7.0.0": "integrity" "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==" "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz" @@ -60,6 +68,11 @@ "resolved" "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz" "version" "8.42.0" +"@fortaine/fetch-event-source@^3.0.6": + "integrity" "sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw==" + "resolved" "https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz" + "version" "3.0.6" + "@humanwhocodes/config-array@^0.11.10": "integrity" "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==" "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz" @@ -216,6 +229,11 @@ "resolved" "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" "version" "2.4.1" +"@types/retry@0.12.0": + "integrity" "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + "resolved" "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" + "version" "0.12.0" + "@types/semver@^7.3.12": "integrity" "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" "resolved" "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz" @@ -559,6 +577,11 @@ dependencies: "color-convert" "^2.0.1" +"ansi-styles@^5.0.0": + "integrity" "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + "version" "5.2.0" + "ansi-styles@^6.1.0": "integrity" "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" @@ -582,6 +605,11 @@ "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" "version" "2.1.0" +"asynckit@^0.4.0": + "integrity" "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "resolved" "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "version" "0.4.0" + "atomically@^2.0.0": "integrity" "sha512-sxBhVZUFBFhqSAsYMM3X2oaUi2NVDJ8U026FsIusM8gYXls9AYs/eXzgGrufs1Qjpkxi9zunds+75QUFz+m7UQ==" "resolved" "https://registry.npmjs.org/atomically/-/atomically-2.0.1.tgz" @@ -590,6 +618,13 @@ "stubborn-fs" "^1.2.4" "when-exit" "^2.0.0" +"axios@*", "axios@^0.26.0": + "integrity" "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz" + "version" "0.26.1" + dependencies: + "follow-redirects" "^1.14.8" + "balanced-match@^1.0.0": "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -600,11 +635,16 @@ "resolved" "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" "version" "1.5.1" -"binary-extensions@^2.0.0": +"binary-extensions@^2.0.0", "binary-extensions@^2.2.0": "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" "version" "2.2.0" +"binary-search@^1.3.5": + "integrity" "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + "resolved" "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz" + "version" "1.3.6" + "brace-expansion@^1.1.7": "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -766,6 +806,13 @@ "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" "version" "2.0.19" +"combined-stream@^1.0.8": + "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" + "resolved" "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "delayed-stream" "~1.0.0" + "commander@^10.0.1": "integrity" "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" "resolved" "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" @@ -800,6 +847,13 @@ "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" "version" "1.0.3" +"cross-fetch@^3.1.5": + "integrity" "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==" + "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz" + "version" "3.1.6" + dependencies: + "node-fetch" "^2.6.11" + "cross-spawn@^7.0.0", "cross-spawn@^7.0.2", "cross-spawn@^7.0.3": "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -833,6 +887,11 @@ "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" "version" "0.1.4" +"delayed-stream@~1.0.0": + "integrity" "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "version" "1.0.0" + "diff@5.0.0": "integrity" "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" "resolved" "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" @@ -1033,6 +1092,11 @@ "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" "version" "2.0.3" +"eventemitter3@^4.0.4": + "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + "version" "4.0.7" + "events@^3.2.0": "integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" @@ -1043,6 +1107,11 @@ "resolved" "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz" "version" "1.0.0" +"expr-eval@^2.0.2": + "integrity" "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==" + "resolved" "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz" + "version" "2.0.2" + "fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -1137,6 +1206,11 @@ "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" "version" "3.2.7" +"follow-redirects@^1.14.8": + "integrity" "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + "version" "1.15.2" + "foreground-child@^3.1.0": "integrity" "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==" "resolved" "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz" @@ -1145,6 +1219,15 @@ "cross-spawn" "^7.0.0" "signal-exit" "^4.0.1" +"form-data@^4.0.0": + "integrity" "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==" + "resolved" "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "asynckit" "^0.4.0" + "combined-stream" "^1.0.8" + "mime-types" "^2.1.12" + "fs.realpath@^1.0.0": "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -1355,6 +1438,11 @@ "resolved" "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" "version" "3.1.1" +"is-any-array@^2.0.0": + "integrity" "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==" + "resolved" "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz" + "version" "2.0.1" + "is-arrayish@^0.2.1": "integrity" "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" @@ -1451,7 +1539,7 @@ "merge-stream" "^2.0.0" "supports-color" "^8.0.0" -"js-tiktoken@^1.0.5": +"js-tiktoken@^1.0.5", "js-tiktoken@^1.0.6": "integrity" "sha512-lxHntEupgjWvSh37WxpAW4XN6UBXBtFJOpZZq5HN5oNjDfN7L/iJhHOKjyL/DFtuYXUwn5jfTciLtOWpgQmHjQ==" "resolved" "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.6.tgz" "version" "1.0.6" @@ -1500,6 +1588,11 @@ "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" "version" "1.0.1" +"jsonpointer@^5.0.1": + "integrity" "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + "resolved" "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" + "version" "5.0.1" + "jszip@^3.10.1": "integrity" "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==" "resolved" "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" @@ -1522,6 +1615,28 @@ "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" "version" "6.0.3" +"langchain@^0.0.92": + "integrity" "sha512-77528kXJu9C391qV1a5jDzZLEjIblq/VtpH7xd4BwavgvhRmeSXR9WkrHHmih/Phpae4Ss3dtb7pcyd78rp/YA==" + "resolved" "https://registry.npmjs.org/langchain/-/langchain-0.0.92.tgz" + "version" "0.0.92" + dependencies: + "@anthropic-ai/sdk" "^0.4.3" + "ansi-styles" "^5.0.0" + "binary-extensions" "^2.2.0" + "expr-eval" "^2.0.2" + "flat" "^5.0.2" + "js-tiktoken" "^1.0.6" + "jsonpointer" "^5.0.1" + "ml-distance" "^4.0.0" + "object-hash" "^3.0.0" + "openai" "^3.2.0" + "p-queue" "^6.6.2" + "p-retry" "4" + "uuid" "^9.0.0" + "yaml" "^2.2.1" + "zod" "^3.21.4" + "zod-to-json-schema" "^3.20.4" + "levn@^0.4.1": "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" "resolved" "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" @@ -1616,7 +1731,7 @@ "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" "version" "1.52.0" -"mime-types@^2.1.27": +"mime-types@^2.1.12", "mime-types@^2.1.27": "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" "version" "2.1.35" @@ -1661,6 +1776,42 @@ "resolved" "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz" "version" "6.0.2" +"ml-array-mean@^1.1.6": + "integrity" "sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ==" + "resolved" "https://registry.npmjs.org/ml-array-mean/-/ml-array-mean-1.1.6.tgz" + "version" "1.1.6" + dependencies: + "ml-array-sum" "^1.1.6" + +"ml-array-sum@^1.1.6": + "integrity" "sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw==" + "resolved" "https://registry.npmjs.org/ml-array-sum/-/ml-array-sum-1.1.6.tgz" + "version" "1.1.6" + dependencies: + "is-any-array" "^2.0.0" + +"ml-distance-euclidean@^2.0.0": + "integrity" "sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q==" + "resolved" "https://registry.npmjs.org/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz" + "version" "2.0.0" + +"ml-distance@^4.0.0": + "integrity" "sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw==" + "resolved" "https://registry.npmjs.org/ml-distance/-/ml-distance-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "ml-array-mean" "^1.1.6" + "ml-distance-euclidean" "^2.0.0" + "ml-tree-similarity" "^1.0.0" + +"ml-tree-similarity@^1.0.0": + "integrity" "sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==" + "resolved" "https://registry.npmjs.org/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "binary-search" "^1.3.5" + "num-sort" "^2.0.0" + "mocha@^10.2.0": "integrity" "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==" "resolved" "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" @@ -1718,6 +1869,13 @@ "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" "version" "2.6.2" +"node-fetch@^2.6.11": + "integrity" "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz" + "version" "2.6.11" + dependencies: + "whatwg-url" "^5.0.0" + "node-releases@^2.0.6": "integrity" "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" @@ -1738,6 +1896,16 @@ "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" "version" "3.0.0" +"num-sort@^2.0.0": + "integrity" "sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg==" + "resolved" "https://registry.npmjs.org/num-sort/-/num-sort-2.1.0.tgz" + "version" "2.1.0" + +"object-hash@^3.0.0": + "integrity" "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + "resolved" "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + "version" "3.0.0" + "once@^1.3.0": "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -1745,6 +1913,14 @@ dependencies: "wrappy" "1" +"openai@^3.2.0": + "integrity" "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==" + "resolved" "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz" + "version" "3.2.1" + dependencies: + "axios" "^0.26.0" + "form-data" "^4.0.0" + "optionator@^0.9.1": "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" @@ -1757,6 +1933,11 @@ "type-check" "^0.4.0" "word-wrap" "^1.2.3" +"p-finally@^1.0.0": + "integrity" "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + "resolved" "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" + "version" "1.0.0" + "p-limit@^2.2.0": "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -1799,6 +1980,29 @@ dependencies: "p-limit" "^4.0.0" +"p-queue@^6.6.2": + "integrity" "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==" + "resolved" "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz" + "version" "6.6.2" + dependencies: + "eventemitter3" "^4.0.4" + "p-timeout" "^3.2.0" + +"p-retry@4": + "integrity" "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==" + "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" + "version" "4.6.2" + dependencies: + "@types/retry" "0.12.0" + "retry" "^0.13.1" + +"p-timeout@^3.2.0": + "integrity" "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==" + "resolved" "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" + "version" "3.2.0" + dependencies: + "p-finally" "^1.0.0" + "p-timeout@^6.1.1": "integrity" "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==" "resolved" "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz" @@ -2000,6 +2204,11 @@ "path-parse" "^1.0.7" "supports-preserve-symlinks-flag" "^1.0.0" +"retry@^0.13.1": + "integrity" "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "resolved" "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + "version" "0.13.1" + "reusify@^1.0.4": "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" @@ -2273,6 +2482,11 @@ dependencies: "is-number" "^7.0.0" +"tr46@~0.0.3": + "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" + "ts-loader@^9.4.3": "integrity" "sha512-n3hBnm6ozJYzwiwt5YRiJZkzktftRpMiBApHaJPoWLA+qetQBAXkHqCLM6nwSdRDimqVtA5ocIkcTRLMTt7yzA==" "resolved" "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz" @@ -2358,6 +2572,11 @@ "glob-to-regexp" "^0.4.1" "graceful-fs" "^4.1.2" +"webidl-conversions@^3.0.0": + "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" + "webpack-cli@^5.1.4", "webpack-cli@5.x.x": "integrity" "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==" "resolved" "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" @@ -2420,6 +2639,14 @@ "watchpack" "^2.4.0" "webpack-sources" "^3.2.3" +"whatwg-url@^5.0.0": + "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" + "when-exit@^2.0.0": "integrity" "sha512-H85ulNwUBU1e6PGxkWUDgxnbohSXD++ah6Xw1VHAN7CtypcbZaC4aYjQ+C2PMVaDkURDuOinNAT+Lnz3utWXxQ==" "resolved" "https://registry.npmjs.org/when-exit/-/when-exit-2.1.0.tgz" @@ -2489,6 +2716,11 @@ "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" "version" "4.0.0" +"yaml@^2.2.1": + "integrity" "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" + "resolved" "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz" + "version" "2.3.1" + "yargs-parser@^20.2.2", "yargs-parser@20.2.4": "integrity" "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" @@ -2526,3 +2758,13 @@ "integrity" "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" "version" "1.0.0" + +"zod-to-json-schema@^3.20.4": + "integrity" "sha512-y5g0MPxDq+YG/T+cHGPYH4PcBpyCqwK6wxeJ76MR563y0gk/14HKfebq8xHiItY7lkc9GDFygCnkvNDTvAhYAg==" + "resolved" "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.21.1.tgz" + "version" "3.21.1" + +"zod@^3.21.4": + "integrity" "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" + "resolved" "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" + "version" "3.21.4" From f2ea147cfa511e424b7263e9d83b284f652a4970 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Mon, 12 Jun 2023 23:37:32 +0200 Subject: [PATCH 03/15] Update Readme on code splitting --- README.md | 13 +- package-lock.json | 11 + package.json | 1 + retrieval/services/chunks.py | 4 +- retrieval/services/code_chunks.py | 203 ++++++++++++++++++ src/file-agent.js | 17 +- src/langchain.ts | 2 +- ...splitter.ts => typescript-textsplitter.ts} | 0 yarn.lock | 5 + 9 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 retrieval/services/code_chunks.py rename src/{typescript_textsplitter.ts => typescript-textsplitter.ts} (100%) diff --git a/README.md b/README.md index 6d7a879..650c421 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,18 @@ or via script: `npm run file-agent` +## Code chunks + +This extension intends to use LangChain [CodeSplitter](https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html) to split the code files as code. + +A custom TypeScript splitter is included in `src/typescript-textsplitter.js`. It would be easy to add the same for Python. + +The retrieval plugin should be made to use a CodeSplitter for text files and other specific chunk splitters for other files (markdown, text etc) using LangChain. + +A placeholder file `services/code_chunks` is there to work from. + +Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. + ## Using the Extension To use the extension, open a text editor in Visual Studio Code and open the ChatGPT panel by clicking on the ChatGPT icon in the sidebar. This will open a panel with an input field where you can enter your prompt or question. By clicking enter, it will be sent to ChatGPT. Its response will be displayed below the input field in the sidebar (note that it may take some time for it to be calculated). @@ -174,7 +186,6 @@ To **reset the conversation context**, click `ctrl+shift+p` and select `ChatGPT: Please note that this extension is currently a proof of concept and may have some limitations or bugs. We welcome feedback and contributions to improve the extension. - ## Credits - This wouldn't be possible without OpenAI's [ChatGPT](https://chat.openai.com/chat) diff --git a/package-lock.json b/package-lock.json index 537c18a..fb40768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "chatgpt": "^5.2.5", + "detect-programming-language": "^1.0.1", "langchain": "^0.0.92" }, "devDependencies": { @@ -1553,6 +1554,11 @@ "node": ">=0.4.0" } }, + "node_modules/detect-programming-language": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detect-programming-language/-/detect-programming-language-1.0.1.tgz", + "integrity": "sha512-31kFRf56KY0p6FXEdpewh/EDGYi+8WIUFxK7bcimFCktXepYkQ3kPEtFfslbNL0cCs/7+f+TQz03sg5AwkEwXQ==" + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -5929,6 +5935,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "detect-programming-language": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detect-programming-language/-/detect-programming-language-1.0.1.tgz", + "integrity": "sha512-31kFRf56KY0p6FXEdpewh/EDGYi+8WIUFxK7bcimFCktXepYkQ3kPEtFfslbNL0cCs/7+f+TQz03sg5AwkEwXQ==" + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", diff --git a/package.json b/package.json index 802b0eb..d5d6c77 100644 --- a/package.json +++ b/package.json @@ -244,6 +244,7 @@ }, "dependencies": { "chatgpt": "^5.2.5", + "detect-programming-language": "^1.0.1", "langchain": "^0.0.92" } } diff --git a/retrieval/services/chunks.py b/retrieval/services/chunks.py index 19be9b9..fa90039 100644 --- a/retrieval/services/chunks.py +++ b/retrieval/services/chunks.py @@ -13,10 +13,10 @@ ) # The encoding scheme to use for tokenization # Constants -CHUNK_SIZE = 1024 # The target size of each text chunk in tokens +CHUNK_SIZE = 200 # The target size of each text chunk in tokens MIN_CHUNK_SIZE_CHARS = 350 # The minimum size of each text chunk in characters MIN_CHUNK_LENGTH_TO_EMBED = 5 # Discard chunks shorter than this -EMBEDDINGS_BATCH_SIZE = 128 # The number of embeddings to request at a time +EMBEDDINGS_BATCH_SIZE = int(os.environ.get("OPENAI_EMBEDDING_BATCH_SIZE", 128)) # The number of embeddings to request at a time MAX_NUM_CHUNKS = 10000 # The maximum number of chunks to generate from a text def get_text_chunks(text: str, chunk_token_size: Optional[int]) -> List[str]: diff --git a/retrieval/services/code_chunks.py b/retrieval/services/code_chunks.py new file mode 100644 index 0000000..4bdd95a --- /dev/null +++ b/retrieval/services/code_chunks.py @@ -0,0 +1,203 @@ +from typing import Dict, List, Optional, Tuple +import uuid +import os +from models.models import Document, DocumentChunk, DocumentChunkMetadata + +import tiktoken + +from services.openai import get_embeddings + +# Global variables +tokenizer = tiktoken.get_encoding( + "cl100k_base" +) # The encoding scheme to use for tokenization + +# Constants +CHUNK_SIZE = 1024 # The target size of each text chunk in tokens +MIN_CHUNK_SIZE_CHARS = 350 # The minimum size of each text chunk in characters +MIN_CHUNK_LENGTH_TO_EMBED = 5 # Discard chunks shorter than this +EMBEDDINGS_BATCH_SIZE = 128 # The number of embeddings to request at a time +MAX_NUM_CHUNKS = 10000 # The maximum number of chunks to generate from a text + +# TODO: use LangChain CodeSplitter with custom TypeScript splitter +def get_code_chunks(text: str, chunk_token_size: Optional[int]) -> List[str]: + """ + Split a text into chunks of ~CHUNK_SIZE tokens, based on punctuation and newline boundaries. + + Args: + text: The text to split into chunks. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A list of text chunks, each of which is a string of ~CHUNK_SIZE tokens. + """ + # Return an empty list if the text is empty or whitespace + if not text or text.isspace(): + return [] + + # Tokenize the text + tokens = tokenizer.encode(text, disallowed_special=()) + + # Initialize an empty list of chunks + chunks = [] + + # Use the provided chunk token size or the default one + chunk_size = chunk_token_size or CHUNK_SIZE + + # Initialize a counter for the number of chunks + num_chunks = 0 + + # Loop until all tokens are consumed + while tokens and num_chunks < MAX_NUM_CHUNKS: + # Take the first chunk_size tokens as a chunk + chunk = tokens[:chunk_size] + + # Decode the chunk into text + chunk_text = tokenizer.decode(chunk) + + # Skip the chunk if it is empty or whitespace + if not chunk_text or chunk_text.isspace(): + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(chunk) :] + # Continue to the next iteration of the loop + continue + + # Find the last period or punctuation mark in the chunk + last_punctuation = max( + chunk_text.rfind("."), + chunk_text.rfind("?"), + chunk_text.rfind("!"), + chunk_text.rfind("\n"), + ) + + # If there is a punctuation mark, and the last punctuation index is before MIN_CHUNK_SIZE_CHARS + if last_punctuation != -1 and last_punctuation > MIN_CHUNK_SIZE_CHARS: + # Truncate the chunk text at the punctuation mark + chunk_text = chunk_text[: last_punctuation + 1] + + # Remove any newline characters and strip any leading or trailing whitespace + chunk_text_to_append = chunk_text.replace("\n", " ").strip() + + if len(chunk_text_to_append) > MIN_CHUNK_LENGTH_TO_EMBED: + # Append the chunk text to the list of chunks + chunks.append(chunk_text_to_append) + + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(tokenizer.encode(chunk_text, disallowed_special=())) :] + + # Increment the number of chunks + num_chunks += 1 + + # Handle the remaining tokens + if tokens: + remaining_text = tokenizer.decode(tokens).replace("\n", " ").strip() + if len(remaining_text) > MIN_CHUNK_LENGTH_TO_EMBED: + chunks.append(remaining_text) + + return chunks + + +def create_document_chunks( + doc: Document, chunk_token_size: Optional[int] +) -> Tuple[List[DocumentChunk], str]: + """ + Create a list of document chunks from a document object and return the document id. + + Args: + doc: The document object to create chunks from. It should have a text attribute and optionally an id and a metadata attribute. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A tuple of (doc_chunks, doc_id), where doc_chunks is a list of document chunks, each of which is a DocumentChunk object with an id, a document_id, a text, and a metadata attribute, + and doc_id is the id of the document object, generated if not provided. The id of each chunk is generated from the document id and a sequential number, and the metadata is copied from the document object. + """ + # Check if the document text is empty or whitespace + if not doc.text or doc.text.isspace(): + return [], doc.id or str(uuid.uuid4()) + + # Generate a document id if not provided + doc_id = doc.id or str(uuid.uuid4()) + + # Split the document text into chunks + text_chunks = get_text_chunks(doc.text, chunk_token_size) + + metadata = ( + DocumentChunkMetadata(**doc.metadata.__dict__) + if doc.metadata is not None + else DocumentChunkMetadata() + ) + + metadata.document_id = doc_id + + # Initialize an empty list of chunks for this document + doc_chunks = [] + + # Assign each chunk a sequential number and create a DocumentChunk object + for i, text_chunk in enumerate(text_chunks): + chunk_id = f"{doc_id}_{i}" + doc_chunk = DocumentChunk( + id=chunk_id, + text=text_chunk, + metadata=metadata, + ) + # Append the chunk object to the list of chunks for this document + doc_chunks.append(doc_chunk) + + # Return the list of chunks and the document id + return doc_chunks, doc_id + + +def get_document_chunks( + documents: List[Document], chunk_token_size: Optional[int] +) -> Dict[str, List[DocumentChunk]]: + """ + Convert a list of documents into a dictionary from document id to list of document chunks. + + Args: + documents: The list of documents to convert. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A dictionary mapping each document id to a list of document chunks, each of which is a DocumentChunk object + with text, metadata, and embedding attributes. + """ + # Initialize an empty dictionary of lists of chunks + chunks: Dict[str, List[DocumentChunk]] = {} + + # Initialize an empty list of all chunks + all_chunks: List[DocumentChunk] = [] + + # Loop over each document and create chunks + for doc in documents: + doc_chunks, doc_id = create_document_chunks(doc, chunk_token_size) + + # Append the chunks for this document to the list of all chunks + all_chunks.extend(doc_chunks) + + # Add the list of chunks for this document to the dictionary with the document id as the key + chunks[doc_id] = doc_chunks + + # Check if there are no chunks + if not all_chunks: + return {} + + # Get all the embeddings for the document chunks in batches, using get_embeddings + embeddings: List[List[float]] = [] + for i in range(0, len(all_chunks), EMBEDDINGS_BATCH_SIZE): + # Get the text of the chunks in the current batch + batch_texts = [ + chunk.text for chunk in all_chunks[i : i + EMBEDDINGS_BATCH_SIZE] + ] + + # Get the embeddings for the batch texts + batch_embeddings = get_embeddings(batch_texts) + + # Append the batch embeddings to the embeddings list + embeddings.extend(batch_embeddings) + + # Update the document chunk objects with the embeddings + for i, chunk in enumerate(all_chunks): + # Assign the embedding from the embeddings list to the chunk object + chunk.embedding = embeddings[i] + + return chunks diff --git a/src/file-agent.js b/src/file-agent.js index ef1471b..e9d286c 100644 --- a/src/file-agent.js +++ b/src/file-agent.js @@ -1,3 +1,5 @@ +const getPath = require("path"); + var ignoringWatcher = require('ignoring-watcher').createWatcher({ // Directory to watch. Defaults to process.cwd() dir: __dirname, @@ -81,8 +83,10 @@ const requestDelete = async (filePath, fileOpts = {}, headers = {}) => { // source_id: Optional[str] = None form.append('ids', [filePath]); - // form.append('filter', filePath); // iterate opts with extra metadata + Object.keys(fileOpts).forEach(key => { + form.append(key, fileOpts[key]); + }); const resp = await axios.post(`${baseApiUrl}/delete`, form, { headers: { @@ -98,8 +102,6 @@ const requestDelete = async (filePath, fileOpts = {}, headers = {}) => { return new Error(err.message); } }; - - const accessToken = process.env.DATABASE_INTERFACE_BEARER_TOKEN; @@ -117,12 +119,19 @@ async function deleteFile (filePath) { console.log(response); }; +import getProgrammingLanguage from "detect-programming-language"; + + ignoringWatcher .on('modified', function(eventArgs) { // Fired for any change event (add, delete, etc.) var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir var path = eventArgs.path; // The full file system path of the modified file - upsertFile(filePath); + + const ext = getPath.extname(path); + const language = getProgrammingLanguage(ext); + + upsertFile(filePath, {language}); }); ignoringWatcher .on('add', function(eventArgs) { // Fired for any change event (add, delete, etc.) diff --git a/src/langchain.ts b/src/langchain.ts index e99e841..f544223 100644 --- a/src/langchain.ts +++ b/src/langchain.ts @@ -1,6 +1,6 @@ import { OpenAI } from "langchain/llms/openai"; import { Document } from "langchain/document"; -import { TypescriptTextSplitter } from "./typescript_textsplitter"; +import { TypescriptTextSplitter } from "./typescript-textsplitter"; import { TextSplitterChunkHeaderOptions } from "langchain/text_splitter"; export const createDocsFromCode = async (codeFiles: string[], metadatas?: Record[], chunkHeaderOptions?: TextSplitterChunkHeaderOptions): Promise => { diff --git a/src/typescript_textsplitter.ts b/src/typescript-textsplitter.ts similarity index 100% rename from src/typescript_textsplitter.ts rename to src/typescript-textsplitter.ts diff --git a/yarn.lock b/yarn.lock index 6004151..acb251b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -892,6 +892,11 @@ "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" "version" "1.0.0" +"detect-programming-language@^1.0.1": + "integrity" "sha512-31kFRf56KY0p6FXEdpewh/EDGYi+8WIUFxK7bcimFCktXepYkQ3kPEtFfslbNL0cCs/7+f+TQz03sg5AwkEwXQ==" + "resolved" "https://registry.npmjs.org/detect-programming-language/-/detect-programming-language-1.0.1.tgz" + "version" "1.0.1" + "diff@5.0.0": "integrity" "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" "resolved" "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" From 73f904e91ecc0f09bf94229ae8e261a23289d396 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 09:28:39 +0200 Subject: [PATCH 04/15] Update Readme and add python Typescript splitter --- README.md | 35 ++++++++++++++++++- package.json | 5 ++- retrieval/services/typescript_textsplitter.py | 35 +++++++++++++++++++ src/typescript-textsplitter.ts | 6 ++++ src/view-provider.ts | 2 +- 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 retrieval/services/typescript_textsplitter.py diff --git a/README.md b/README.md index 650c421..7be47c7 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,27 @@ or via script: This extension intends to use LangChain [CodeSplitter](https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html) to split the code files as code. -A custom TypeScript splitter is included in `src/typescript-textsplitter.js`. It would be easy to add the same for Python. +A custom TypeScript splitter is included in `src/typescript-textsplitter.js`. +A TypeScript splitter is also included as python code in `services/typescript-textsplitter.py` + +They both have a static `build` method that takes a dictionary of options. + +Typescript variant + +```ts +splitter = TypescriptTextSplitter.build(opts) +tsDocs = splitter.create_documents([code]) +return tsDocs +``` + +Python variant + +```py +splitter = TypescriptTextSplitter.build(opts) +ts_docs = splitter.create_documents([code]) +return ts_docs +``` + The retrieval plugin should be made to use a CodeSplitter for text files and other specific chunk splitters for other files (markdown, text etc) using LangChain. @@ -154,6 +174,19 @@ A placeholder file `services/code_chunks` is there to work from. Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. +`upsert` calls `get_document_chunks`, which calls `create_document_chunks` which calls `get_text_chunks` + +`create_document_chunks` gets a document of the type `Document` + +```py +class Document(BaseModel): + id: Optional[str] = None + text: str + metadata: Optional[DocumentMetadata] = None +``` + +Based on the metadata for the document it should decide which chunker (splitter) to use. + ## Using the Extension To use the extension, open a text editor in Visual Studio Code and open the ChatGPT panel by clicking on the ChatGPT icon in the sidebar. This will open a panel with an input field where you can enter your prompt or question. By clicking enter, it will be sent to ChatGPT. Its response will be displayed below the input field in the sidebar (note that it may take some time for it to be calculated). diff --git a/package.json b/package.json index d5d6c77..d701c0b 100644 --- a/package.json +++ b/package.json @@ -149,17 +149,20 @@ "order": 1 }, "chatgpt.model": { - "type": "number", + "type": "string", + "default": "", "description": "The model to use (default: turbo-gpt-3.5)", "order": 1 }, "chatgpt.temperature": { "type": "number", + "default": 1, "description": "The temperature to use (default: 1)", "order": 1 }, "chatgpt.topP": { "type": "number", + "default": 0.1, "description": "The top_p sampling to use (default: 0.1)", "order": 1 }, diff --git a/retrieval/services/typescript_textsplitter.py b/retrieval/services/typescript_textsplitter.py new file mode 100644 index 0000000..2b313df --- /dev/null +++ b/retrieval/services/typescript_textsplitter.py @@ -0,0 +1,35 @@ +from langchain import RecursiveCharacterTextSplitter + +class TypescriptTextSplitter(RecursiveCharacterTextSplitter): + separators = [ + # typescript + "\type ", + "\ninterface ", + "\namespace ", + # javascript + # Split along function definitions + "\nfunction ", + "\nconst ", + "\nlet ", + "\nvar ", + "\nclass ", + # Split along control flow statements + "\nif ", + "\nfor ", + "\nwhile ", + "\nswitch ", + "\ncase ", + "\ndefault ", + # Split by the normal type of lines + "\n\n", + "\n", + " ", + "", + ]; + + @classmethod + def build( + cls, **kwargs: Any + ) -> RecursiveCharacterTextSplitter: + separators = cls.separators + return cls(separators=separators, **kwargs) \ No newline at end of file diff --git a/src/typescript-textsplitter.ts b/src/typescript-textsplitter.ts index 9a19ca0..bce1686 100644 --- a/src/typescript-textsplitter.ts +++ b/src/typescript-textsplitter.ts @@ -27,6 +27,12 @@ export class TypescriptTextSplitter { "", ]; + static build(opts: any = {}) { + return new TypescriptTextSplitter().build({ + ...opts + }); + } + build(opts: any = {}) { return new RecursiveCharacterTextSplitter({ ...opts, diff --git a/src/view-provider.ts b/src/view-provider.ts index 94e0819..ea90699 100644 --- a/src/view-provider.ts +++ b/src/view-provider.ts @@ -188,7 +188,7 @@ export class ChatGPTViewProvider implements vscode.WebviewViewProvider { } }, timeoutMs: this.timeoutLength * 1000 - } + }; if (this.keepConversation) { sendOpts.conversationId = this._conversationId; } From dd6a9b5663e064c6c813765ee14d06ae3000db81 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 20:07:32 +0200 Subject: [PATCH 05/15] Loads more improvements --- README.md | 105 ++++++++++++--- retrieval/models/models.py | 12 ++ retrieval/pyproject.toml | 1 + retrieval/services/chunks.py | 89 +------------ retrieval/services/code_chunks.py | 209 +++--------------------------- retrieval/services/text_chunks.py | 100 ++++++++++++++ src/agent-sync.js | 79 +++++++++++ src/file-agent.js | 176 +++++++++++-------------- 8 files changed, 377 insertions(+), 394 deletions(-) create mode 100644 retrieval/services/text_chunks.py create mode 100644 src/agent-sync.js diff --git a/README.md b/README.md index 7be47c7..98d7312 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This Visual Studio Code extension allows you to use the [unofficial ChatGPT API] ## Update -Updated to use version 5.2.x [ChatGPT API](https://www.npmjs.com/package/chatgpt) Node.js library with support for GPT-4 using an API key instead of the old session key. +Updated to use version `5.2.x` of [ChatGPT API](https://www.npmjs.com/package/chatgpt) Node.js library with support for GPT-4 using an API key instead of the old session key. ## Features - **Ask general questions** or use code snippets from the editor to query ChatGPT via an input box in the sidebar @@ -61,11 +61,17 @@ After completing these steps, the extension should be ready to use. To use this extension, you will need to authenticate with a valid API key from ChatGPT. To get an API key: 1. Go to https://chat.openai.com/chat and log in or sign up. -2. Go to Profile -> API key +2. Go to Profile -> View API keys +3. Create new secret key + +See [How to get ChatGPT API key](https://elephas.app/blog/how-to-get-chatgpt-api-key-clh93ii2e1642073tpacu6w934j) Once you have obtained a API key, you can configure the extension to use it as described in the previous section. -### Run ChatGPT plugin +### Run ChatGPT retrieval plugin + +The modified ChatGPT retrieval plugin can be found in the retrieval folder. +To set it up, following these steps in the terminal: ``` cd retrieval @@ -87,9 +93,12 @@ export PINECONE_INDEX= ``` ### Run Database Interface Server -When The config is ready. Under the project directory, run: -`poetry run start` +Then run `poetry run start` in the `/retrieval` folder + +```bash +retrieval $ poetry run start +``` It will start your Database Interface server. Open your browser and open `http://0.0.0.0:8000/docs#/`. @@ -103,9 +112,9 @@ The goal is to use an architecture similar to [chatgpt with external memory usin This will additionally use LangChain to chunk code files using a [Code splitter](https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html) specific to the language of the code file being processed. -We will need an agent running in OS in the background to monitor file changes which upserts and deletes vectors in the DB based on OS file operations. +We will need an agent running in the project root directory in the background to monitor file changes. For each file change it should call the API to `upsert` or `delete` vectors in the Vector Database so that this DB is always in sync with the project. -It should only take into account files that are "source files" of the project. To achieve this, we can use `.gitignore` or `.npmignore` files foralong with a custom configuration. +It should only take into account files that are "source files" of the project. To achieve this, we can use `.gitignore` or `.npmignore` files along with a custom configuration. ### Chokidar agent with ignore files @@ -128,20 +137,33 @@ watcher .on('unlink', path => log(`File ${path} has been removed`)); ``` -For any `add` or `change` we call the `upsert` API of the FastAPI python API for [GPT Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin.git). +For any `add` or `change` we will call the `upsert` endpoint of the FastAPI python API for the modified [GPT Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin.git). -For `unlink` we call the `delete` endpoint. +For `unlink` of files (ie. deletion of a file) we call the `delete` endpoint. -We will however use [ignoring-watcher](https://www.npmjs.com/package/ignoring-watcher) as a convenient wrapper of Chokidar :) +Doing this, we can keep the Vector DB in sync with the file system of code base. + +We will use [ignoring-watcher](https://www.npmjs.com/package/ignoring-watcher) as a convenient wrapper of Chokidar :) The `file_agent.js` can be found in `src` and can be run simply via node: +You will need to copy the `file-agent.js` file to the root of your project folder and execute it there. + +In addition install `ignoring-watcher` and `detect-programming-language` as development dependencies in your project using a node package such as `npm` + +```bash +npm install ignoring-watcher detect-programming-language -D` +``` + +### Run file agent in your project + `node src/file_agent.js` or via script: `npm run file-agent` + ## Code chunks This extension intends to use LangChain [CodeSplitter](https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html) to split the code files as code. @@ -167,14 +189,26 @@ ts_docs = splitter.create_documents([code]) return ts_docs ``` +The retrieval plugin uses a normal text splitter for text files but uses LangChain CodeSplitter when encountering files with an extension matching a programming language. -The retrieval plugin should be made to use a CodeSplitter for text files and other specific chunk splitters for other files (markdown, text etc) using LangChain. +Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. -A placeholder file `services/code_chunks` is there to work from. +The agent will call `upsert` with the following meta-info based on the file path. +This is meta is determined on a "best effort" basis (some basic assumptions/rules), that can be customized as needed for the particular project. -Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. +```ts + const meta = { + language, + test: isTest, + markdown: isMarkdown, + documentation: isDocumentation, + source: isSrc + }; +``` + +Currently only `language` is being picked up on the Python API side in `upsert` and used for code splitting (chunking). -`upsert` calls `get_document_chunks`, which calls `create_document_chunks` which calls `get_text_chunks` +In the Python API `upsert` calls `get_document_chunks`, which calls `create_document_chunks` which calls `get_text_chunks` `create_document_chunks` gets a document of the type `Document` @@ -185,7 +219,48 @@ class Document(BaseModel): metadata: Optional[DocumentMetadata] = None ``` -Based on the metadata for the document it should decide which chunker (splitter) to use. +`DocumentMetadata` has been expanded to contain some fields to specify more information regarding the particular document, to answer questions such as: + +- Is it part of test suite? +- Is it a configuration file? +- Is it a source file? +- Is it part of project documentation? +- Is it a markdown file? + +```py +class DocumentMetadata(BaseModel): + source: Optional[Source] = None + source_id: Optional[str] = None + language: Optional[str] = None + config: Optional[bool] = None + test: Optional[bool] = None + src: Optional[bool] = None + doc: Optional[bool] = None + markdown: Optional[bool] = None + url: Optional[str] = None + created_at: Optional[str] = None + author: Optional[str] = None +``` + +These fields have also been expanded into the filter: + +```py +class DocumentMetadataFilter(BaseModel): + document_id: Optional[str] = None + language: Optional[str] = None + config: Optional[bool] = None + test: Optional[bool] = None + src: Optional[bool] = None + documentation: Optional[bool] = None + markdown: Optional[bool] = None + source: Optional[Source] = None + source_id: Optional[str] = None + author: Optional[str] = None + start_date: Optional[str] = None # any date string format + end_date: Optional[str] = None # any date string format +``` + +Based on the metadata for the document it can decide which chunker (splitter) to use and how to populate the VectorDB with powerful metadata for use by the AI and embeddings search etc. ## Using the Extension diff --git a/retrieval/models/models.py b/retrieval/models/models.py index ee3ad77..5217a4d 100644 --- a/retrieval/models/models.py +++ b/retrieval/models/models.py @@ -12,6 +12,12 @@ class Source(str, Enum): class DocumentMetadata(BaseModel): source: Optional[Source] = None source_id: Optional[str] = None + language: Optional[str] = None + config: Optional[bool] = None + test: Optional[bool] = None + src: Optional[bool] = None + documentation: Optional[bool] = None + markdown: Optional[bool] = None url: Optional[str] = None created_at: Optional[str] = None author: Optional[str] = None @@ -44,6 +50,12 @@ class DocumentWithChunks(Document): class DocumentMetadataFilter(BaseModel): document_id: Optional[str] = None + language: Optional[str] = None + config: Optional[bool] = None + test: Optional[bool] = None + src: Optional[bool] = None + documentation: Optional[bool] = None + markdown: Optional[bool] = None source: Optional[Source] = None source_id: Optional[str] = None author: Optional[str] = None diff --git a/retrieval/pyproject.toml b/retrieval/pyproject.toml index 37d13be..f8d84af 100644 --- a/retrieval/pyproject.toml +++ b/retrieval/pyproject.toml @@ -13,6 +13,7 @@ secondary = true [tool.poetry.dependencies] igittigitt = "^2.1.2" +langchain = "^0.0.198" python = "^3.10" fastapi = "^0.92.0" uvicorn = "^0.20.0" diff --git a/retrieval/services/chunks.py b/retrieval/services/chunks.py index fa90039..b57e650 100644 --- a/retrieval/services/chunks.py +++ b/retrieval/services/chunks.py @@ -3,6 +3,9 @@ import os from models.models import Document, DocumentChunk, DocumentChunkMetadata +from code_chunks import get_code_chunks +from text_chunks import get_text_chunks + import tiktoken from services.openai import get_embeddings @@ -13,88 +16,7 @@ ) # The encoding scheme to use for tokenization # Constants -CHUNK_SIZE = 200 # The target size of each text chunk in tokens -MIN_CHUNK_SIZE_CHARS = 350 # The minimum size of each text chunk in characters -MIN_CHUNK_LENGTH_TO_EMBED = 5 # Discard chunks shorter than this EMBEDDINGS_BATCH_SIZE = int(os.environ.get("OPENAI_EMBEDDING_BATCH_SIZE", 128)) # The number of embeddings to request at a time -MAX_NUM_CHUNKS = 10000 # The maximum number of chunks to generate from a text - -def get_text_chunks(text: str, chunk_token_size: Optional[int]) -> List[str]: - """ - Split a text into chunks of ~CHUNK_SIZE tokens, based on punctuation and newline boundaries. - - Args: - text: The text to split into chunks. - chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. - - Returns: - A list of text chunks, each of which is a string of ~CHUNK_SIZE tokens. - """ - # Return an empty list if the text is empty or whitespace - if not text or text.isspace(): - return [] - - # Tokenize the text - tokens = tokenizer.encode(text, disallowed_special=()) - - # Initialize an empty list of chunks - chunks = [] - - # Use the provided chunk token size or the default one - chunk_size = chunk_token_size or CHUNK_SIZE - - # Initialize a counter for the number of chunks - num_chunks = 0 - - # Loop until all tokens are consumed - while tokens and num_chunks < MAX_NUM_CHUNKS: - # Take the first chunk_size tokens as a chunk - chunk = tokens[:chunk_size] - - # Decode the chunk into text - chunk_text = tokenizer.decode(chunk) - - # Skip the chunk if it is empty or whitespace - if not chunk_text or chunk_text.isspace(): - # Remove the tokens corresponding to the chunk text from the remaining tokens - tokens = tokens[len(chunk) :] - # Continue to the next iteration of the loop - continue - - # Find the last period or punctuation mark in the chunk - last_punctuation = max( - chunk_text.rfind("."), - chunk_text.rfind("?"), - chunk_text.rfind("!"), - chunk_text.rfind("\n"), - ) - - # If there is a punctuation mark, and the last punctuation index is before MIN_CHUNK_SIZE_CHARS - if last_punctuation != -1 and last_punctuation > MIN_CHUNK_SIZE_CHARS: - # Truncate the chunk text at the punctuation mark - chunk_text = chunk_text[: last_punctuation + 1] - - # Remove any newline characters and strip any leading or trailing whitespace - chunk_text_to_append = chunk_text.replace("\n", " ").strip() - - if len(chunk_text_to_append) > MIN_CHUNK_LENGTH_TO_EMBED: - # Append the chunk text to the list of chunks - chunks.append(chunk_text_to_append) - - # Remove the tokens corresponding to the chunk text from the remaining tokens - tokens = tokens[len(tokenizer.encode(chunk_text, disallowed_special=())) :] - - # Increment the number of chunks - num_chunks += 1 - - # Handle the remaining tokens - if tokens: - remaining_text = tokenizer.decode(tokens).replace("\n", " ").strip() - if len(remaining_text) > MIN_CHUNK_LENGTH_TO_EMBED: - chunks.append(remaining_text) - - return chunks - def create_document_chunks( doc: Document, chunk_token_size: Optional[int] @@ -117,8 +39,9 @@ def create_document_chunks( # Generate a document id if not provided doc_id = doc.id or str(uuid.uuid4()) - # Split the document text into chunks - text_chunks = get_text_chunks(doc.text, chunk_token_size) + doc_lang = doc.metadata.language + + text_chunks = get_code_chunks(doc, chunk_token_size) if doc_lang else get_text_chunks(doc.text, chunk_token_size) metadata = ( DocumentChunkMetadata(**doc.metadata.__dict__) diff --git a/retrieval/services/code_chunks.py b/retrieval/services/code_chunks.py index 4bdd95a..4fee195 100644 --- a/retrieval/services/code_chunks.py +++ b/retrieval/services/code_chunks.py @@ -1,203 +1,26 @@ from typing import Dict, List, Optional, Tuple -import uuid -import os from models.models import Document, DocumentChunk, DocumentChunkMetadata +from langchain.text_splitter import RecursiveCharacterTextSplitter +from typescript_textsplitter import TypescriptTextSplitter +from text_chunks import get_text_chunks -import tiktoken - -from services.openai import get_embeddings - -# Global variables -tokenizer = tiktoken.get_encoding( - "cl100k_base" -) # The encoding scheme to use for tokenization - -# Constants -CHUNK_SIZE = 1024 # The target size of each text chunk in tokens -MIN_CHUNK_SIZE_CHARS = 350 # The minimum size of each text chunk in characters -MIN_CHUNK_LENGTH_TO_EMBED = 5 # Discard chunks shorter than this -EMBEDDINGS_BATCH_SIZE = 128 # The number of embeddings to request at a time -MAX_NUM_CHUNKS = 10000 # The maximum number of chunks to generate from a text - -# TODO: use LangChain CodeSplitter with custom TypeScript splitter -def get_code_chunks(text: str, chunk_token_size: Optional[int]) -> List[str]: - """ - Split a text into chunks of ~CHUNK_SIZE tokens, based on punctuation and newline boundaries. - - Args: - text: The text to split into chunks. - chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. - - Returns: - A list of text chunks, each of which is a string of ~CHUNK_SIZE tokens. - """ - # Return an empty list if the text is empty or whitespace - if not text or text.isspace(): - return [] - - # Tokenize the text - tokens = tokenizer.encode(text, disallowed_special=()) - - # Initialize an empty list of chunks - chunks = [] - - # Use the provided chunk token size or the default one - chunk_size = chunk_token_size or CHUNK_SIZE - - # Initialize a counter for the number of chunks - num_chunks = 0 - - # Loop until all tokens are consumed - while tokens and num_chunks < MAX_NUM_CHUNKS: - # Take the first chunk_size tokens as a chunk - chunk = tokens[:chunk_size] - - # Decode the chunk into text - chunk_text = tokenizer.decode(chunk) - - # Skip the chunk if it is empty or whitespace - if not chunk_text or chunk_text.isspace(): - # Remove the tokens corresponding to the chunk text from the remaining tokens - tokens = tokens[len(chunk) :] - # Continue to the next iteration of the loop - continue - - # Find the last period or punctuation mark in the chunk - last_punctuation = max( - chunk_text.rfind("."), - chunk_text.rfind("?"), - chunk_text.rfind("!"), - chunk_text.rfind("\n"), - ) - - # If there is a punctuation mark, and the last punctuation index is before MIN_CHUNK_SIZE_CHARS - if last_punctuation != -1 and last_punctuation > MIN_CHUNK_SIZE_CHARS: - # Truncate the chunk text at the punctuation mark - chunk_text = chunk_text[: last_punctuation + 1] - - # Remove any newline characters and strip any leading or trailing whitespace - chunk_text_to_append = chunk_text.replace("\n", " ").strip() - - if len(chunk_text_to_append) > MIN_CHUNK_LENGTH_TO_EMBED: - # Append the chunk text to the list of chunks - chunks.append(chunk_text_to_append) - - # Remove the tokens corresponding to the chunk text from the remaining tokens - tokens = tokens[len(tokenizer.encode(chunk_text, disallowed_special=())) :] - - # Increment the number of chunks - num_chunks += 1 - - # Handle the remaining tokens - if tokens: - remaining_text = tokenizer.decode(tokens).replace("\n", " ").strip() - if len(remaining_text) > MIN_CHUNK_LENGTH_TO_EMBED: - chunks.append(remaining_text) - - return chunks - - -def create_document_chunks( - doc: Document, chunk_token_size: Optional[int] -) -> Tuple[List[DocumentChunk], str]: - """ - Create a list of document chunks from a document object and return the document id. - - Args: - doc: The document object to create chunks from. It should have a text attribute and optionally an id and a metadata attribute. - chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. - - Returns: - A tuple of (doc_chunks, doc_id), where doc_chunks is a list of document chunks, each of which is a DocumentChunk object with an id, a document_id, a text, and a metadata attribute, - and doc_id is the id of the document object, generated if not provided. The id of each chunk is generated from the document id and a sequential number, and the metadata is copied from the document object. - """ - # Check if the document text is empty or whitespace - if not doc.text or doc.text.isspace(): - return [], doc.id or str(uuid.uuid4()) - - # Generate a document id if not provided - doc_id = doc.id or str(uuid.uuid4()) - - # Split the document text into chunks - text_chunks = get_text_chunks(doc.text, chunk_token_size) - - metadata = ( - DocumentChunkMetadata(**doc.metadata.__dict__) - if doc.metadata is not None - else DocumentChunkMetadata() +def get_lang_splitter(lang: str): + RecursiveCharacterTextSplitter.from_language( + language=lang, chunk_size=50, chunk_overlap=0 ) - metadata.document_id = doc_id - - # Initialize an empty list of chunks for this document - doc_chunks = [] - - # Assign each chunk a sequential number and create a DocumentChunk object - for i, text_chunk in enumerate(text_chunks): - chunk_id = f"{doc_id}_{i}" - doc_chunk = DocumentChunk( - id=chunk_id, - text=text_chunk, - metadata=metadata, - ) - # Append the chunk object to the list of chunks for this document - doc_chunks.append(doc_chunk) - - # Return the list of chunks and the document id - return doc_chunks, doc_id - - -def get_document_chunks( - documents: List[Document], chunk_token_size: Optional[int] -) -> Dict[str, List[DocumentChunk]]: - """ - Convert a list of documents into a dictionary from document id to list of document chunks. - - Args: - documents: The list of documents to convert. - chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. - - Returns: - A dictionary mapping each document id to a list of document chunks, each of which is a DocumentChunk object - with text, metadata, and embedding attributes. - """ - # Initialize an empty dictionary of lists of chunks - chunks: Dict[str, List[DocumentChunk]] = {} - - # Initialize an empty list of all chunks - all_chunks: List[DocumentChunk] = [] - - # Loop over each document and create chunks - for doc in documents: - doc_chunks, doc_id = create_document_chunks(doc, chunk_token_size) - - # Append the chunks for this document to the list of all chunks - all_chunks.extend(doc_chunks) - - # Add the list of chunks for this document to the dictionary with the document id as the key - chunks[doc_id] = doc_chunks - - # Check if there are no chunks - if not all_chunks: - return {} +# use LangChain CodeSplitter with custom TypeScript splitter +def get_code_chunks(doc: Document, chunk_token_size: Optional[int]) -> List[str]: + ts_splitter = TypescriptTextSplitter.build() - # Get all the embeddings for the document chunks in batches, using get_embeddings - embeddings: List[List[float]] = [] - for i in range(0, len(all_chunks), EMBEDDINGS_BATCH_SIZE): - # Get the text of the chunks in the current batch - batch_texts = [ - chunk.text for chunk in all_chunks[i : i + EMBEDDINGS_BATCH_SIZE] - ] + lang = doc.metadata.language - # Get the embeddings for the batch texts - batch_embeddings = get_embeddings(batch_texts) + lang_splitter = ts_splitter if lang is "typescript" else get_lang_splitter(lang) - # Append the batch embeddings to the embeddings list - embeddings.extend(batch_embeddings) + # default to use text chunk splitter if no matching specific lang splitter is found + if not lang_splitter: + return get_text_chunks(doc, chunk_token_size) + + return lang_splitter.create_documents([doc.text]) - # Update the document chunk objects with the embeddings - for i, chunk in enumerate(all_chunks): - # Assign the embedding from the embeddings list to the chunk object - chunk.embedding = embeddings[i] - return chunks diff --git a/retrieval/services/text_chunks.py b/retrieval/services/text_chunks.py new file mode 100644 index 0000000..297db94 --- /dev/null +++ b/retrieval/services/text_chunks.py @@ -0,0 +1,100 @@ +from typing import List, Optional, Tuple +import os +from models.models import Document, DocumentChunk, DocumentChunkMetadata + +from code_chunks import get_code_chunks + +import tiktoken + +from services.openai import get_embeddings + +# Global variables +tokenizer = tiktoken.get_encoding( + "cl100k_base" +) # The encoding scheme to use for tokenization + +# Constants +CHUNK_SIZE = 200 # The target size of each text chunk in tokens +MIN_CHUNK_SIZE_CHARS = 350 # The minimum size of each text chunk in characters +MIN_CHUNK_LENGTH_TO_EMBED = 5 # Discard chunks shorter than this +EMBEDDINGS_BATCH_SIZE = int(os.environ.get("OPENAI_EMBEDDING_BATCH_SIZE", 128)) # The number of embeddings to request at a time +MAX_NUM_CHUNKS = 10000 # The maximum number of chunks to generate from a text + + +def get_text_chunks(doc: Document, chunk_token_size: Optional[int]) -> List[str]: + """ + Split a text into chunks of ~CHUNK_SIZE tokens, based on punctuation and newline boundaries. + + Args: + text: The text to split into chunks. + chunk_token_size: The target size of each chunk in tokens, or None to use the default CHUNK_SIZE. + + Returns: + A list of text chunks, each of which is a string of ~CHUNK_SIZE tokens. + """ + text = doc.text + + # Return an empty list if the text is empty or whitespace + if not text or text.isspace(): + return [] + + # Tokenize the text + tokens = tokenizer.encode(text, disallowed_special=()) + + # Initialize an empty list of chunks + chunks = [] + + # Use the provided chunk token size or the default one + chunk_size = chunk_token_size or CHUNK_SIZE + + # Initialize a counter for the number of chunks + num_chunks = 0 + + # Loop until all tokens are consumed + while tokens and num_chunks < MAX_NUM_CHUNKS: + # Take the first chunk_size tokens as a chunk + chunk = tokens[:chunk_size] + + # Decode the chunk into text + chunk_text = tokenizer.decode(chunk) + + # Skip the chunk if it is empty or whitespace + if not chunk_text or chunk_text.isspace(): + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(chunk) :] + # Continue to the next iteration of the loop + continue + + # Find the last period or punctuation mark in the chunk + last_punctuation = max( + chunk_text.rfind("."), + chunk_text.rfind("?"), + chunk_text.rfind("!"), + chunk_text.rfind("\n"), + ) + + # If there is a punctuation mark, and the last punctuation index is before MIN_CHUNK_SIZE_CHARS + if last_punctuation != -1 and last_punctuation > MIN_CHUNK_SIZE_CHARS: + # Truncate the chunk text at the punctuation mark + chunk_text = chunk_text[: last_punctuation + 1] + + # Remove any newline characters and strip any leading or trailing whitespace + chunk_text_to_append = chunk_text.replace("\n", " ").strip() + + if len(chunk_text_to_append) > MIN_CHUNK_LENGTH_TO_EMBED: + # Append the chunk text to the list of chunks + chunks.append(chunk_text_to_append) + + # Remove the tokens corresponding to the chunk text from the remaining tokens + tokens = tokens[len(tokenizer.encode(chunk_text, disallowed_special=())) :] + + # Increment the number of chunks + num_chunks += 1 + + # Handle the remaining tokens + if tokens: + remaining_text = tokenizer.decode(tokens).replace("\n", " ").strip() + if len(remaining_text) > MIN_CHUNK_LENGTH_TO_EMBED: + chunks.append(remaining_text) + + return chunks diff --git a/src/agent-sync.js b/src/agent-sync.js new file mode 100644 index 0000000..e5ffb25 --- /dev/null +++ b/src/agent-sync.js @@ -0,0 +1,79 @@ +const baseApiUrl = process.env.BASE_API_URL || 'http://0.0.0.0:8000'; + +const requestUpsertFile = async (filePath, fileOpts = {}, headers = {}) => { + try { + const file = fs.createReadStream(filePath); + const form = new FormData(); + form.append('title', title); + form.append('file', file); + form.append('filePath', filePath); + // iterate opts with extra metadata + Object.keys(fileOpts).forEach(key => { + form.append(key, fileOpts[key]); + }); + + const resp = await axios.post(`${baseApiUrl}/upsert-file`, form, { + headers: { + ...form.getHeaders(), + ...headers || {} + } + }); + + if (resp.status === 200) { + return 'Upload complete'; + } + } catch(err) { + return new Error(err.message); + } +}; + +// ids or request.filter or request.delete_all +const requestDelete = async (filePath, fileOpts = {}, headers = {}) => { + try { + const form = new FormData(); + // + // ids: Optional[List[str]] = None + // filter: Optional[DocumentMetadataFilter] = None + // delete_all: Optional[bool] = False + + // DocumentMetadataFilter + // document_id: Optional[str] = None + // source: Optional[Source] = None + // source_id: Optional[str] = None + + form.append('ids', [filePath]); + // iterate opts with extra metadata + Object.keys(fileOpts).forEach(key => { + form.append(key, fileOpts[key]); + }); + + const resp = await axios.post(`${baseApiUrl}/delete`, form, { + headers: { + ...form.getHeaders(), + ...headers || {} + } + }); + + if (resp.status === 200) { + return 'Delete complete'; + } + } catch(err) { + return new Error(err.message); + } +}; + +const accessToken = process.env.DATABASE_INTERFACE_BEARER_TOKEN; + +const headers = { + "Authorization": "Bearer " + accessToken // token +}; + +async function upsertFile (filePath) { + const response = await requestUpsertFile(filePath, {}, headers); + console.log(response); +}; + +async function deleteFile (filePath) { + const response = await requestDelete(filePath, {}, headers); + console.log(response); +}; diff --git a/src/file-agent.js b/src/file-agent.js index e9d286c..93d0f43 100644 --- a/src/file-agent.js +++ b/src/file-agent.js @@ -1,6 +1,54 @@ const getPath = require("path"); +const watcher = require('ignoring-watcher'); +import getProgrammingLanguage from "detect-programming-language"; + +function getMeta(path) { + const ext = getPath.extname(path); + const isTest = false; + if (path.match(/tests?.\//)) { + isTest = true; + } + if (ext.match(/\.test\./)) { + isTest = true; + } + if (ext.match(/\.spec\./)) { + isTest = true; + } + + if (ext.match(/\.spec\./)) { + isTest = true; + } + + isSrc = false; + if (path.match(/src\//) || path.match(/source\//) || path.match(/libs?\//)) { + isSrc = true; + } + + isMarkDown = false; + if (['.md', '.mkd', '.mkdown', '.markdown'].includes(ext)) { + isMarkDown = true; + } + // to be improved w doc/docs folder etc + isDocumentation = false; + if (isMarkDown) { + isDocumentation = true; + } + + const language = getProgrammingLanguage(ext); + + const meta = { + language, + test: isTest, + markdown: isMarkdown, + documentation: isDocumentation, + source: isSrc + }; + return meta; +} + -var ignoringWatcher = require('ignoring-watcher').createWatcher({ +function startWatcher(dir) { + const ignoringWatcher = watcher.createWatcher({ // Directory to watch. Defaults to process.cwd() dir: __dirname, @@ -28,123 +76,45 @@ var ignoringWatcher = require('ignoring-watcher').createWatcher({ // If no ignore patterns were found via the other properties // then these ignore patterns will be used defaultIgnorePatterns: [ - '.*' + '.*', + '*.lock' ], // The following patterns will always be loaded and not impact // the loading of the `defaultIgnorePatterns` ignoreAlwaysPatterns: [ - 'log.*', - '/temp/*' + 'log.*', // no need to use log files + '*.lock', // no need to use lock files + '/temp/*' // no need to look at anything in temp folder ] -}); - -const baseApiUrl = process.env.BASE_API_URL || 'http://0.0.0.0:8000'; - -const requestUpsertFile = async (filePath, fileOpts = {}, headers = {}) => { - try { - const file = fs.createReadStream(filePath); - const filePath = filePath; - - const form = new FormData(); - form.append('title', title); - form.append('filePath', filePath); - // iterate opts with extra metadata - - const resp = await axios.post(`${baseApiUrl}/upsert-file`, form, { - headers: { - ...form.getHeaders(), - ...headers || {} - } - }); - - if (resp.status === 200) { - return 'Upload complete'; - } - } catch(err) { - return new Error(err.message); - } -}; - -// ids or request.filter or request.delete_all -const requestDelete = async (filePath, fileOpts = {}, headers = {}) => { - try { - const filePath = filePath; - - const form = new FormData(); - // - // ids: Optional[List[str]] = None - // filter: Optional[DocumentMetadataFilter] = None - // delete_all: Optional[bool] = False - - // DocumentMetadataFilter - // document_id: Optional[str] = None - // source: Optional[Source] = None - // source_id: Optional[str] = None - - form.append('ids', [filePath]); - // iterate opts with extra metadata - Object.keys(fileOpts).forEach(key => { - form.append(key, fileOpts[key]); - }); - - const resp = await axios.post(`${baseApiUrl}/delete`, form, { - headers: { - ...form.getHeaders(), - ...headers || {} - } - }); - - if (resp.status === 200) { - return 'Delete complete'; - } - } catch(err) { - return new Error(err.message); - } -}; - -const accessToken = process.env.DATABASE_INTERFACE_BEARER_TOKEN; - -const headers = { - "Authorization": "Bearer " + accessToken // token -}; - -async function upsertFile (filePath) { - const response = await requestUpsertFile(filePath, {}, headers); - console.log(response); -}; + }); -async function deleteFile (filePath) { - const response = await requestDelete(filePath, {}, headers); - console.log(response); -}; - -import getProgrammingLanguage from "detect-programming-language"; - - - -ignoringWatcher + ignoringWatcher .on('modified', function(eventArgs) { // Fired for any change event (add, delete, etc.) - var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + // var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir var path = eventArgs.path; // The full file system path of the modified file - - const ext = getPath.extname(path); - const language = getProgrammingLanguage(ext); - - upsertFile(filePath, {language}); + const meta = getMeta(path); + upsertFile(filePath, meta); }); -ignoringWatcher + ignoringWatcher .on('add', function(eventArgs) { // Fired for any change event (add, delete, etc.) - var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir - var path = eventArgs.path; // The full file system path of the modified file - upsertFile(filePath); + // var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + var path = eventArgs.path; + const meta = getMeta(path); + upsertFile(filePath, meta); }); -ignoringWatcher + ignoringWatcher .on('unlink', function(eventArgs) { // Fired for any change event (add, delete, etc.) var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir var path = eventArgs.path; // The full file system path of the modified file deleteFile(filePath); }); - -ignoringWatcher.startWatching(); \ No newline at end of file + + + + ignoringWatcher.startWatching(); +} + +startWatcher(); + From a387a51fe0d30bcd6122a599ac189fc21f5656f8 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 20:22:13 +0200 Subject: [PATCH 06/15] Add path to document metadata and update docs --- README.md | 46 +++++++++++++++++++++++++++++++++----- retrieval/models/models.py | 2 ++ src/file-agent.js | 24 +++++++++++++++++++- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 98d7312..1b90ca2 100644 --- a/README.md +++ b/README.md @@ -193,12 +193,14 @@ The retrieval plugin uses a normal text splitter for text files but uses LangCha Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. -The agent will call `upsert` with the following meta-info based on the file path. +The agent will call the `upsert_file` Python API with the following meta-info based on the file path. This is meta is determined on a "best effort" basis (some basic assumptions/rules), that can be customized as needed for the particular project. ```ts const meta = { + path: filePath, language, + config: isConfig, test: isTest, markdown: isMarkdown, documentation: isDocumentation, @@ -206,7 +208,37 @@ This is meta is determined on a "best effort" basis (some basic assumptions/rule }; ``` -Currently only `language` is being picked up on the Python API side in `upsert` and used for code splitting (chunking). +`language` will be picked up on the Python API side in `upsert` and is set, it will be used for code splitting (chunking) based on language specific chunk separators and rules. + +### Upsert file + +The upsert file Python logic is shown here for reference. First the Metadata is parsed into a `metadata_obj` that adheres to `DocumentMetadata` (see further below). + +This `metadata_obj` is then used to get a full `document` object (of type `Document`) from the file. This document is then stored in the Vector DB. + +```py +async def upsert_file( + file: UploadFile = File(...), + metadata: Optional[str] = Form(None), +): + try: + metadata_obj = ( + DocumentMetadata.parse_raw(metadata) + if metadata + else DocumentMetadata(source=Source.file) + ) + except: + metadata_obj = DocumentMetadata(source=Source.file) + + document = await get_document_from_file(file, metadata_obj) + + try: + ids = await datastore.upsert([document]) + return UpsertResponse(ids=ids) + except Exception as e: + logger.error(e) + raise HTTPException(status_code=500, detail=f"str({e})") +``` In the Python API `upsert` calls `get_document_chunks`, which calls `create_document_chunks` which calls `get_text_chunks` @@ -221,6 +253,8 @@ class Document(BaseModel): `DocumentMetadata` has been expanded to contain some fields to specify more information regarding the particular document, to answer questions such as: +- What is the local file path of the document? +- What programming language is used? - Is it part of test suite? - Is it a configuration file? - Is it a source file? @@ -231,11 +265,12 @@ class Document(BaseModel): class DocumentMetadata(BaseModel): source: Optional[Source] = None source_id: Optional[str] = None + path: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None - src: Optional[bool] = None - doc: Optional[bool] = None + source: Optional[bool] = None + documentation: Optional[bool] = None markdown: Optional[bool] = None url: Optional[str] = None created_at: Optional[str] = None @@ -247,10 +282,11 @@ These fields have also been expanded into the filter: ```py class DocumentMetadataFilter(BaseModel): document_id: Optional[str] = None + path: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None - src: Optional[bool] = None + source: Optional[bool] = None documentation: Optional[bool] = None markdown: Optional[bool] = None source: Optional[Source] = None diff --git a/retrieval/models/models.py b/retrieval/models/models.py index 5217a4d..720b256 100644 --- a/retrieval/models/models.py +++ b/retrieval/models/models.py @@ -12,6 +12,7 @@ class Source(str, Enum): class DocumentMetadata(BaseModel): source: Optional[Source] = None source_id: Optional[str] = None + path: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None @@ -50,6 +51,7 @@ class DocumentWithChunks(Document): class DocumentMetadataFilter(BaseModel): document_id: Optional[str] = None + path: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None diff --git a/src/file-agent.js b/src/file-agent.js index 93d0f43..b09279a 100644 --- a/src/file-agent.js +++ b/src/file-agent.js @@ -23,7 +23,27 @@ function getMeta(path) { if (path.match(/src\//) || path.match(/source\//) || path.match(/libs?\//)) { isSrc = true; } - + + isConfig = false; + if (path.match(/configs?\//) || path.match(/configurations?\//)) { + isConfig = true; + } + if (ext.match(/\.ya?ml/)) { + isConfig = true; + } + if (ext.match(/\.json/)) { + isConfig = true; + } + if (ext.match(/\.xml/)) { + isConfig = true; + } + if (ext.match(/\.ini/)) { + isConfig = true; + } + if (ext.match(/\.env/)) { + isConfig = true; + } + isMarkDown = false; if (['.md', '.mkd', '.mkdown', '.markdown'].includes(ext)) { isMarkDown = true; @@ -37,7 +57,9 @@ function getMeta(path) { const language = getProgrammingLanguage(ext); const meta = { + path: filePath, language, + config: isConfig, test: isTest, markdown: isMarkdown, documentation: isDocumentation, From 3fa14a85903a78ce1ee71586574c995ed9e3c259 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 20:50:28 +0200 Subject: [PATCH 07/15] Map and match langCode to LangChain codesplitter --- README.md | 25 ++++++++++++++++++++++--- retrieval/models/models.py | 2 ++ retrieval/services/chunks.py | 2 +- src/file-agent.js | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1b90ca2..a3d2190 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,15 @@ You will need to copy the `file-agent.js` file to the root of your project folde In addition install `ignoring-watcher` and `detect-programming-language` as development dependencies in your project using a node package such as `npm` +The programming language will be detected based on the [languagemap](https://github.com/blakeembrey/language-map/blob/main/languages.json) based on Github's [linguist yaml language file](https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml) + + +Alternative node lang detectors: +- [lang-detector](https://www.npmjs.com/package/lang-detector) +- [language-detect](https://www.npmjs.com/package/language-detect) +- [flourite](https://www.npmjs.com/package/flourite) +- [program-language-detector](https://www.npmjs.com/package/program-language-detector) + ```bash npm install ignoring-watcher detect-programming-language -D` ``` @@ -194,12 +203,14 @@ The retrieval plugin uses a normal text splitter for text files but uses LangCha Currently the file agent sends a special `language` metadata field as part of the `upsert` API call which can be used for chunking code files using an appropriate splitter. The agent will call the `upsert_file` Python API with the following meta-info based on the file path. -This is meta is determined on a "best effort" basis (some basic assumptions/rules), that can be customized as needed for the particular project. + +This meta is determined on a "best effort" basis (some basic assumptions/rules), that can be customized as needed for the particular project. ```ts const meta = { path: filePath, - language, + language, // from Github's languagemap + langCode, // matches supported language codes for LangChain CodeSplitter lookup config: isConfig, test: isTest, markdown: isMarkdown, @@ -208,7 +219,11 @@ This is meta is determined on a "best effort" basis (some basic assumptions/rule }; ``` -`language` will be picked up on the Python API side in `upsert` and is set, it will be used for code splitting (chunking) based on language specific chunk separators and rules. +`language` will be picked up on the Python API side in `upsert` and is set, it will be used for code splitting (chunking) based on LangChain's language specific chunk separators and rules. + +Yet another approach would be to use the python library [guesslang](https://github.com/yoeo/guesslang) to guess the language of the document text based on Tensorflow machine learning framework (See [how does guesslang guess](https://guesslang.readthedocs.io/en/latest/contents.html#how-does-guesslang-guess)) + +> "Guesslang detects the programming language of a given source code. It supports more than 50 programming languages and detects the correct programming language with more than 90% accuracy." ### Upsert file @@ -242,6 +257,8 @@ async def upsert_file( In the Python API `upsert` calls `get_document_chunks`, which calls `create_document_chunks` which calls `get_text_chunks` +### Create document chunks + `create_document_chunks` gets a document of the type `Document` ```py @@ -266,6 +283,7 @@ class DocumentMetadata(BaseModel): source: Optional[Source] = None source_id: Optional[str] = None path: Optional[str] = None + langCode: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None @@ -283,6 +301,7 @@ These fields have also been expanded into the filter: class DocumentMetadataFilter(BaseModel): document_id: Optional[str] = None path: Optional[str] = None + langCode: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None diff --git a/retrieval/models/models.py b/retrieval/models/models.py index 720b256..77cc2f1 100644 --- a/retrieval/models/models.py +++ b/retrieval/models/models.py @@ -14,6 +14,7 @@ class DocumentMetadata(BaseModel): source_id: Optional[str] = None path: Optional[str] = None language: Optional[str] = None + langCode: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None src: Optional[bool] = None @@ -52,6 +53,7 @@ class DocumentWithChunks(Document): class DocumentMetadataFilter(BaseModel): document_id: Optional[str] = None path: Optional[str] = None + langCode: Optional[str] = None language: Optional[str] = None config: Optional[bool] = None test: Optional[bool] = None diff --git a/retrieval/services/chunks.py b/retrieval/services/chunks.py index b57e650..0462870 100644 --- a/retrieval/services/chunks.py +++ b/retrieval/services/chunks.py @@ -39,7 +39,7 @@ def create_document_chunks( # Generate a document id if not provided doc_id = doc.id or str(uuid.uuid4()) - doc_lang = doc.metadata.language + doc_lang = doc.metadata.langCode text_chunks = get_code_chunks(doc, chunk_token_size) if doc_lang else get_text_chunks(doc.text, chunk_token_size) diff --git a/src/file-agent.js b/src/file-agent.js index b09279a..11c8a79 100644 --- a/src/file-agent.js +++ b/src/file-agent.js @@ -56,9 +56,30 @@ function getMeta(path) { const language = getProgrammingLanguage(ext); + const langMap = { + 'C++': 'cpp', + Go: 'go', + Java: 'java', + Javascript: 'js', + PHP: 'php', + Proto: 'proto', + Python: 'python', + reStructuredText: 'rst', + Ruby: 'ruby', + Rust: 'rust', + Scala: 'scala', + Swift: 'swift', + Markdown: 'markdown', + Latex: 'latex', + HTML: 'html' + }; + + const langCode = langMap[language] + const meta = { path: filePath, language, + langCode, config: isConfig, test: isTest, markdown: isMarkdown, From 74930cd9043a74ae45aa1696286f5900ff21f8f7 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 21:54:40 +0200 Subject: [PATCH 08/15] More improvements and fixes --- README.md | 30 +++++++++++++++---- {src => agent}/agent-sync.js | 16 +++++++++- {src => agent}/file-agent.js | 27 ++++++++++------- agent/package.json | 17 +++++++++++ agent/run-agent | 6 ++++ package.json | 3 +- retrieval/services/arm_textsplitter.py | 21 +++++++++++++ retrieval/services/code_chunks.py | 2 +- retrieval/services/typescript_textsplitter.py | 2 +- retrieval/xtras/chat_utils.py | 4 +-- retrieval/xtras/database_utils.py | 7 +++-- src/typescript-textsplitter.ts | 2 +- 12 files changed, 110 insertions(+), 27 deletions(-) rename {src => agent}/agent-sync.js (85%) rename {src => agent}/file-agent.js (83%) create mode 100644 agent/package.json create mode 100644 agent/run-agent create mode 100644 retrieval/services/arm_textsplitter.py diff --git a/README.md b/README.md index a3d2190..5f228e8 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ poetry install Set your environment variables -``` +```bash export DATASTORE=pinecone export BEARER_TOKEN= export OPENAI_API_KEY= @@ -92,6 +92,23 @@ export PINECONE_ENVIRONMENT= export PINECONE_INDEX= ``` +Other environment variables + +```bash +DATABASE_INTERFACE_BEARER_TOKEN= +BASE_API_URL=http://0.0.0.0:8000 +PROJECT_PATH= +GPT_MODEL= +``` + +`GPT_MODEL` is the ChatGPT model to use, defaulting to `gpt-3.5-turbo`. + +`DATABASE_INTERFACE_BEARER_TOKEN` should be the same as `BEARER_TOKEN`. It is the token used with OAuth2 to authenticate with the DB. + +`BASE_API_URL` is the root URL of the Python FastAPI server which defaults to `http://0.0.0.0:8000` + +`PROJECT_PATH` is used by the file agent to correctly determine relative file path of a changed file relative to the project root. + ### Run Database Interface Server Then run `poetry run start` in the `/retrieval` folder @@ -147,9 +164,10 @@ We will use [ignoring-watcher](https://www.npmjs.com/package/ignoring-watcher) a The `file_agent.js` can be found in `src` and can be run simply via node: -You will need to copy the `file-agent.js` file to the root of your project folder and execute it there. - -In addition install `ignoring-watcher` and `detect-programming-language` as development dependencies in your project using a node package such as `npm` +Copy the `agent` folder of this repo to the root of your project folder: +- `file-agent.js` +- `agent-sync.js` +- `package.json` The programming language will be detected based on the [languagemap](https://github.com/blakeembrey/language-map/blob/main/languages.json) based on Github's [linguist yaml language file](https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml) @@ -166,9 +184,9 @@ npm install ignoring-watcher detect-programming-language -D` ### Run file agent in your project -`node src/file_agent.js` +`node agent/file_agent.js` -or via script: +or via script (see `scripts` section in `agent/package.json`): `npm run file-agent` diff --git a/src/agent-sync.js b/agent/agent-sync.js similarity index 85% rename from src/agent-sync.js rename to agent/agent-sync.js index e5ffb25..bf14d3f 100644 --- a/src/agent-sync.js +++ b/agent/agent-sync.js @@ -1,3 +1,5 @@ +const path = require("path"); +const axios = require("axios"); const baseApiUrl = process.env.BASE_API_URL || 'http://0.0.0.0:8000'; const requestUpsertFile = async (filePath, fileOpts = {}, headers = {}) => { @@ -68,8 +70,15 @@ const headers = { "Authorization": "Bearer " + accessToken // token }; +const projectPath = process.env.PROJECT_PATH; + async function upsertFile (filePath) { - const response = await requestUpsertFile(filePath, {}, headers); + const opts = {}; + if (projectPath) { + const relativePath = path.relative(projectPath, filePath); + opts = {relativePath}; + } + const response = await requestUpsertFile(filePath, opts, headers); console.log(response); }; @@ -77,3 +86,8 @@ async function deleteFile (filePath) { const response = await requestDelete(filePath, {}, headers); console.log(response); }; + +module.exports = { + upsertFile, + deleteFile +} diff --git a/src/file-agent.js b/agent/file-agent.js similarity index 83% rename from src/file-agent.js rename to agent/file-agent.js index 11c8a79..0aeca8a 100644 --- a/src/file-agent.js +++ b/agent/file-agent.js @@ -1,6 +1,9 @@ + + +const sync = require("./agent-sync"); const getPath = require("path"); const watcher = require('ignoring-watcher'); -import getProgrammingLanguage from "detect-programming-language"; +const getProgrammingLanguage = requie("detect-programming-language"); function getMeta(path) { const ext = getPath.extname(path); @@ -58,9 +61,13 @@ function getMeta(path) { const langMap = { 'C++': 'cpp', + 'C#': 'java', Go: 'go', Java: 'java', Javascript: 'js', + JSX: 'js', + Typescript: 'typescript', + TSX: 'typescript', PHP: 'php', Proto: 'proto', Python: 'python', @@ -74,7 +81,7 @@ function getMeta(path) { HTML: 'html' }; - const langCode = langMap[language] + const langCode = langMap[language]; const meta = { path: filePath, @@ -93,7 +100,7 @@ function getMeta(path) { function startWatcher(dir) { const ignoringWatcher = watcher.createWatcher({ // Directory to watch. Defaults to process.cwd() - dir: __dirname, + dir: dir || getPath.join(__dirname, '..'), // Watch multiple directories instead of a single dir dirs: ['some/dir', 'another/dir'], @@ -135,23 +142,23 @@ function startWatcher(dir) { ignoringWatcher .on('modified', function(eventArgs) { // Fired for any change event (add, delete, etc.) // var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir - var path = eventArgs.path; // The full file system path of the modified file + var filePath = eventArgs.path; // The full file system path of the modified file const meta = getMeta(path); - upsertFile(filePath, meta); + sync.upsertFile(filePath, meta); }); ignoringWatcher .on('add', function(eventArgs) { // Fired for any change event (add, delete, etc.) // var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir - var path = eventArgs.path; + var filePath = eventArgs.path; const meta = getMeta(path); - upsertFile(filePath, meta); + sync.upsertFile(filePath, meta); }); ignoringWatcher .on('unlink', function(eventArgs) { // Fired for any change event (add, delete, etc.) - var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir - var path = eventArgs.path; // The full file system path of the modified file - deleteFile(filePath); + // var type = eventArgs.type; // add | addDir | change | unlink | unlinkDir + var filePath = eventArgs.path; // The full file system path of the modified file + sync.deleteFile(filePath); }); diff --git a/agent/package.json b/agent/package.json new file mode 100644 index 0000000..7fd899a --- /dev/null +++ b/agent/package.json @@ -0,0 +1,17 @@ +{ + "name": "file-agent", + "version": "1.0.0", + "description": "Agent to detect file changes and sync with Vector DB for AI memory", + "main": "file-agent.js", + "scripts": { + "file-agent": "node agent/file_agent.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "ignoring-watcher": "^1.1.0", + "detect-programming-language": "^1.0.1", + "axios": "^1.4.0" + } +} diff --git a/agent/run-agent b/agent/run-agent new file mode 100644 index 0000000..b8b2016 --- /dev/null +++ b/agent/run-agent @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +const projPath = process.env.PROJECT_PATH; + + const { startWatcher } = require("./file-agent"); + startWatcher(projPath); \ No newline at end of file diff --git a/package.json b/package.json index d701c0b..6b4c201 100644 --- a/package.json +++ b/package.json @@ -226,8 +226,7 @@ "watch-tests": "tsc -p . -w --outDir out", "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js", - "file-agent": "node src/file_agent.js" + "test": "node ./out/test/runTest.js" }, "devDependencies": { "@types/glob": "^8.1.0", diff --git a/retrieval/services/arm_textsplitter.py b/retrieval/services/arm_textsplitter.py new file mode 100644 index 0000000..4b293f6 --- /dev/null +++ b/retrieval/services/arm_textsplitter.py @@ -0,0 +1,21 @@ +from langchain import RecursiveCharacterTextSplitter + +class ArmTextSplitter(RecursiveCharacterTextSplitter): + separators = [ + "\nparameters", + "\nvariables", + "\nfunctions", + "\nresources", + "\noutputs", + # Split by the normal type of lines + "\n\n", + "\n", + "", + ]; + + @classmethod + def build( + cls, **kwargs: Any + ) -> RecursiveCharacterTextSplitter: + separators = cls.separators + return cls(separators=separators, **kwargs) \ No newline at end of file diff --git a/retrieval/services/code_chunks.py b/retrieval/services/code_chunks.py index 4fee195..ea6c7af 100644 --- a/retrieval/services/code_chunks.py +++ b/retrieval/services/code_chunks.py @@ -13,7 +13,7 @@ def get_lang_splitter(lang: str): def get_code_chunks(doc: Document, chunk_token_size: Optional[int]) -> List[str]: ts_splitter = TypescriptTextSplitter.build() - lang = doc.metadata.language + lang = doc.metadata.langCode lang_splitter = ts_splitter if lang is "typescript" else get_lang_splitter(lang) diff --git a/retrieval/services/typescript_textsplitter.py b/retrieval/services/typescript_textsplitter.py index 2b313df..baf8903 100644 --- a/retrieval/services/typescript_textsplitter.py +++ b/retrieval/services/typescript_textsplitter.py @@ -3,7 +3,7 @@ class TypescriptTextSplitter(RecursiveCharacterTextSplitter): separators = [ # typescript - "\type ", + "\ntype ", "\ninterface ", "\namespace ", # javascript diff --git a/retrieval/xtras/chat_utils.py b/retrieval/xtras/chat_utils.py index 5fdb459..2506692 100644 --- a/retrieval/xtras/chat_utils.py +++ b/retrieval/xtras/chat_utils.py @@ -3,15 +3,15 @@ import requests import os from secrets import DATABASE_INTERFACE_BEAR_TOKEN -from secrets import OPENAI_API_KEY import logging +base_url = os.environ.get("BASE_API_URL") or "http://0.0.0.0:8000" def query_database(query_prompt: str) -> Dict[str, Any]: """ Query vector database to retrieve chunk with user's input questions. """ - url = "http://0.0.0.0:8000/query" + url = "{base_url}/query" headers = { "Content-Type": "application/json", "accept": "application/json", diff --git a/retrieval/xtras/database_utils.py b/retrieval/xtras/database_utils.py index 32be187..c5f1c8e 100644 --- a/retrieval/xtras/database_utils.py +++ b/retrieval/xtras/database_utils.py @@ -10,6 +10,7 @@ parser.parse_rule_file(project_path.join('.gitignore')) +base_url = os.environ.get("BASE_API_URL") or "http://0.0.0.0:8000" SEARCH_TOP_K = 3 @@ -24,7 +25,7 @@ def upsert_file(directory: str): """ Upload all files under a directory to the vector database. """ - url = "http://0.0.0.0:8000/upsert-file" + url = "{base_url}/upsert-file" headers = {"Authorization": "Bearer " + DATABASE_INTERFACE_BEARER_TOKEN} files = [] for filename in os.listdir(directory): @@ -53,7 +54,7 @@ def upsert(id: str, content: str): """ Upload one piece of text to the database. """ - url = "http://0.0.0.0:8000/upsert" + url = "{base_url}/upsert" headers = { "accept": "application/json", "Content-Type": "application/json", @@ -78,7 +79,7 @@ def query_database(query_prompt: str) -> Dict[str, Any]: """ Query vector database to retrieve chunk with user's input question. """ - url = "http://0.0.0.0:8000/query" + url = "{base_url}/query" headers = { "Content-Type": "application/json", "accept": "application/json", diff --git a/src/typescript-textsplitter.ts b/src/typescript-textsplitter.ts index bce1686..2c3f584 100644 --- a/src/typescript-textsplitter.ts +++ b/src/typescript-textsplitter.ts @@ -3,7 +3,7 @@ import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; export class TypescriptTextSplitter { separators = [ // typescript - "\type ", + "\ntype ", "\ninterface ", "\namespace ", // javascript From 2302e6260caf2a9e44ec821c3062aece8caa7b46 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 13 Jun 2023 22:11:39 +0200 Subject: [PATCH 09/15] Update agent files --- README.md | 2 +- agent/agent-sync.js | 15 +++++++++------ agent/file-agent.js | 10 +++------- agent/run-agent | 4 +--- 4 files changed, 14 insertions(+), 17 deletions(-) mode change 100644 => 100755 agent/run-agent diff --git a/README.md b/README.md index 5f228e8..d8ec4a0 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,6 @@ We will need an agent running in the project root directory in the background to It should only take into account files that are "source files" of the project. To achieve this, we can use `.gitignore` or `.npmignore` files along with a custom configuration. - ### Chokidar agent with ignore files We can use [chokidar](https://github.com/paulmillr/chokidar) as a battle-tested agent to monitor the file system for changes using file, dir, glob, or array of files to match. @@ -167,6 +166,7 @@ The `file_agent.js` can be found in `src` and can be run simply via node: Copy the `agent` folder of this repo to the root of your project folder: - `file-agent.js` - `agent-sync.js` +- `run-agent` - `package.json` The programming language will be detected based on the [languagemap](https://github.com/blakeembrey/language-map/blob/main/languages.json) based on Github's [linguist yaml language file](https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml) diff --git a/agent/agent-sync.js b/agent/agent-sync.js index bf14d3f..60957a2 100644 --- a/agent/agent-sync.js +++ b/agent/agent-sync.js @@ -2,6 +2,10 @@ const path = require("path"); const axios = require("axios"); const baseApiUrl = process.env.BASE_API_URL || 'http://0.0.0.0:8000'; +const envProjPath = process.env.PROJECT_PATH; +const defaultPath = getPath.join(__dirname, '..'); +const projPath = envProjPath || defaultPath; + const requestUpsertFile = async (filePath, fileOpts = {}, headers = {}) => { try { const file = fs.createReadStream(filePath); @@ -70,12 +74,10 @@ const headers = { "Authorization": "Bearer " + accessToken // token }; -const projectPath = process.env.PROJECT_PATH; - async function upsertFile (filePath) { const opts = {}; - if (projectPath) { - const relativePath = path.relative(projectPath, filePath); + if (projPath) { + const relativePath = path.relative(projPath, filePath); opts = {relativePath}; } const response = await requestUpsertFile(filePath, opts, headers); @@ -89,5 +91,6 @@ async function deleteFile (filePath) { module.exports = { upsertFile, - deleteFile -} + deleteFile, + projPath +}; diff --git a/agent/file-agent.js b/agent/file-agent.js index 0aeca8a..0ede45d 100644 --- a/agent/file-agent.js +++ b/agent/file-agent.js @@ -1,9 +1,7 @@ - - const sync = require("./agent-sync"); const getPath = require("path"); const watcher = require('ignoring-watcher'); -const getProgrammingLanguage = requie("detect-programming-language"); +const getProgrammingLanguage = require("detect-programming-language"); function getMeta(path) { const ext = getPath.extname(path); @@ -96,11 +94,11 @@ function getMeta(path) { return meta; } - function startWatcher(dir) { + const ignoringWatcher = watcher.createWatcher({ // Directory to watch. Defaults to process.cwd() - dir: dir || getPath.join(__dirname, '..'), + dir: dir || sync.projPath, // Watch multiple directories instead of a single dir dirs: ['some/dir', 'another/dir'], @@ -161,8 +159,6 @@ function startWatcher(dir) { sync.deleteFile(filePath); }); - - ignoringWatcher.startWatching(); } diff --git a/agent/run-agent b/agent/run-agent old mode 100644 new mode 100755 index b8b2016..cc4d497 --- a/agent/run-agent +++ b/agent/run-agent @@ -1,6 +1,4 @@ #!/usr/bin/env node -const projPath = process.env.PROJECT_PATH; - const { startWatcher } = require("./file-agent"); - startWatcher(projPath); \ No newline at end of file + startWatcher(); \ No newline at end of file From eba68c5068e9c6ad00e69adf18230a82779d7af0 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Thu, 15 Jun 2023 20:07:43 +0200 Subject: [PATCH 10/15] Add basic ability to generate files from chat --- retrieval/xtras/ai.py | 39 ++ retrieval/xtras/chat_to_files.py | 26 ++ .../xtras/design/backend/database/supabase | 373 ++++++++++++++++++ .../xtras/design/backend/nextjs/auth/nextauth | 22 ++ .../xtras/design/backend/nextjs/productivity | 14 + .../xtras/design/backend/nextjs/ui-design | 2 + .../xtras/design/backend/nextjs/ui/next-ui | 15 + retrieval/xtras/design/backend/payment/stripe | 12 + .../xtras/design/frontend/web/react/ui-tools | 83 ++++ .../design/frontend/web/react/ui/ant-design | 7 + .../design/frontend/web/react/ui/bootstrap | 9 + .../design/frontend/web/react/ui/chakra-ui | 7 + .../design/frontend/web/react/ui/evergreen | 7 + .../xtras/design/frontend/web/react/ui/fluent | 11 + .../design/frontend/web/react/ui/grommet | 7 + .../design/frontend/web/react/ui/headless-ui | 7 + .../design/frontend/web/react/ui/mantine | 15 + .../design/frontend/web/react/ui/react-admin | 14 + .../design/frontend/web/react/ui/react-motion | 5 + .../frontend/web/react/ui/react-virtualized | 7 + .../xtras/design/frontend/web/react/ui/retool | 22 ++ .../design/frontend/web/react/ui/semantic-ui | 13 + retrieval/xtras/identity/philosophy | 5 + retrieval/xtras/identity/qa | 3 + retrieval/xtras/identity/setup | 15 + retrieval/xtras/identity/use_qa | 13 + retrieval/xtras/local_db.py | 28 ++ 27 files changed, 781 insertions(+) create mode 100644 retrieval/xtras/ai.py create mode 100644 retrieval/xtras/chat_to_files.py create mode 100644 retrieval/xtras/design/backend/database/supabase create mode 100644 retrieval/xtras/design/backend/nextjs/auth/nextauth create mode 100644 retrieval/xtras/design/backend/nextjs/productivity create mode 100644 retrieval/xtras/design/backend/nextjs/ui-design create mode 100644 retrieval/xtras/design/backend/nextjs/ui/next-ui create mode 100644 retrieval/xtras/design/backend/payment/stripe create mode 100644 retrieval/xtras/design/frontend/web/react/ui-tools create mode 100644 retrieval/xtras/design/frontend/web/react/ui/ant-design create mode 100644 retrieval/xtras/design/frontend/web/react/ui/bootstrap create mode 100644 retrieval/xtras/design/frontend/web/react/ui/chakra-ui create mode 100644 retrieval/xtras/design/frontend/web/react/ui/evergreen create mode 100644 retrieval/xtras/design/frontend/web/react/ui/fluent create mode 100644 retrieval/xtras/design/frontend/web/react/ui/grommet create mode 100644 retrieval/xtras/design/frontend/web/react/ui/headless-ui create mode 100644 retrieval/xtras/design/frontend/web/react/ui/mantine create mode 100644 retrieval/xtras/design/frontend/web/react/ui/react-admin create mode 100644 retrieval/xtras/design/frontend/web/react/ui/react-motion create mode 100644 retrieval/xtras/design/frontend/web/react/ui/react-virtualized create mode 100644 retrieval/xtras/design/frontend/web/react/ui/retool create mode 100644 retrieval/xtras/design/frontend/web/react/ui/semantic-ui create mode 100644 retrieval/xtras/identity/philosophy create mode 100644 retrieval/xtras/identity/qa create mode 100644 retrieval/xtras/identity/setup create mode 100644 retrieval/xtras/identity/use_qa create mode 100644 retrieval/xtras/local_db.py diff --git a/retrieval/xtras/ai.py b/retrieval/xtras/ai.py new file mode 100644 index 0000000..dc332cf --- /dev/null +++ b/retrieval/xtras/ai.py @@ -0,0 +1,39 @@ + +import openai + + +class AI: + def __init__(self, **kwargs): + self.kwargs = kwargs + + def start(self, system, user): + messages = [ + {"role": "system", "content": system}, + {"role": "user", "content": user}, + ] + + return self.next(messages) + + def fsystem(self, msg): + return {"role": "system", "content": msg} + + def fuser(self, msg): + return {"role": "user", "content": msg} + + def next(self, messages: list[dict[str, str]], prompt=None): + if prompt: + messages = messages + [{"role": "user", "content": prompt}] + + response = openai.ChatCompletion.create( + messages=messages, + stream=True, + **self.kwargs + ) + + chat = [] + for chunk in response: + delta = chunk['choices'][0]['delta'] + msg = delta.get('content', '') + print(msg, end="") + chat.append(msg) + return messages + [{"role": "assistant", "content": "".join(chat)}] \ No newline at end of file diff --git a/retrieval/xtras/chat_to_files.py b/retrieval/xtras/chat_to_files.py new file mode 100644 index 0000000..85a1dae --- /dev/null +++ b/retrieval/xtras/chat_to_files.py @@ -0,0 +1,26 @@ +import re + + +def parse_chat(chat):# -> List[Tuple[str, str]]: + # Get all ``` blocks + regex = r"```(.*?)```" + + matches = re.finditer(regex, chat, re.DOTALL) + + files = [] + for match in matches: + path = match.group(1).split("\n")[0] + # Get the code + code = match.group(1).split("\n")[1:] + code = "\n".join(code) + # Add the file to the list + files.append((path, code)) + + return files + +def to_files(chat, workspace): + workspace['all_output.txt'] = chat + + files = parse_chat(chat) + for file_name, file_content in files: + workspace[file_name] = file_content \ No newline at end of file diff --git a/retrieval/xtras/design/backend/database/supabase b/retrieval/xtras/design/backend/database/supabase new file mode 100644 index 0000000..4cb7655 --- /dev/null +++ b/retrieval/xtras/design/backend/database/supabase @@ -0,0 +1,373 @@ +Supabase is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools. + + - Hosted Postgres Database + - Authentication and Authorization. + - Auto-generated APIs + - REST + - GraphQL + - Realtime subscriptions. + - Functions. + - Database Functions + - Edge Functions + - File Storage + - AI + Vector/Embeddings Toolkit + - Dashboard + +Management API: +https://supabase.com/docs/reference/api/introduction + +Client: +Client library: supabase-js +Site: https://github.com/supabase/supabase-js + +Supabase SDK: + +Create new user: +https://supabase.com/docs/reference/javascript/auth-signup + +Sign in user: +https://supabase.com/docs/reference/javascript/auth-signinwithpassword + +Get user session: +https://supabase.com/docs/reference/javascript/auth-getsession + +Subscribe to channel: +https://supabase.com/docs/reference/javascript/subscribe + +List all buckets: +https://supabase.com/docs/reference/javascript/storage-listbuckets + +Create a signed url: +https://supabase.com/docs/reference/javascript/storage-from-createsignedurl + +Fetch data: +https://supabase.com/docs/reference/javascript/select + +Update data: +https://supabase.com/docs/reference/javascript/update + +Upsert data: +https://supabase.com/docs/reference/javascript/upsert + +Delete data: +https://supabase.com/docs/reference/javascript/delete + +Insert data: +https://supabase.com/docs/reference/javascript/insert + +Authentication# +All API requests require a Supabase Personal token to be included in the Authorization header: Authorization Bearer Date: Thu, 15 Jun 2023 21:05:59 +0200 Subject: [PATCH 11/15] AI strategies for entire client/server project --- .../xtras/design/backend/database/strategy | 14 ++++ .../xtras/design/backend/nextjs/strategy | 27 ++++++ .../xtras/design/backend/payment/strategy | 20 +++++ retrieval/xtras/design/backend/strategy | 50 +++++++++++ .../design/frontend/web/react/hooks/hook-libs | 83 +++++++++++++++++++ .../design/frontend/web/react/hooks/strategy | 16 ++++ .../web/react/state-management/state-libs | 20 +++++ .../web/react/state-management/strategy | 15 ++++ .../xtras/design/frontend/web/react/strategy | 10 +++ .../design/frontend/web/react/ui/strategy | 15 ++++ 10 files changed, 270 insertions(+) create mode 100644 retrieval/xtras/design/backend/database/strategy create mode 100644 retrieval/xtras/design/backend/nextjs/strategy create mode 100644 retrieval/xtras/design/backend/payment/strategy create mode 100644 retrieval/xtras/design/backend/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/hooks/hook-libs create mode 100644 retrieval/xtras/design/frontend/web/react/hooks/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/state-management/state-libs create mode 100644 retrieval/xtras/design/frontend/web/react/state-management/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/ui/strategy diff --git a/retrieval/xtras/design/backend/database/strategy b/retrieval/xtras/design/backend/database/strategy new file mode 100644 index 0000000..a521f7a --- /dev/null +++ b/retrieval/xtras/design/backend/database/strategy @@ -0,0 +1,14 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: +- What are the main real world entities that are to be managed by the application + +Determine which properties and types would be suitable for each table given the project description + +Please provide the code to: + +Create each table using PostGres SQL: + +create table: name +``` +``` diff --git a/retrieval/xtras/design/backend/nextjs/strategy b/retrieval/xtras/design/backend/nextjs/strategy new file mode 100644 index 0000000..b8fbd8d --- /dev/null +++ b/retrieval/xtras/design/backend/nextjs/strategy @@ -0,0 +1,27 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: +- Which nextjs project generator to use +- Which Cloud hosting provider to use +- Which external APIs to use +- Which Database to use +- Which Payment provider to use +- Which Authentication provider to use + +Leverage Blitz to the extent possible to simplify the project setup + +Please suggest a good choice for each based on information provided by the user. + +List the choices in the following manner: + +Backend project generator: +Name: +Command to execute generator with name of project: `insert command here` + +Nextjs supporting library: +Name: +Command to install backend library: `insert command here` + +If you believe multiple supporting libraries should be used, list all such libraries and a single install command which installs them all. + +Make sure the install commands also adds each library to the package.json file. \ No newline at end of file diff --git a/retrieval/xtras/design/backend/payment/strategy b/retrieval/xtras/design/backend/payment/strategy new file mode 100644 index 0000000..365b004 --- /dev/null +++ b/retrieval/xtras/design/backend/payment/strategy @@ -0,0 +1,20 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: +- Which payment options to support +- Which Subscriptions and plans to enable +- What to consult (file or database table) to determine costs for individual items + +Please provide the code to: + +Configure payment options: + +filename.ext +``` +``` + +Configure subscriptions and plans: + +filename.ext +``` +``` diff --git a/retrieval/xtras/design/backend/strategy b/retrieval/xtras/design/backend/strategy new file mode 100644 index 0000000..bbe49dc --- /dev/null +++ b/retrieval/xtras/design/backend/strategy @@ -0,0 +1,50 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: +- Which backend project creator to use +- Which backend library or framework to use +- Which Cloud hosting provider to use +- Which APIs to use +- Which Database to use +- Which Payment provider to use + +Please suggest a good choice for each based on information provided by the user. + +List the choices in the following manner: + +Backend project generator: +Name: +Command to execute generator with name of project: `insert command here` + +Backend library: +Name: +Command to install backend library: `insert command here` + +Cloud hosting provider: +Name: +Code for configuration of hosting provider information: + +path/filename.ext +``` +insert code here +``` + +Database: +Name: +Commands or code to setup database: + +Payment provider: +Name: +Code to setup payment provider account: + +path/filename.ext +``` +insert code here +``` + +Code to connect to payment provider account: + +path/filename.ext +``` +insert code here +``` diff --git a/retrieval/xtras/design/frontend/web/react/hooks/hook-libs b/retrieval/xtras/design/frontend/web/react/hooks/hook-libs new file mode 100644 index 0000000..3f0cac5 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/hooks/hook-libs @@ -0,0 +1,83 @@ +React Hooks Form: + +Npm name: react-hook-form + +React Hooks for form state management and validation (Web + React Native). It reduces the amount of code you need to write while removing unnecessary re-renders. + +Features +- Built with performance, UX, and DX in mind +- Embraces native HTML form validation +- Out-of-the-box integration with UI libraries +- Small size and no dependencies +- Support Yup, Zod, AJV, Superstruct, Joi, Vest, class-validator, io-ts, nope, and custom build + +React use: + +Npm name: react-use + +React use has a collection of essential React Hooks for managing devices sensors, UI controls, animations, side-effects, Lifecycle, State, and more + +React Query: + +Npm name: query + +Hooks for fetching, caching, and updating asynchronous data in React. It has declarative, always-up-to-date auto-managed queries and mutations that directly improve both your developer and user experiences + +Features +Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!) +Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime) +Parallel + Dependent Queries +Mutations + Reactive Query Refetching +Multi-layer Cache + Automatic Garbage Collection +Paginated + Cursor-based Queries +Load-More + Infinite Scroll Queries w/ Scroll Recovery +Request Cancellation +React Suspense + Fetch-As-You-Render Query Prefetching +Dedicated Devtools ... + +rehooks: + +Npm name: @rehooks/local-storage + +React hook for enabling synchronization with local storage. + +Features +Automatic JSON serialization +Synchronization across multiple tabs +Synchronization across multiple tabs +Type hinting via TypeScript + +useMedia: + +Npm name: use-media + +React sensor hook that tracks the state of a CSS media query. It takes a straightforward solution to the issue as media queries are extremely critical for the responsiveness of any application or website + +useFetch: + +Alias: useHttp + +Npm name: use-http + +It is an awesome package that is used as the replacement of Fetch API + +Features +- SSR (server-side rendering) support +- TypeScript support +- 2 dependencies (use-ssr, urs) +- GraphQL support (queries + mutations) +- Provider to set default URL and options +- Request/response interceptors +- React Native support +- Aborts/Cancels pending HTTP requests when a component unmounts +- Built in caching +- Persistent caching support +- Suspense(experimental) support +- Retry functionality + +React Hanger: + +Npm name: react-hanger + +Set of helpful hooks, for specific to some primitives types state changing helpers +react-hanger is a library that has some React Hooks that let us more easily manage various kinds of states. Here are some following Hooks: useInput useBoolean useNumber useArray useOnMount useOnUnmount and many more... \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/hooks/strategy b/retrieval/xtras/design/frontend/web/react/hooks/strategy new file mode 100644 index 0000000..77e05cf --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/hooks/strategy @@ -0,0 +1,16 @@ +Explain the main design philosophy and benefits of each Hooks library and the pros and cons +of using it. Determine whether it would be a good fit for the project and give it a score of 1-10 across the following dimensions. + +- simplicity +- scalability +- suitability +- popularity +- modern technology use +- overall + +Provide the command to install the latest stable package version of the library. + +Install package command: `insert command here` + +If you believe multiple Hook libraries should be used, list up to 3 such libraries and a single install command which installs them all. + diff --git a/retrieval/xtras/design/frontend/web/react/state-management/state-libs b/retrieval/xtras/design/frontend/web/react/state-management/state-libs new file mode 100644 index 0000000..290bc4b --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/state-management/state-libs @@ -0,0 +1,20 @@ +React redux: +Package name: react-redux + +Zustand: +Package name: zustand + +Mobx: +Package name: mobx + +Jotai: +Package name: jotai + +Recoil: +Package name: recoil + +Mobx State Tree: +Package name: mobx-state-tree + +Unstated: +Package name: unstated diff --git a/retrieval/xtras/design/frontend/web/react/state-management/strategy b/retrieval/xtras/design/frontend/web/react/state-management/strategy new file mode 100644 index 0000000..8dc6f6a --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/state-management/strategy @@ -0,0 +1,15 @@ +Explain the main design philosophy of each State management library and the pros and cons +of using it. Determine whether it would be a good fit for the project and give it a score of 1-10 across the following dimensions. + +- simplicity +- scalability +- suitability +- popularity +- modern technology use +- overall + +Provide the command to install the latest stable package version of the library. + +Install package command: `insert command here` + + diff --git a/retrieval/xtras/design/frontend/web/react/strategy b/retrieval/xtras/design/frontend/web/react/strategy new file mode 100644 index 0000000..62a7f2b --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/strategy @@ -0,0 +1,10 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: + +- Which project creator to use +- Which UI library or framework to use +- Which Hosting provider to use +- Which hooks to use +- Which State management libraries to use +- Which APIs to use diff --git a/retrieval/xtras/design/frontend/web/react/ui/strategy b/retrieval/xtras/design/frontend/web/react/ui/strategy new file mode 100644 index 0000000..fef0e41 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/ui/strategy @@ -0,0 +1,15 @@ +Explain the main design philosophy of each UI/UX library and the pros and cons +of using it. Determine whether it would be a good fit for the project and give it a score of 1-10 across the following dimensions. + +- simplicity +- scalability +- suitability +- popularity +- modern technology use +- overall + +Provide the command to install the latest stable package version of the library. + +Install package command: `insert command here` + +If you believe multiple UI/UX libraries should be used, list up to 3 such libraries and a single install command which installs them all. From c45b01f363bf5dcccc798f0ad6d677859ef7ba1d Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Thu, 15 Jun 2023 21:45:22 +0200 Subject: [PATCH 12/15] More strategies for complete app design --- .../xtras/design/backend/nextjs/auth/strategy | 10 +++ .../design/backend/nextjs/blitz/strategy | 1 + .../backend/nextjs/{productivity => libs} | 0 .../backend/nextjs/{ui-design => ui/libs} | 0 .../xtras/design/backend/nextjs/ui/next-ui | 2 +- .../xtras/design/backend/nextjs/ui/strategy | 15 ++++ retrieval/xtras/design/backend/payment/stripe | 1 + .../design/frontend/architecture/strategy | 71 +++++++++++++++++++ .../frontend/web/react/test/api-mocking/libs | 2 + .../web/react/test/api-mocking/strategy | 5 ++ .../web/react/test/component-tests/libs | 11 +++ .../web/react/test/component-tests/strategy | 14 ++++ .../frontend/web/react/test/stories/libs | 35 +++++++++ .../frontend/web/react/test/stories/strategy | 7 ++ .../design/frontend/web/react/test/strategy | 9 +++ .../frontend/web/react/test/unit-testing/libs | 2 + .../web/react/test/unit-testing/strategy | 5 ++ 17 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 retrieval/xtras/design/backend/nextjs/auth/strategy create mode 100644 retrieval/xtras/design/backend/nextjs/blitz/strategy rename retrieval/xtras/design/backend/nextjs/{productivity => libs} (100%) rename retrieval/xtras/design/backend/nextjs/{ui-design => ui/libs} (100%) create mode 100644 retrieval/xtras/design/backend/nextjs/ui/strategy create mode 100644 retrieval/xtras/design/frontend/architecture/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/test/api-mocking/libs create mode 100644 retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/test/component-tests/libs create mode 100644 retrieval/xtras/design/frontend/web/react/test/component-tests/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/test/stories/libs create mode 100644 retrieval/xtras/design/frontend/web/react/test/stories/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/test/strategy create mode 100644 retrieval/xtras/design/frontend/web/react/test/unit-testing/libs create mode 100644 retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy diff --git a/retrieval/xtras/design/backend/nextjs/auth/strategy b/retrieval/xtras/design/backend/nextjs/auth/strategy new file mode 100644 index 0000000..bb8e7d3 --- /dev/null +++ b/retrieval/xtras/design/backend/nextjs/auth/strategy @@ -0,0 +1,10 @@ +Determine and decide the Auth strategies to be supported and which backends are to be used. +Ask clarifying questions. + +When you have sufficient information generate the auth code as follows: + +One main authentication file + +Supporting auth files for each type of authentication supported + +Supporting files for backends as needed. \ No newline at end of file diff --git a/retrieval/xtras/design/backend/nextjs/blitz/strategy b/retrieval/xtras/design/backend/nextjs/blitz/strategy new file mode 100644 index 0000000..30404ce --- /dev/null +++ b/retrieval/xtras/design/backend/nextjs/blitz/strategy @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/retrieval/xtras/design/backend/nextjs/productivity b/retrieval/xtras/design/backend/nextjs/libs similarity index 100% rename from retrieval/xtras/design/backend/nextjs/productivity rename to retrieval/xtras/design/backend/nextjs/libs diff --git a/retrieval/xtras/design/backend/nextjs/ui-design b/retrieval/xtras/design/backend/nextjs/ui/libs similarity index 100% rename from retrieval/xtras/design/backend/nextjs/ui-design rename to retrieval/xtras/design/backend/nextjs/ui/libs diff --git a/retrieval/xtras/design/backend/nextjs/ui/next-ui b/retrieval/xtras/design/backend/nextjs/ui/next-ui index 21fac1c..64cf08f 100644 --- a/retrieval/xtras/design/backend/nextjs/ui/next-ui +++ b/retrieval/xtras/design/backend/nextjs/ui/next-ui @@ -1,4 +1,4 @@ -Features and benefits include: +Features and benefits of NextUI include: Faster: NextUI eliminates unwanted style props during runtime. This makes the tool better performing than other React UI libraries. diff --git a/retrieval/xtras/design/backend/nextjs/ui/strategy b/retrieval/xtras/design/backend/nextjs/ui/strategy new file mode 100644 index 0000000..fef0e41 --- /dev/null +++ b/retrieval/xtras/design/backend/nextjs/ui/strategy @@ -0,0 +1,15 @@ +Explain the main design philosophy of each UI/UX library and the pros and cons +of using it. Determine whether it would be a good fit for the project and give it a score of 1-10 across the following dimensions. + +- simplicity +- scalability +- suitability +- popularity +- modern technology use +- overall + +Provide the command to install the latest stable package version of the library. + +Install package command: `insert command here` + +If you believe multiple UI/UX libraries should be used, list up to 3 such libraries and a single install command which installs them all. diff --git a/retrieval/xtras/design/backend/payment/stripe b/retrieval/xtras/design/backend/payment/stripe index e5302b0..221e94e 100644 --- a/retrieval/xtras/design/backend/payment/stripe +++ b/retrieval/xtras/design/backend/payment/stripe @@ -1,3 +1,4 @@ +Stripe: Stripe is one of the prime payment gateways. It enables businesses and individuals to accept payments using their rich API and robust platform. Stripe makes it easier for business owners and vendors to start and manage their internet businesses by providing various payment solutions. Features diff --git a/retrieval/xtras/design/frontend/architecture/strategy b/retrieval/xtras/design/frontend/architecture/strategy new file mode 100644 index 0000000..d136d86 --- /dev/null +++ b/retrieval/xtras/design/frontend/architecture/strategy @@ -0,0 +1,71 @@ +Ask clarifying questions to determine the best overall architecture. + +Consider suitable architectures for: + +- State management +- User Interfaces +- API calls +- Security +- ... + +UI: +For the UI, ask clarifying questions in a top down manner. + +First determine how the user should enter the main home screen +Should there be a splash screen or fancy animation before home screen is displayed + +Home screen: +Ask clarifying questions to determine the general layout of the home screen. +Determine how much of this layout should be reused across other screens. + +Determine the main areas of the home screen and the use cases it should enable. +Each use case should be either directly accessible from the home screen and by navigation from the home screen. + +Determine the: + +- main menu structure and if the menu should be at the left or top. +- main content area +- side bars +- bottom area + +Ask clarifying questions to determine what should be presented in each main area +For each navigation option ask what screen should be presented +then proceed to break down that screen in terms of layout and what should be presented + +Continue this until all navigation paths and actions have been completed. + +For each area of the screen covered, generate a display folder with the following: + +folder: name + +Display Component + +name/component.tsx +``` +code +``` + +Component unit test + +name/component.spec.tsx +``` +code +``` + +State management + +name/state.ts +``` +code +``` + +Ensure the folder hierarchy reflects the logical hierarchy of the navigation from the main page. Build the UI and UX using the components from the chosen UI libraries where possible. +Use hooks to manage state, user input and API communication. + +Try to keep functions small (no more than 50 lines) and ensure they adhere to best practices such as Single Responsibility. + + + + + + diff --git a/retrieval/xtras/design/frontend/web/react/test/api-mocking/libs b/retrieval/xtras/design/frontend/web/react/test/api-mocking/libs new file mode 100644 index 0000000..73d5447 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/api-mocking/libs @@ -0,0 +1,2 @@ +Mock Service Worker: +Mock by intercepting requests on the network level. Seamlessly reuse the same mock definition for testing, development, and debugging. \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy b/retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy new file mode 100644 index 0000000..ca48178 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy @@ -0,0 +1,5 @@ +Provide the command to install the latest stable package version of each API mocking library. + +Install package command: `insert command here` + +If you believe multiple libraries should be used, list up to 3 such libraries and a single install command which installs them all. diff --git a/retrieval/xtras/design/frontend/web/react/test/component-tests/libs b/retrieval/xtras/design/frontend/web/react/test/component-tests/libs new file mode 100644 index 0000000..c492c40 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/component-tests/libs @@ -0,0 +1,11 @@ +React Testing Library: + +The React Testing Library is a very light-weight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is: + +The more your tests resemble the way your software is used, the more confidence they can give you. + +So rather than dealing with instances of rendered React components, your tests will work with actual DOM nodes. The utilities this library provides facilitate querying the DOM in the same way the user would. Finding form elements by their label text (just like a user would), finding links and buttons from their text (like a user would). It also exposes a recommended way to find elements by a data-testid as an "escape hatch" for elements where the text content and label do not make sense or is not practical. + +This library encourages your applications to be more accessible and allows you to get your tests closer to using your components the way a user will, which allows your tests to give you more confidence that your application will work when a real user uses it. + +Package name: @testing-library/react \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/test/component-tests/strategy b/retrieval/xtras/design/frontend/web/react/test/component-tests/strategy new file mode 100644 index 0000000..7e42068 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/component-tests/strategy @@ -0,0 +1,14 @@ +Explain the main design philosophy of the most popular (up to 3) component testing libraries and the pros and cons of using each. Determine whether it would be a good fit for the project and give each library a score of 1-10 across the following dimensions. + +- simplicity +- scalability +- suitability +- popularity +- modern technology use +- overall + +Provide the command to install the latest stable package version of the library. + +Install package command: `insert command here` + +If you believe multiple libraries should be used, list up to 3 such libraries and a single install command which installs them all. diff --git a/retrieval/xtras/design/frontend/web/react/test/stories/libs b/retrieval/xtras/design/frontend/web/react/test/stories/libs new file mode 100644 index 0000000..781b5d1 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/stories/libs @@ -0,0 +1,35 @@ +Storybook: +Every piece of UI is now a component. The superpower of components is that you don't need to spin up the whole app just to see how they render. You can render a specific variation in isolation by passing in props, mocking data, or faking events. + +Storybook is packaged as a small, development-only, workshop that lives alongside your app. It provides an isolated iframe to render components without interference from app business logic and context. That helps you focus development on each variation of a component, even the hard-to-reach edge cases. + +Capture UI variations as “stories”: +When developing a component variation in isolation, save it as a story. Stories are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. + +You write stories for granular UI component variation and then use those stories in development, testing, and documentation. + +Storybook keeps track of every story: +Storybook is an interactive directory of your UI components and their stories. In the past, you'd have to spin up the app, navigate to a page, and contort the UI into the right state. This is a huge waste of time and bogs down frontend development. With Storybook, you can skip all those steps and jump straight to working on a UI component in a specific state. + +Benefits: +When you write stories for components, you get a bunch of additional benefits for free. + +📝 Develop UIs that are more durable: +Isolate components and pages and track their use cases as stories. Verify hard-to-reach edge cases of UI. Use addons to mock everything a component needs—context, API requests, device features, etc. + +✅ Test UIs with less effort and no flakes +Stories are a pragmatic, reproducible way of tracking UI states. Use them to spot-test the UI during development. Storybook offers built-in workflows for automated Accessibility, Interaction, and Visual testing. Or use stories as test cases by importing them into other JavaScript testing tools. + +📚 Document UI for your team to reuse: +Storybook is the single source of truth for your UI. Stories index all your components and their various states, making it easy for your team to find and reuse existing UI patterns. Storybook also auto-generates documentation from those stories. + +📤 Share how the UI actually works: +Stories show how UIs actually work, not just a picture of how they're supposed to work. That keeps everyone aligned on what's currently in production. Publish Storybook to get sign-off from teammates. Or embed them in wikis, Markdown, and Figma to streamline collaboration. + +🚦Automate UI workflows: +Storybook is compatible with your continuous integration workflow. Add it as a CI step to automate user interface testing, review implementation with teammates, and get signoff from stakeholders. + +Write stories once, reuse everywhere: +Storybook is powered by Component Story Format, an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. + +Reuse stories with Jest and Testing Library to verify interactions. Put them in Chromatic for visual testing. Audit story accessibility with Axe. Or test user flows with Playwright and Cypress. Reuse unlocks more workflows at no extra cost. \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/test/stories/strategy b/retrieval/xtras/design/frontend/web/react/test/stories/strategy new file mode 100644 index 0000000..6b981ba --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/stories/strategy @@ -0,0 +1,7 @@ +Provide the command to install the latest stable package version of storybook. + +Install package command: `insert command here` + +If you believe supporting libraries should be used, list up to 3 such libraries and a single install command which installs them all. + +Install supporting libraries command: `insert command here` \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/test/strategy b/retrieval/xtras/design/frontend/web/react/test/strategy new file mode 100644 index 0000000..977dffd --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/strategy @@ -0,0 +1,9 @@ +First set up the project and identify the main libraries and tools to use. + +Ask the user for: + +- Which component testing library to use +- Which user stories framework to use +- Which unit testing library to use +- Which general mocking library to use +- Which API mocking library to use diff --git a/retrieval/xtras/design/frontend/web/react/test/unit-testing/libs b/retrieval/xtras/design/frontend/web/react/test/unit-testing/libs new file mode 100644 index 0000000..7cabf4d --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/unit-testing/libs @@ -0,0 +1,2 @@ +Jest: +Jest is a JavaScript test runner that lets you access the DOM via jsdom. While jsdom is only an approximation of how the browser works, it is often good enough for testing React components. Jest provides a great iteration speed combined with powerful features like mocking modules and timers so you can have more control over how the code executes. \ No newline at end of file diff --git a/retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy b/retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy new file mode 100644 index 0000000..189c202 --- /dev/null +++ b/retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy @@ -0,0 +1,5 @@ +Provide the command to install the latest stable package version of each frontend Unit testing library. + +Install package command: `insert command here` + +If you believe multiple libraries should be used, list up to 3 such libraries and a single install command which installs them all. From ef3a64a60730813f108fea7978b66c31284cdf89 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Thu, 15 Jun 2023 22:13:04 +0200 Subject: [PATCH 13/15] Divide and conquer using AI team --- .../backend/database/strategy | 0 .../backend/database/supabase | 0 .../backend/nextjs/auth/nextauth | 0 .../backend/nextjs/auth/strategy | 0 .../backend/nextjs/blitz/strategy | 0 .../design => ai-team}/backend/nextjs/libs | 0 .../backend/nextjs/strategy | 0 .../design => ai-team}/backend/nextjs/ui/libs | 0 .../backend/nextjs/ui/next-ui | 0 .../backend/nextjs/ui/strategy | 0 .../backend/payment/strategy | 0 .../design => ai-team}/backend/payment/stripe | 0 .../xtras/design => ai-team}/backend/strategy | 0 .../frontend/architecture/strategy | 0 .../frontend/web/react/hooks/hook-libs | 0 .../frontend/web/react/hooks/strategy | 0 .../web/react/state-management/state-libs | 0 .../web/react/state-management/strategy | 0 .../frontend/web/react/strategy | 0 .../frontend/web/react/test/api-mocking/libs | 0 .../web/react/test/api-mocking/strategy | 0 .../web/react/test/component-tests/libs | 0 .../web/react/test/component-tests/strategy | 0 .../frontend/web/react/test/stories/libs | 0 .../frontend/web/react/test/stories/strategy | 0 .../frontend/web/react/test/strategy | 0 .../frontend/web/react/test/unit-testing/libs | 0 .../web/react/test/unit-testing/strategy | 0 .../frontend/web/react/ui-tools | 0 .../frontend/web/react/ui/ant-design | 0 .../frontend/web/react/ui/bootstrap | 0 .../frontend/web/react/ui/chakra-ui | 0 .../frontend/web/react/ui/evergreen | 0 .../frontend/web/react/ui/fluent | 0 .../frontend/web/react/ui/grommet | 0 .../frontend/web/react/ui/headless-ui | 0 .../frontend/web/react/ui/mantine | 0 .../frontend/web/react/ui/react-admin | 0 .../frontend/web/react/ui/react-motion | 0 .../frontend/web/react/ui/react-virtualized | 0 .../frontend/web/react/ui/retool | 0 .../frontend/web/react/ui/semantic-ui | 0 .../frontend/web/react/ui/strategy | 0 ai-team/strategy | 57 +++++++++++++++++++ 44 files changed, 57 insertions(+) rename {retrieval/xtras/design => ai-team}/backend/database/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/database/supabase (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/auth/nextauth (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/auth/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/blitz/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/libs (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/ui/libs (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/ui/next-ui (100%) rename {retrieval/xtras/design => ai-team}/backend/nextjs/ui/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/payment/strategy (100%) rename {retrieval/xtras/design => ai-team}/backend/payment/stripe (100%) rename {retrieval/xtras/design => ai-team}/backend/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/architecture/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/hooks/hook-libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/hooks/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/state-management/state-libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/state-management/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/api-mocking/libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/api-mocking/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/component-tests/libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/component-tests/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/stories/libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/stories/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/unit-testing/libs (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/test/unit-testing/strategy (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui-tools (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/ant-design (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/bootstrap (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/chakra-ui (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/evergreen (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/fluent (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/grommet (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/headless-ui (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/mantine (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/react-admin (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/react-motion (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/react-virtualized (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/retool (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/semantic-ui (100%) rename {retrieval/xtras/design => ai-team}/frontend/web/react/ui/strategy (100%) create mode 100644 ai-team/strategy diff --git a/retrieval/xtras/design/backend/database/strategy b/ai-team/backend/database/strategy similarity index 100% rename from retrieval/xtras/design/backend/database/strategy rename to ai-team/backend/database/strategy diff --git a/retrieval/xtras/design/backend/database/supabase b/ai-team/backend/database/supabase similarity index 100% rename from retrieval/xtras/design/backend/database/supabase rename to ai-team/backend/database/supabase diff --git a/retrieval/xtras/design/backend/nextjs/auth/nextauth b/ai-team/backend/nextjs/auth/nextauth similarity index 100% rename from retrieval/xtras/design/backend/nextjs/auth/nextauth rename to ai-team/backend/nextjs/auth/nextauth diff --git a/retrieval/xtras/design/backend/nextjs/auth/strategy b/ai-team/backend/nextjs/auth/strategy similarity index 100% rename from retrieval/xtras/design/backend/nextjs/auth/strategy rename to ai-team/backend/nextjs/auth/strategy diff --git a/retrieval/xtras/design/backend/nextjs/blitz/strategy b/ai-team/backend/nextjs/blitz/strategy similarity index 100% rename from retrieval/xtras/design/backend/nextjs/blitz/strategy rename to ai-team/backend/nextjs/blitz/strategy diff --git a/retrieval/xtras/design/backend/nextjs/libs b/ai-team/backend/nextjs/libs similarity index 100% rename from retrieval/xtras/design/backend/nextjs/libs rename to ai-team/backend/nextjs/libs diff --git a/retrieval/xtras/design/backend/nextjs/strategy b/ai-team/backend/nextjs/strategy similarity index 100% rename from retrieval/xtras/design/backend/nextjs/strategy rename to ai-team/backend/nextjs/strategy diff --git a/retrieval/xtras/design/backend/nextjs/ui/libs b/ai-team/backend/nextjs/ui/libs similarity index 100% rename from retrieval/xtras/design/backend/nextjs/ui/libs rename to ai-team/backend/nextjs/ui/libs diff --git a/retrieval/xtras/design/backend/nextjs/ui/next-ui b/ai-team/backend/nextjs/ui/next-ui similarity index 100% rename from retrieval/xtras/design/backend/nextjs/ui/next-ui rename to ai-team/backend/nextjs/ui/next-ui diff --git a/retrieval/xtras/design/backend/nextjs/ui/strategy b/ai-team/backend/nextjs/ui/strategy similarity index 100% rename from retrieval/xtras/design/backend/nextjs/ui/strategy rename to ai-team/backend/nextjs/ui/strategy diff --git a/retrieval/xtras/design/backend/payment/strategy b/ai-team/backend/payment/strategy similarity index 100% rename from retrieval/xtras/design/backend/payment/strategy rename to ai-team/backend/payment/strategy diff --git a/retrieval/xtras/design/backend/payment/stripe b/ai-team/backend/payment/stripe similarity index 100% rename from retrieval/xtras/design/backend/payment/stripe rename to ai-team/backend/payment/stripe diff --git a/retrieval/xtras/design/backend/strategy b/ai-team/backend/strategy similarity index 100% rename from retrieval/xtras/design/backend/strategy rename to ai-team/backend/strategy diff --git a/retrieval/xtras/design/frontend/architecture/strategy b/ai-team/frontend/architecture/strategy similarity index 100% rename from retrieval/xtras/design/frontend/architecture/strategy rename to ai-team/frontend/architecture/strategy diff --git a/retrieval/xtras/design/frontend/web/react/hooks/hook-libs b/ai-team/frontend/web/react/hooks/hook-libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/hooks/hook-libs rename to ai-team/frontend/web/react/hooks/hook-libs diff --git a/retrieval/xtras/design/frontend/web/react/hooks/strategy b/ai-team/frontend/web/react/hooks/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/hooks/strategy rename to ai-team/frontend/web/react/hooks/strategy diff --git a/retrieval/xtras/design/frontend/web/react/state-management/state-libs b/ai-team/frontend/web/react/state-management/state-libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/state-management/state-libs rename to ai-team/frontend/web/react/state-management/state-libs diff --git a/retrieval/xtras/design/frontend/web/react/state-management/strategy b/ai-team/frontend/web/react/state-management/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/state-management/strategy rename to ai-team/frontend/web/react/state-management/strategy diff --git a/retrieval/xtras/design/frontend/web/react/strategy b/ai-team/frontend/web/react/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/strategy rename to ai-team/frontend/web/react/strategy diff --git a/retrieval/xtras/design/frontend/web/react/test/api-mocking/libs b/ai-team/frontend/web/react/test/api-mocking/libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/api-mocking/libs rename to ai-team/frontend/web/react/test/api-mocking/libs diff --git a/retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy b/ai-team/frontend/web/react/test/api-mocking/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/api-mocking/strategy rename to ai-team/frontend/web/react/test/api-mocking/strategy diff --git a/retrieval/xtras/design/frontend/web/react/test/component-tests/libs b/ai-team/frontend/web/react/test/component-tests/libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/component-tests/libs rename to ai-team/frontend/web/react/test/component-tests/libs diff --git a/retrieval/xtras/design/frontend/web/react/test/component-tests/strategy b/ai-team/frontend/web/react/test/component-tests/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/component-tests/strategy rename to ai-team/frontend/web/react/test/component-tests/strategy diff --git a/retrieval/xtras/design/frontend/web/react/test/stories/libs b/ai-team/frontend/web/react/test/stories/libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/stories/libs rename to ai-team/frontend/web/react/test/stories/libs diff --git a/retrieval/xtras/design/frontend/web/react/test/stories/strategy b/ai-team/frontend/web/react/test/stories/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/stories/strategy rename to ai-team/frontend/web/react/test/stories/strategy diff --git a/retrieval/xtras/design/frontend/web/react/test/strategy b/ai-team/frontend/web/react/test/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/strategy rename to ai-team/frontend/web/react/test/strategy diff --git a/retrieval/xtras/design/frontend/web/react/test/unit-testing/libs b/ai-team/frontend/web/react/test/unit-testing/libs similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/unit-testing/libs rename to ai-team/frontend/web/react/test/unit-testing/libs diff --git a/retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy b/ai-team/frontend/web/react/test/unit-testing/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/test/unit-testing/strategy rename to ai-team/frontend/web/react/test/unit-testing/strategy diff --git a/retrieval/xtras/design/frontend/web/react/ui-tools b/ai-team/frontend/web/react/ui-tools similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui-tools rename to ai-team/frontend/web/react/ui-tools diff --git a/retrieval/xtras/design/frontend/web/react/ui/ant-design b/ai-team/frontend/web/react/ui/ant-design similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/ant-design rename to ai-team/frontend/web/react/ui/ant-design diff --git a/retrieval/xtras/design/frontend/web/react/ui/bootstrap b/ai-team/frontend/web/react/ui/bootstrap similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/bootstrap rename to ai-team/frontend/web/react/ui/bootstrap diff --git a/retrieval/xtras/design/frontend/web/react/ui/chakra-ui b/ai-team/frontend/web/react/ui/chakra-ui similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/chakra-ui rename to ai-team/frontend/web/react/ui/chakra-ui diff --git a/retrieval/xtras/design/frontend/web/react/ui/evergreen b/ai-team/frontend/web/react/ui/evergreen similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/evergreen rename to ai-team/frontend/web/react/ui/evergreen diff --git a/retrieval/xtras/design/frontend/web/react/ui/fluent b/ai-team/frontend/web/react/ui/fluent similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/fluent rename to ai-team/frontend/web/react/ui/fluent diff --git a/retrieval/xtras/design/frontend/web/react/ui/grommet b/ai-team/frontend/web/react/ui/grommet similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/grommet rename to ai-team/frontend/web/react/ui/grommet diff --git a/retrieval/xtras/design/frontend/web/react/ui/headless-ui b/ai-team/frontend/web/react/ui/headless-ui similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/headless-ui rename to ai-team/frontend/web/react/ui/headless-ui diff --git a/retrieval/xtras/design/frontend/web/react/ui/mantine b/ai-team/frontend/web/react/ui/mantine similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/mantine rename to ai-team/frontend/web/react/ui/mantine diff --git a/retrieval/xtras/design/frontend/web/react/ui/react-admin b/ai-team/frontend/web/react/ui/react-admin similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/react-admin rename to ai-team/frontend/web/react/ui/react-admin diff --git a/retrieval/xtras/design/frontend/web/react/ui/react-motion b/ai-team/frontend/web/react/ui/react-motion similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/react-motion rename to ai-team/frontend/web/react/ui/react-motion diff --git a/retrieval/xtras/design/frontend/web/react/ui/react-virtualized b/ai-team/frontend/web/react/ui/react-virtualized similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/react-virtualized rename to ai-team/frontend/web/react/ui/react-virtualized diff --git a/retrieval/xtras/design/frontend/web/react/ui/retool b/ai-team/frontend/web/react/ui/retool similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/retool rename to ai-team/frontend/web/react/ui/retool diff --git a/retrieval/xtras/design/frontend/web/react/ui/semantic-ui b/ai-team/frontend/web/react/ui/semantic-ui similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/semantic-ui rename to ai-team/frontend/web/react/ui/semantic-ui diff --git a/retrieval/xtras/design/frontend/web/react/ui/strategy b/ai-team/frontend/web/react/ui/strategy similarity index 100% rename from retrieval/xtras/design/frontend/web/react/ui/strategy rename to ai-team/frontend/web/react/ui/strategy diff --git a/ai-team/strategy b/ai-team/strategy new file mode 100644 index 0000000..39b7dd0 --- /dev/null +++ b/ai-team/strategy @@ -0,0 +1,57 @@ +The AI dev team will consist of: + +- Architecture team +- Backend dev team +- Frontend dev team +- Devops team +- Project Management team + +To initiate the project, the end user will supply a folder with a requirements specification in a specific format that the AI can parse and understand clearly + +The AI team should then partition the requirements spec into relevant tasks and responsibilities for each team. + +Each team should start to clarify anything that is unclear by asking the user for further information. + +The team should then proceed to break down the overall requirements into smaller tasks that are further refined. + +When the overall requirement are clear, the team should start by determining the general project infrastructure: + +Create basic project structure: +Determine the overall type of project such as: +- CLI +- Simple client app +- Fullstack Client/Server app +- Run locally or needs Cloud infrastructure +- External services needed +- Payment gateways +- Security and Authentication +- Keys and tokens needed for the services + +Based on these findings the Architect team should: + +- Generate initial project structure +- Use app generators to generate the initial skeleton app for each part of the application needed + +Examples of app generators: +- azd Azure Developer CLI - generates an app based on Azure app template +- create-react-app +- nextjs generator +- ... + +When the initial skeleton project has been set up, the requirements should then be delegated to each team to work on + +Each dev team will have: +- Implementer (think Junior developer) who does the base level coding +- Overseer (think Tech lead or senior developer) who oversees and suggests improvements +- Tester who writes and maintains tests +- Documentation writer who writes and maintains documentation both in code and external docs, including diagrams + +Architecture team will oversee overall technical direction + +Project team will oversee that what is developed matches requirements + +Additional Frontend team roles: +- Story writer who maintains storybook tests +- E2E tester who writes tests simulating end user + + From 463e6f2cb73d939d71f73b2d2a09e8663f1148b3 Mon Sep 17 00:00:00 2001 From: kristianmandrup Date: Tue, 20 Jun 2023 22:13:44 +0200 Subject: [PATCH 14/15] more prompt engineering experiments --- Diagrams.md | 38 + ai-team/api/strategy | 1 + .../architecture/diagrams/C4-architecture.md | 112 + .../architecture/diagrams/cafeteria-arch.dsl | 56 + ai-team/architecture/diagrams/cafeteria.mmd | 61 + ai-team/architecture/diagrams/mermaid.md | 274 +++ ai-team/backend/data-model.md | 186 ++ ai-team/backend/database/prisma-schema.md | 174 ++ .../backend/database/repository-classes.md | 71 + ai-team/backend/database/tRPC-zod-API.md | 73 + ai-team/backend/nextjs/client-api.md | 44 + .../backend/nextjs/react-hook-form-pages.md | 74 + ai-team/backend/nextjs/react-query-pages.md | 90 + ai-team/backend/payment/write-stripe-code.md | 120 ++ ai-team/frontend/strategy | 1 + ai-team/marketing/name | 17 + ai-team/marketing/strategy | 0 ai-team/product/proposal.md | 84 + ai-team/ui-ux/accessibility.md | 124 ++ ai-team/ui-ux/alerts.md | 144 ++ ai-team/ui-ux/buttons.md | 72 + ai-team/ui-ux/cards.md | 106 + ai-team/ui-ux/chat-xp | 1805 +++++++++++++++++ .../data-visualizations/charts-graphs.md | 293 +++ ai-team/ui-ux/data-visualizations/tables.md | 57 + ai-team/ui-ux/design-system-items.md | 34 + ai-team/ui-ux/design-system.md | 873 ++++++++ ai-team/ui-ux/design.md | 1061 ++++++++++ ai-team/ui-ux/folder-layout.md | 77 + ai-team/ui-ux/forms.md | 81 + ai-team/ui-ux/grid-system.md | 204 ++ ai-team/ui-ux/iconography.md | 25 + ai-team/ui-ux/layout-components.md | 83 + ai-team/ui-ux/logo | 22 + ai-team/ui-ux/modals.md | 106 + ai-team/ui-ux/navigation-patterns.md | 247 +++ ai-team/ui-ux/strategy | 0 ai-team/ui-ux/style-guide.md | 56 + ai-team/ui-ux/typography.md | 248 +++ ai-team/use-cases.md | 66 + 40 files changed, 7260 insertions(+) create mode 100644 Diagrams.md create mode 100644 ai-team/api/strategy create mode 100644 ai-team/architecture/diagrams/C4-architecture.md create mode 100644 ai-team/architecture/diagrams/cafeteria-arch.dsl create mode 100644 ai-team/architecture/diagrams/cafeteria.mmd create mode 100644 ai-team/architecture/diagrams/mermaid.md create mode 100644 ai-team/backend/data-model.md create mode 100644 ai-team/backend/database/prisma-schema.md create mode 100644 ai-team/backend/database/repository-classes.md create mode 100644 ai-team/backend/database/tRPC-zod-API.md create mode 100644 ai-team/backend/nextjs/client-api.md create mode 100644 ai-team/backend/nextjs/react-hook-form-pages.md create mode 100644 ai-team/backend/nextjs/react-query-pages.md create mode 100644 ai-team/backend/payment/write-stripe-code.md create mode 100644 ai-team/frontend/strategy create mode 100644 ai-team/marketing/name create mode 100644 ai-team/marketing/strategy create mode 100644 ai-team/product/proposal.md create mode 100644 ai-team/ui-ux/accessibility.md create mode 100644 ai-team/ui-ux/alerts.md create mode 100644 ai-team/ui-ux/buttons.md create mode 100644 ai-team/ui-ux/cards.md create mode 100644 ai-team/ui-ux/chat-xp create mode 100644 ai-team/ui-ux/data-visualizations/charts-graphs.md create mode 100644 ai-team/ui-ux/data-visualizations/tables.md create mode 100644 ai-team/ui-ux/design-system-items.md create mode 100644 ai-team/ui-ux/design-system.md create mode 100644 ai-team/ui-ux/design.md create mode 100644 ai-team/ui-ux/folder-layout.md create mode 100644 ai-team/ui-ux/forms.md create mode 100644 ai-team/ui-ux/grid-system.md create mode 100644 ai-team/ui-ux/iconography.md create mode 100644 ai-team/ui-ux/layout-components.md create mode 100644 ai-team/ui-ux/logo create mode 100644 ai-team/ui-ux/modals.md create mode 100644 ai-team/ui-ux/navigation-patterns.md create mode 100644 ai-team/ui-ux/strategy create mode 100644 ai-team/ui-ux/style-guide.md create mode 100644 ai-team/ui-ux/typography.md create mode 100644 ai-team/use-cases.md diff --git a/Diagrams.md b/Diagrams.md new file mode 100644 index 0000000..69d967b --- /dev/null +++ b/Diagrams.md @@ -0,0 +1,38 @@ +# Mermaid diagrams + +We can simply ask Chat GPT to create Mermaid ERD code for a specific use case. +We can also specify a specific diagram type: + +Mermaid supports the following types of diagrams: + +- Flowchart +- Sequence +- Class +- State +- Entity Relationship +- User Journey +- Gantt +- Pie +- Requirement + +See [how to include diagrams in github project/](https://www.makeuseof.com/how-to-include-diagrams-in-github-project/) + +## Export diagrams to PNG + +We can export a mermaid diagram to PNG very simply using the official Mermaid CLI Tool. + +Installation via npm + +`npm i -g mermaid.cli` + +Usage: + +`mmdc -i ${input_file_location} -o ${output_file_location}` + +Example + +```bash +echo 'sequenceDiagram\nAlice ->> Bob: Hey, Bob!\nBob ->> Alice: Hey there, Alice!' > /tmp/alice-bob.mmd +mmdc -i /tmp/alice-bob.mmd -o /tmp/alice-bob.png +open /tmp/alice-bob.png +``` \ No newline at end of file diff --git a/ai-team/api/strategy b/ai-team/api/strategy new file mode 100644 index 0000000..16e730d --- /dev/null +++ b/ai-team/api/strategy @@ -0,0 +1 @@ +To build the frontend the frontend team will work closely with the ui-ux team and the API team. diff --git a/ai-team/architecture/diagrams/C4-architecture.md b/ai-team/architecture/diagrams/C4-architecture.md new file mode 100644 index 0000000..76df8f0 --- /dev/null +++ b/ai-team/architecture/diagrams/C4-architecture.md @@ -0,0 +1,112 @@ +Write a Structurizr DSL diagram for the system +=== + +Certainly! Here's the Structurizr DSL code for the Cafeteria Management System: + +``` +workspace { + model { + user = person "Employee" + staff = person "Cafeteria Staff" + system = softwareSystem "Cafeteria Management System" { + containers { + webApp = container "Web Application" { + uses database, "Reads/Writes data to" + uses paymentGateway, "Processes Payments via" + uses orderService, "Places Orders via" + uses menuService, "Fetches Menus from" + } + + database = container "Database" { + technology = "Relational Database" + } + + paymentGateway = container "Payment Gateway" { + technology = "External Service" + } + + orderService = container "Order Service" { + technology = "Microservice" + uses database, "Stores Orders in" + } + + menuService = container "Menu Service" { + technology = "Microservice" + uses database, "Stores Menus in" + } + } + + views { + systemContext webApp { + include * + autoLayout + } + + containerDiagram webApp { + include * + autoLayout + } + } + } + } + + views { + configuration { + theme "Cafeteria Management System" { + font "Roboto", "14", "#ffffff" + background "#363636" + border "#d3d3d3" + } + } + } +} +``` + +This Structurizr DSL code represents the high-level architecture of the Cafeteria Management System, including the web application, database, payment gateway, order service, and menu service. The relationships and dependencies between the components are defined using the appropriate DSL syntax. + +=== +Install structurizr CLI +=== + +See [structurizr container](https://github.com/structurizr/cli/blob/master/docs/containers.md) + +A prebuilt Docker image is available at Docker Hub. To use it, for example: + +```bash +docker pull structurizr/cli:latest +docker run -it --rm -v $PWD:/usr/local/structurizr structurizr/cli +``` + +The file extension for a Structurizr file is typically `.dsl`. It is a common convention to use the ".dsl" extension to indicate that the file contains Structurizr DSL code. However, it's important to note that the file extension is not strictly enforced by the Structurizr platform, and you can choose any file extension you prefer as long as the content follows the Structurizr DSL syntax. + +[Structurizr DSL](https://github.com/structurizr/dsl) + +See [Structurizr DSL playground](https://structurizr.com/dsl) + +[Getting started with Structurizr lite](https://dev.to/simonbrown/getting-started-with-structurizr-lite-27d0) + +Step 1) Create a directory somewhere to store our workspace. + +Step 2) + +Be sure to replace PATH with the full path to the directory created in step 1. + +```bash +docker pull structurizr/lite +docker run -it --rm -p 8080:8080 -v PATH:/usr/local/structurizr structurizr/lite +``` + +For example, if the directory is located at `/Users/alice/structurizr`, the commands would be: + +```bash +docker pull structurizr/lite +docker run -it --rm -p 8080:8080 -v /Users/alice/structurizr:/usr/local/structurizr structurizr/lite +``` + +Open the workspace in a web browser by heading to `http://localhost:8080` and you should see your diagram. + +[Structurizr Cookbook](https://github.com/structurizr/dsl/tree/master/docs/cookbook) + +[DSL language reference](https://github.com/structurizr/dsl/blob/master/docs/language-reference.md) + +[structurizr to PNG](https://github.com/extenda/structurizr-to-png) \ No newline at end of file diff --git a/ai-team/architecture/diagrams/cafeteria-arch.dsl b/ai-team/architecture/diagrams/cafeteria-arch.dsl new file mode 100644 index 0000000..ebb32d6 --- /dev/null +++ b/ai-team/architecture/diagrams/cafeteria-arch.dsl @@ -0,0 +1,56 @@ +workspace { + model { + user = person "Employee" + staff = person "Cafeteria Staff" + system = softwareSystem "Cafeteria Management System" { + containers { + webApp = container "Web Application" { + uses database, "Reads/Writes data to" + uses paymentGateway, "Processes Payments via" + uses orderService, "Places Orders via" + uses menuService, "Fetches Menus from" + } + + database = container "Database" { + technology = "Relational Database" + } + + paymentGateway = container "Payment Gateway" { + technology = "External Service" + } + + orderService = container "Order Service" { + technology = "Microservice" + uses database, "Stores Orders in" + } + + menuService = container "Menu Service" { + technology = "Microservice" + uses database, "Stores Menus in" + } + } + + views { + systemContext webApp { + include * + autoLayout + } + + containerDiagram webApp { + include * + autoLayout + } + } + } + } + + views { + configuration { + theme "Cafeteria Management System" { + font "Roboto", "14", "#ffffff" + background "#363636" + border "#d3d3d3" + } + } + } +} \ No newline at end of file diff --git a/ai-team/architecture/diagrams/cafeteria.mmd b/ai-team/architecture/diagrams/cafeteria.mmd new file mode 100644 index 0000000..b585c05 --- /dev/null +++ b/ai-team/architecture/diagrams/cafeteria.mmd @@ -0,0 +1,61 @@ +erDiagram + Employee ||--o{ Order : Places + Employee ||--o{ Payment : Makes + Employee ||--o{ CafeteriaStaff : Manages + + Order ||--|{ Payment : Includes + + Menu ||--o{ OrderItem : Has + MenuCategory ||--o{ Menu : Categorizes + + Employee { + string employeeId + string firstName + string lastName + string username + string password + string email + string employeeMealCard + } + + Order { + string orderId + string employeeId + } + + Payment { + string paymentId + string employeeId + string orderId + number amount + string paymentMethod + } + + Menu { + string menuId + string name + string description + number price + string categoryId + } + + OrderItem { + string orderItemId + string orderId + string menuId + number quantity + } + + MenuCategory { + string categoryId + string name + } + + CafeteriaStaff { + string staffId + string firstName + string lastName + string username + string password + string email + } \ No newline at end of file diff --git a/ai-team/architecture/diagrams/mermaid.md b/ai-team/architecture/diagrams/mermaid.md new file mode 100644 index 0000000..39c5f75 --- /dev/null +++ b/ai-team/architecture/diagrams/mermaid.md @@ -0,0 +1,274 @@ +Write the Mermaid ERD code for this use case +=== + +Certainly! Here's the Mermaid ERD code representing the data model for the Cafeteria Management System: + +``` +erDiagram + Employee ||--o{ Order : Places + Employee ||--o{ Payment : Makes + Employee ||--o{ CafeteriaStaff : Manages + + Order ||--|{ Payment : Includes + + Menu ||--o{ OrderItem : Has + MenuCategory ||--o{ Menu : Categorizes + + Employee { + string employeeId + string firstName + string lastName + string username + string password + string email + string employeeMealCard + } + + Order { + string orderId + string employeeId + } + + Payment { + string paymentId + string employeeId + string orderId + number amount + string paymentMethod + } + + Menu { + string menuId + string name + string description + number price + string categoryId + } + + OrderItem { + string orderItemId + string orderId + string menuId + number quantity + } + + MenuCategory { + string categoryId + string name + } + + CafeteriaStaff { + string staffId + string firstName + string lastName + string username + string password + string email + } +``` + +=== +Generate code for mermaid sequence diagram for this use case +=== + +Certainly! Here's an example of a Mermaid sequence diagram for the cafeteria management system use case: + +``` +sequenceDiagram + participant Employee + participant Cafeteria + participant PaymentGateway + participant Kitchen + + Employee->Cafeteria: Place Order + Cafeteria->Kitchen: Prepare Order + Kitchen->Cafeteria: Order Ready + Cafeteria->Employee: Serve Order + Employee->Cafeteria: Make Payment + Cafeteria->PaymentGateway: Process Payment + PaymentGateway-->Cafeteria: Payment Success + Cafeteria-->Employee: Payment Acknowledgement +``` + +This sequence diagram illustrates the interactions between the employee, cafeteria, payment gateway, and kitchen components in the cafeteria management system. The sequence starts with the employee placing an order at the cafeteria. The kitchen prepares the order and notifies the cafeteria when it's ready. The cafeteria serves the order to the employee. Then, the employee makes a payment at the cafeteria, which is processed by the payment gateway. Finally, the cafeteria receives the payment confirmation and acknowledges it to the employee. + +=== +Create mermaid code for state transition diagram for payment of employees +=== + +Certainly! Here's an example of a Mermaid state transition diagram for the payment process of employees: + + +``` +stateDiagram + [*] --> Pending: Initialize Payment + Pending --> Processing: Submit Payment + Processing --> Completed: Payment Processed + Completed --> [*]: Payment Received +``` + +In this state transition diagram, the payment process starts in the initial state [*] and transitions to the Pending state when the payment is initialized. From the Pending state, the payment transitions to the Processing state when it is submitted for processing. Once the payment is successfully processed, it transitions to the Completed state. Finally, from the Completed state, the payment transitions back to the initial state [*] when the payment is received. + +=== +Create more detailed state diagram +=== + +Certainly! Here's an example of a more detailed Mermaid state transition diagram for the payment process of employees: + + +``` +stateDiagram + [*] --> Init: Initialize Payment + Init --> Verify: Verify Payment Details + Verify --> Process: Process Payment + Process --> Deduct: Deduct Amount from Account + Deduct --> Transfer: Transfer Funds to Employee + Transfer --> Notify: Notify Employee of Payment + Notify --> [*]: Payment Complete + + Verify --> Cancel: Cancel Payment + Process --> Cancel: Cancel Payment + Deduct --> Cancel: Cancel Payment + Transfer --> Cancel: Cancel Payment + Notify --> Cancel: Cancel Payment + Cancel --> [*]: Payment Canceled +``` + +In this state transition diagram, the payment process starts in the initial state [*] and transitions to the Init state when the payment is initialized. From the Init state, the payment moves to the Verify state to verify the payment details. If the payment details are valid, it transitions to the Process state to initiate the payment process. The payment then moves to the Deduct state to deduct the payment amount from the employee's account. Once the deduction is successful, it transitions to the Transfer state to transfer the funds to the employee's account. After the transfer, it moves to the Notify state to notify the employee of the payment. Finally, from the Notify state, the payment transitions back to the initial state [*] indicating the completion of the payment process. + +Additionally, there is a possibility to cancel the payment at various stages. If the payment needs to be canceled during the verification, processing, deduction, transfer, or notification stages, it transitions to the Cancel state, and from there, it moves back to the initial state [*] indicating the cancellation of the payment. + +=== +Create state diagram for entire use case +=== + +Certainly! Here's a state diagram for the entire cafeteria management system use case: + +``` +stateDiagram + [*] --> Startup: System Startup + Startup --> Idle: System Ready + + Idle --> Menu: Select Menu Option + Menu --> Order: Place Order + Order --> Process: Process Order + Process --> Prepare: Prepare Order + Prepare --> Serve: Serve Order + Serve --> Complete: Order Complete + Complete --> Idle: Return to Idle + + Menu --> AddBalance: Add Balance + AddBalance --> Idle: Return to Idle + + Menu --> CheckBalance: Check Balance + CheckBalance --> Idle: Return to Idle + + Menu --> ViewHistory: View Order History + ViewHistory --> Idle: Return to Idle + + Menu --> Quit: Quit System + Quit --> Shutdown: System Shutdown +``` + +In this state diagram, the system starts in the initial state [*] and transitions to the Startup state when the system is being started up. From the Startup state, it moves to the Idle state, indicating that the system is ready and waiting for user input. + +Once in the Idle state, users can select various options from the menu, such as placing an order (Menu --> Order), adding balance to their account (Menu --> AddBalance), checking their balance (Menu --> CheckBalance), viewing their order history (Menu --> ViewHistory), or quitting the system (Menu --> Quit). + +If a user chooses to place an order, the system transitions to the Order state. The order is then processed (Order --> Process), prepared (Process --> Prepare), served (Prepare --> Serve), and marked as complete (Serve --> Complete). After the order is complete, the system returns to the Idle state. + +Users can also add balance to their account, check their balance, or view their order history. Each of these options transitions to a corresponding state and then returns to the Idle state after the operation is complete. + +If the user chooses to quit the system, the system transitions to the Quit state and eventually shuts down (Quit --> Shutdown). + +=== +Please create a more detailed diagram +=== + +Certainly! Here's a more detailed state diagram for the cafeteria management system use case: + +``` +stateDiagram + [*] --> Startup: System Startup + Startup --> Idle: System Ready + + state Idle { + [*] --> Menu: Select Menu Option + Menu --> ViewBalance: View Balance + Menu --> AddBalance: Add Balance + Menu --> ViewOrderHistory: View Order History + Menu --> PlaceOrder: Place Order + Menu --> Checkout: Checkout + Menu --> CancelOrder: Cancel Order + Menu --> Logout: Logout + + state ViewBalance { + [*] --> DisplayBalance: Display Balance + DisplayBalance --> Menu: Return to Menu + } + + state AddBalance { + [*] --> EnterAmount: Enter Amount + EnterAmount --> ProcessPayment: Process Payment + ProcessPayment --> DisplaySuccess: Display Success Message + DisplaySuccess --> Menu: Return to Menu + } + + state ViewOrderHistory { + [*] --> FetchHistory: Fetch Order History + FetchHistory --> DisplayHistory: Display Order History + DisplayHistory --> Menu: Return to Menu + } + + state PlaceOrder { + [*] --> SelectItems: Select Items + SelectItems --> AddToCart: Add to Cart + SelectItems --> RemoveFromCart: Remove from Cart + SelectItems --> AdjustQuantity: Adjust Quantity + SelectItems --> ViewCart: View Cart + SelectItems --> ConfirmOrder: Confirm Order + + state ViewCart { + [*] --> DisplayCart: Display Cart + DisplayCart --> SelectItems: Continue Editing + DisplayCart --> ConfirmOrder: Proceed to Checkout + } + + state ConfirmOrder { + [*] --> ProcessOrder: Process Order + ProcessOrder --> DisplaySuccess: Display Success Message + DisplaySuccess --> Menu: Return to Menu + } + } + + state Checkout { + [*] --> ProcessPayment: Process Payment + ProcessPayment --> DisplaySuccess: Display Success Message + DisplaySuccess --> Menu: Return to Menu + } + + state CancelOrder { + [*] --> ConfirmCancellation: Confirm Cancellation + ConfirmCancellation --> ProcessCancellation: Process Cancellation + ProcessCancellation --> DisplaySuccess: Display Success Message + DisplaySuccess --> Menu: Return to Menu + } + + Logout --> Startup: Logout and Restart + } + + Startup --> Shutdown: System Shutdown +``` + +In this more detailed state diagram, the system starts in the initial state [*] and transitions to the Startup state during system startup. From the Startup state, it moves to the Idle state, indicating that the system is ready and waiting for user input. + +Within the Idle state, users have various menu options such as viewing their balance, adding balance, viewing order history, placing an order, checking out, canceling an order, and logging out. + +The Idle state contains substates for each menu option, allowing for more specific interactions. For example, users can view their balance (Menu --> ViewBalance), add balance to their account (Menu --> AddBalance), view their order history (Menu --> ViewOrderHistory), place an order (Menu --> PlaceOrder), proceed to checkout (Menu --> Checkout), cancel an order (Menu --> CancelOrder), or log out of the system (Menu --> Logout). + +Some substates have further substates. For instance, within the PlaceOrder state, users can select items, add them to the cart, adjust quantities, view the cart, and confirm the order. + +The diagram also includes transitions to handle specific actions, such as processing payments, displaying success messages, and returning to the main menu. + +If the user chooses to log out, the system transitions back to the Startup state, allowing for a fresh start. Finally, the system can be shut down from the Startup state (Startup --> Shutdown). + +This more detailed state diagram provides a comprehensive representation of the various states, substates, and transitions involved in the cafeteria management system. It showcases the different paths and interactions users can have with the system based on their menu selections. diff --git a/ai-team/backend/data-model.md b/ai-team/backend/data-model.md new file mode 100644 index 0000000..10e0241 --- /dev/null +++ b/ai-team/backend/data-model.md @@ -0,0 +1,186 @@ +Define a data model for this use case +=== + +Data Model for Cafeteria Management System: + +Employee: +employeeId: string (unique identifier for each employee) +firstName: string (employee's first name) +lastName: string (employee's last name) +username: string (employee's login username) +password: string (employee's login password) +email: string (employee's email address) +employeeMealCard: string (unique identifier for the employee's meal card) + +Menu: +menuId: string (unique identifier for each menu item) +name: string (name of the menu item) +description: string (description of the menu item) +price: number (price of the menu item) +category: string (category of the menu item, e.g., main course, side dish, beverage) + +Order: +orderId: string (unique identifier for each order) +employeeId: string (reference to the employee who placed the order) +items: Array of objects +menuId: string (reference to the menu item ordered) +quantity: number (quantity of the menu item ordered) + +Payment: +paymentId: string (unique identifier for each payment) +employeeId: string (reference to the employee who made the payment) +orderId: string (reference to the order for which the payment is made) +amount: number (total amount paid) +paymentMethod: string (method used for payment, e.g., credit card, meal card) + +Meal: +mealId: string (unique identifier for each prepared meal) +orderId: string (reference to the order for which the meal is prepared) +preparationTime: Date (timestamp indicating when the meal is prepared) +pickupTime: Date (timestamp indicating when the meal is ready for pickup) + +Cafeteria Staff: +staffId: string (unique identifier for each staff member) +firstName: string (staff member's first name) +lastName: string (staff member's last name) +username: string (staff member's login username) +password: string (staff member's login password) +email: string (staff member's email address) + +Menu Category: +categoryId: string (unique identifier for each menu category) +name: string (name of the menu category) +Note: This data model provides a basic structure for the cafeteria management system. Additional attributes and relationships can be added based on specific requirements, such as dietary preferences, allergens, loyalty points, etc. + +=== +write data model in tabular form +=== + +Sure! Here's the data model for the Cafeteria Management System in tabular form: + +Employee Table: + +Column Data Type Description +employeeId string Unique identifier for each employee +firstName string Employee's first name +lastName string Employee's last name +username string Employee's login username +password string Employee's login password +email string Employee's email address +employeeMealCard string Unique identifier for the employee's meal card +Menu Table: + +Column Data Type Description +menuId string Unique identifier for each menu item +name string Name of the menu item +description string Description of the menu item +price number Price of the menu item +category string Category of the menu item +Order Table: + +Column Data Type Description +orderId string Unique identifier for each order +employeeId string Reference to the employee who placed the order +items array Array of objects representing the ordered menu items +Payment Table: + +Column Data Type Description +paymentId string Unique identifier for each payment +employeeId string Reference to the employee who made the payment +orderId string Reference to the order for which the payment is made +amount number Total amount paid +paymentMethod string Method used for payment +Meal Table: + +Column Data Type Description +mealId string Unique identifier for each prepared meal +orderId string Reference to the order for which the meal is prepared +preparationTime Date Timestamp indicating when the meal is prepared +pickupTime Date Timestamp indicating when the meal is ready for pickup +Cafeteria Staff Table: + +Column Data Type Description +staffId string Unique identifier for each staff member +firstName string Staff member's first name +lastName string Staff member's last name +username string Staff member's login username +password string Staff member's login password +email string Staff member's email address +Menu Category Table: + +Column Data Type Description +categoryId string Unique identifier for each menu category +name string Name of the menu category +Note: This tabular representation provides a clear overview of the data model, its columns, and their respective data types. + +=== +Write the Mermaid ERD code for this use case +=== + +Certainly! Here's the Mermaid ERD code representing the data model for the Cafeteria Management System: + +``` +erDiagram + Employee ||--o{ Order : Places + Employee ||--o{ Payment : Makes + Employee ||--o{ CafeteriaStaff : Manages + + Order ||--|{ Payment : Includes + + Menu ||--o{ OrderItem : Has + MenuCategory ||--o{ Menu : Categorizes + + Employee { + string employeeId + string firstName + string lastName + string username + string password + string email + string employeeMealCard + } + + Order { + string orderId + string employeeId + } + + Payment { + string paymentId + string employeeId + string orderId + number amount + string paymentMethod + } + + Menu { + string menuId + string name + string description + number price + string categoryId + } + + OrderItem { + string orderItemId + string orderId + string menuId + number quantity + } + + MenuCategory { + string categoryId + string name + } + + CafeteriaStaff { + string staffId + string firstName + string lastName + string username + string password + string email + } +``` + +This Mermaid ERD code represents the relationships between the various entities in the Cafeteria Management System, including employees, orders, payments, menus, and cafeteria staff. The relationships are visualized using the appropriate arrow notations. diff --git a/ai-team/backend/database/prisma-schema.md b/ai-team/backend/database/prisma-schema.md new file mode 100644 index 0000000..9ef0719 --- /dev/null +++ b/ai-team/backend/database/prisma-schema.md @@ -0,0 +1,174 @@ +create the prisma schema for the use case +=== + +To create the Prisma schema for the property administration system use case, you can define the data models and their relationships in the schema file. Here's an example Prisma schema for this use case: + +```prisma +// schema.prisma + +model Property { + id Int @id @default(autoincrement()) + name String + address String + units Unit[] +} + +model Unit { + id Int @id @default(autoincrement()) + number Int + area Float + property Property @relation(fields: [propertyId], references: [id]) + propertyId Int + tenants Tenant[] +} + +model Tenant { + id Int @id @default(autoincrement()) + firstName String + lastName String + email String @unique + phone String? + unit Unit @relation(fields: [unitId], references: [id]) + unitId Int + leases Lease[] +} + +model Lease { + id Int @id @default(autoincrement()) + startDate DateTime + endDate DateTime + rent Float + unit Unit @relation(fields: [unitId], references: [id]) + unitId Int + tenant Tenant @relation(fields: [tenantId], references: [id]) + tenantId Int +} +``` + +In the above schema, we have defined four models: Property, Unit, Tenant, and Lease. Each model represents a different entity in the property administration system. + +The Property model represents a property with its name, address, and a list of units associated with it. + +The Unit model represents an individual unit within a property, with its number, area, and a reference to the parent Property model. + +The Tenant model represents a tenant with their first name, last name, email, phone number, and the unit they are associated with. + +The Lease model represents a lease agreement with its start date, end date, rent amount, and references to the associated Unit and Tenant. + +The relationships between the models are defined using the @relation attribute. For example, the Property model has a one-to-many relationship with the Unit model, and the Unit model has a one-to-many relationship with the Tenant model. + +You can use this Prisma schema to generate database migrations, create the database tables, and interact with the database using the Prisma Client. + +=== +Please create a more detailed schema for the property administration system which includes models to support all user flows in the use case +=== + +Certainly! Here's an expanded schema for the property administration system that includes additional models to support various user flows: + +```prisma +// schema.prisma + +model User { + id Int @id @default(autoincrement()) + email String @unique + password String + firstName String + lastName String + role Role +} + +model Role { + id Int @id @default(autoincrement()) + name String @unique + users User[] +} + +model Property { + id Int @id @default(autoincrement()) + name String + address String + units Unit[] +} + +model Unit { + id Int @id @default(autoincrement()) + number Int + area Float + property Property @relation(fields: [propertyId], references: [id]) + propertyId Int + tenants Tenant[] + leases Lease[] +} + +model Tenant { + id Int @id @default(autoincrement()) + firstName String + lastName String + email String @unique + phone String? + unit Unit @relation(fields: [unitId], references: [id]) + unitId Int + leases Lease[] +} + +model Lease { + id Int @id @default(autoincrement()) + startDate DateTime + endDate DateTime + rent Float + unit Unit @relation(fields: [unitId], references: [id]) + unitId Int + tenant Tenant @relation(fields: [tenantId], references: [id]) + tenantId Int + payments Payment[] +} + +model Payment { + id Int @id @default(autoincrement()) + amount Float + date DateTime + lease Lease @relation(fields: [leaseId], references: [id]) + leaseId Int +} + +model MaintenanceRequest { + id Int @id @default(autoincrement()) + title String + description String + status MaintenanceRequestStatus + unit Unit @relation(fields: [unitId], references: [id]) + unitId Int +} + +enum MaintenanceRequestStatus { + OPEN + IN_PROGRESS + COMPLETED +} + +model Announcement { + id Int @id @default(autoincrement()) + title String + content String + date DateTime @default(now()) + property Property @relation(fields: [propertyId], references: [id]) + propertyId Int +} + +model Document { + id Int @id @default(autoincrement()) + name String + url String + property Property @relation(fields: [propertyId], references: [id]) + propertyId Int +} +``` + +In this expanded schema: + +The User model represents system users and includes fields for email, password, first name, last name, and a reference to the user's role. +The Role model represents user roles and can be used to define different access levels or permissions. +The MaintenanceRequest model represents maintenance requests for specific units and includes fields for title, description, status, and a reference to the associated unit. +The Announcement model represents announcements that can be made for a specific property and includes fields for title, content, date, and a reference to the associated property. +The Document model represents documents associated with a property and includes fields for name, URL, and a reference to the associated property. +These additional models support functionalities such as user management, maintenance requests, announcements, and document management, enhancing the overall functionality of the property administration system. \ No newline at end of file diff --git a/ai-team/backend/database/repository-classes.md b/ai-team/backend/database/repository-classes.md new file mode 100644 index 0000000..9b27443 --- /dev/null +++ b/ai-team/backend/database/repository-classes.md @@ -0,0 +1,71 @@ +Please create a set of repository (data query and command) classes that can be used to operate on the Prisma based data model +ChatGPT +Certainly! Here's an example of repository classes that can be used to operate on the Prisma-based data model: + +```typescript +// userRepository.ts + +import { PrismaClient, User } from '@prisma/client'; + +export class UserRepository { + private prisma: PrismaClient; + + constructor() { + this.prisma = new PrismaClient(); + } + + async createUser(data: User): Promise { + return this.prisma.user.create({ data }); + } + + async getUserById(id: number): Promise { + return this.prisma.user.findUnique({ where: { id } }); + } + + async getUserByEmail(email: string): Promise { + return this.prisma.user.findUnique({ where: { email } }); + } + + async updateUser(id: number, data: Partial): Promise { + return this.prisma.user.update({ where: { id }, data }); + } + + async deleteUser(id: number): Promise { + return this.prisma.user.delete({ where: { id } }); + } +} +``` + +```ts +// propertyRepository.ts + +import { PrismaClient, Property } from '@prisma/client'; + +export class PropertyRepository { + private prisma: PrismaClient; + + constructor() { + this.prisma = new PrismaClient(); + } + + async createProperty(data: Property): Promise { + return this.prisma.property.create({ data }); + } + + async getPropertyById(id: number): Promise { + return this.prisma.property.findUnique({ where: { id } }); + } + + async updateProperty(id: number, data: Partial): Promise { + return this.prisma.property.update({ where: { id }, data }); + } + + async deleteProperty(id: number): Promise { + return this.prisma.property.delete({ where: { id } }); + } +} +``` + +These are just examples for the User and Property models. You can create similar repository classes for other models in your data model. These repository classes encapsulate the data operations using Prisma's query and mutation methods. You can use these classes to perform CRUD operations on the corresponding entities in your Prisma-based data model. + +Please make sure to install the required dependencies (@prisma/client and prisma) and configure Prisma properly in your project before using these repository classes. \ No newline at end of file diff --git a/ai-team/backend/database/tRPC-zod-API.md b/ai-team/backend/database/tRPC-zod-API.md new file mode 100644 index 0000000..434d3e0 --- /dev/null +++ b/ai-team/backend/database/tRPC-zod-API.md @@ -0,0 +1,73 @@ +Please create a tRPC API with Zod which validates data and then calls the repository classes. + +=== +Certainly! Here's an example of creating a tRPC API with Zod for validating data and calling the repository classes: + +```typescript +Copy code +// api.ts + +import { createRouter, z } from 'trpc'; +import { zodResolver } from '@trpc/server'; +import { UserRepository, PropertyRepository } from './repositories'; + +const userRepository = new UserRepository(); +const propertyRepository = new PropertyRepository(); + +// Define Zod schema for user creation +const createUserSchema = z.object({ + name: z.string(), + email: z.string().email(), + password: z.string().min(6), +}); + +// Define Zod schema for property creation +const createPropertySchema = z.object({ + name: z.string(), + address: z.string(), + // ... other property fields +}); + +// Create tRPC router +const trpc = createRouter() + .mutation('createUser', { + input: zodResolver(createUserSchema), + resolve: async ({ input }) => { + const user = await userRepository.createUser(input); + return user; + }, + }) + .mutation('createProperty', { + input: zodResolver(createPropertySchema), + resolve: async ({ input }) => { + const property = await propertyRepository.createProperty(input); + return property; + }, + }) + .query('getUser', { + input: z.number(), + resolve: async ({ input }) => { + const user = await userRepository.getUserById(input); + return user; + }, + }) + .query('getProperty', { + input: z.number(), + resolve: async ({ input }) => { + const property = await propertyRepository.getPropertyById(input); + return property; + }, + }); + +export type API = typeof trpc; + +export const api = trpc; +``` + +In this example, we define two mutations (createUser and createProperty) and two queries (getUser and getProperty). Each mutation and query has a corresponding Zod schema for data validation. We use the zodResolver from @trpc/server to automatically validate the input data against the specified Zod schema. + +The resolve function for each mutation and query calls the corresponding methods from the repository classes (userRepository and propertyRepository) to perform the desired data operations. + +You can add more mutations and queries as per your requirements and define their Zod schemas accordingly. Also, make sure to import and configure the necessary dependencies (trpc, @trpc/server, and z) in your project. + +Note: This is a simplified example to demonstrate the usage of tRPC and Zod for data validation. You may need to customize and expand it based on your specific use case and requirements. \ No newline at end of file diff --git a/ai-team/backend/nextjs/client-api.md b/ai-team/backend/nextjs/client-api.md new file mode 100644 index 0000000..83b4490 --- /dev/null +++ b/ai-team/backend/nextjs/client-api.md @@ -0,0 +1,44 @@ +Please write the `API.getUser` and `API.getUsers` with the actual functions that make the API calls to fetch the user data in your tRPC API +=== + +Certainly! Here's an example of how you can implement API.getUser and API.getUsers functions in your tRPC API to make the actual API calls for fetching user data: + +```javascript +// path/to/api.js + +import { trpc } from './path/to/trpc'; // Import your tRPC client + +export const API = { + getUser: async (id) => { + try { + const { data } = await trpc.query('getUser', { id }); + return data; + } catch (error) { + console.error('Error fetching user:', error); + throw error; + } + }, + + getUsers: async () => { + try { + const { data } = await trpc.query('getUsers'); + return data; + } catch (error) { + console.error('Error fetching users:', error); + throw error; + } + }, +}; +``` + +In this example, we assume that you have already set up your tRPC client using trpc and have defined the appropriate query names (getUser and getUsers) in your tRPC schema. + +Make sure to replace path/to/trpc with the correct path to your tRPC client file. + +Inside each function, we use trpc.query to make the actual API call to the corresponding tRPC endpoint. The getUser function takes the id parameter and passes it as an argument to the query. Similarly, the getUsers function doesn't require any parameters. + +If the API call is successful, we return the data property from the response. If an error occurs during the API call, we log the error and rethrow it to handle it further up the call stack. + +Remember to update the `getUser` and `getUsers` functions based on your tRPC API schema and actual endpoint names. + +With these functions in place, the `UserPage` and `UsersPage` components we discussed earlier will be able to fetch user data by calling `API.getUser` and `API.getUsers` respectively. \ No newline at end of file diff --git a/ai-team/backend/nextjs/react-hook-form-pages.md b/ai-team/backend/nextjs/react-hook-form-pages.md new file mode 100644 index 0000000..516e881 --- /dev/null +++ b/ai-team/backend/nextjs/react-hook-form-pages.md @@ -0,0 +1,74 @@ +Please create a User profile management page in NextJS which calls the tRPC API using best practices. What library or built-in functionality would you recommend to take the form data from the page and make the call to the API? +=== + +To handle form data and make API calls in a Next.js application, I recommend using the `react-hook-form` library. It's a lightweight form validation library that integrates well with Next.js and provides an easy way to manage form state and perform form submissions. + +Here's an example of creating a User profile management page in Next.js that utilizes `react-hook-form` and makes API calls to the tRPC API: + +Install `react-hook-form` and other necessary dependencies: + +``` +npm install react-hook-form trpc react-query +``` + +Create a new file for the User profile management page: + +```jsx +Copy code +// pages/profile.js + +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { useMutation, useQueryClient } from 'react-query'; +import { API } from '../path/to/api'; // Import your tRPC API + +const ProfilePage = () => { + const { register, handleSubmit, formState: { errors } } = useForm(); + const queryClient = useQueryClient(); + const updateUserMutation = useMutation((data) => API.mutationName(data), { + onSuccess: () => { + queryClient.invalidateQueries('getUser'); // Invalidate cached user data + }, + }); + + const onSubmit = (data) => { + updateUserMutation.mutate(data); + }; + + return ( +
+

User Profile

+
+
+ + + {errors.name && Name is required} +
+
+ + + {errors.email && Valid email is required} +
+ {/* Add more form fields as needed */} + +
+
+ ); +}; + +export default ProfilePage; +``` + +In this example, we're using the useForm hook from `react-hook-form` to handle form state and validation. The form fields are registered with the register function, and we can specify validation rules such as required and pattern directly in the register function. + +The `useMutation` hook from `react-query` is used to handle the API call for updating the user profile. We define the onSuccess callback to invalidate the cached user data in the queryClient after a successful update. + +Inside the `onSubmit` function, we call the mutate function from updateUserMutation to trigger the API call with the form data. + +Note that you'll need to replace API.mutationName with the actual mutation name from your tRPC API. + +Make sure to import and configure the necessary dependencies (react-hook-form, react-query, and trpc) in your Next.js application. + +This example demonstrates a basic setup for form handling and API calls in Next.js using `react-hook-form` and `react-query`. You can expand upon this code to include additional form fields and validations as needed for your User profile management page. \ No newline at end of file diff --git a/ai-team/backend/nextjs/react-query-pages.md b/ai-team/backend/nextjs/react-query-pages.md new file mode 100644 index 0000000..1de1c66 --- /dev/null +++ b/ai-team/backend/nextjs/react-query-pages.md @@ -0,0 +1,90 @@ +Please create a single user display page which uses react-query and then a page which displays a list of users with pagination +=== + +Sure! Here's an example of creating a single user display page and a user list page using react-query in Next.js: + +Single User Display Page: + +```jsx +// pages/user/[id].js + +import { useRouter } from 'next/router'; +import { useQuery } from 'react-query'; +import { API } from '../path/to/api'; // Import your tRPC API + +const UserPage = () => { + const router = useRouter(); + const { id } = router.query; + + const { data: user, isLoading, isError } = useQuery(['user', id], () => API.getUser(id)); + + if (isLoading) { + return
Loading...
; + } + + if (isError) { + return
Error fetching user data
; + } + + return ( +
+

User Details

+ {user && ( +
+

{user.name}

+

Email: {user.email}

+ {/* Display other user details */} +
+ )} +
+ ); +}; + +export default UserPage; +``` + +In this example, we're using the useRouter hook from Next.js to get the id parameter from the URL. We then use the useQuery hook from react-query to fetch the user data based on the id parameter. The API.getUser function is a placeholder for the actual API call to retrieve a single user. + +User List Page with Pagination: + +```jsx +// pages/users.js + +import { useQuery } from 'react-query'; +import { API } from '../path/to/api'; // Import your tRPC API + +const UsersPage = () => { + const { data: users, isLoading, isError } = useQuery('users', API.getUsers); + + if (isLoading) { + return
Loading...
; + } + + if (isError) { + return
Error fetching user data
; + } + + return ( +
+ ); +}; + +export default UsersPage; +``` + +In this example, we're using the `useQuery` hook from `react-query` to fetch the list of users. The API.getUsers function is a placeholder for the actual API call to retrieve the list of users. We then render the list of users as clickable links to their individual user pages. + +Make sure to replace API.getUser and API.getUsers with the actual functions that make the API calls to fetch the user data in your tRPC API. + +Remember to configure and set up react-query in your Next.js application by providing the appropriate and components in your _app.js file. + +These examples provide a basic structure for displaying a single user's details and a list of users with pagination using react-query in Next.js. You can customize and enhance these pages according to your specific needs and UI requirements. \ No newline at end of file diff --git a/ai-team/backend/payment/write-stripe-code.md b/ai-team/backend/payment/write-stripe-code.md new file mode 100644 index 0000000..0eafdbe --- /dev/null +++ b/ai-team/backend/payment/write-stripe-code.md @@ -0,0 +1,120 @@ +Write the typescript code necessary for the detailed state diagram, using stripe as the payment provider. The code should be divided into separate logical files and reference each other correctly. +=== + +To implement the detailed state diagram using TypeScript and Stripe as the payment provider, you would need to divide the code into separate logical files and reference each other correctly. Here's an example structure for the code files: + +`paymentStateMachine.ts`: This file contains the state machine definition and transitions for the payment process. + +```ts +import { Machine, assign } from 'xstate'; + +interface PaymentContext { + // Define the context properties here + paymentIntentId: string | null; +} + +type PaymentEvent = + | { type: 'PAYMENT_REQUESTED' } + | { type: 'PAYMENT_FAILED' } + | { type: 'PAYMENT_COMPLETED' }; + +const paymentStateMachine = Machine( + { + id: 'payment', + initial: 'idle', + context: { + paymentIntentId: null, + }, + states: { + idle: { + on: { + PAYMENT_REQUESTED: 'processing', + }, + }, + processing: { + entry: assign({ + paymentIntentId: (context) => { + // Call Stripe API to create a Payment Intent and return the ID + const paymentIntentId = 'payment_intent_id'; // Replace with actual Payment Intent ID + return paymentIntentId; + }, + }), + on: { + PAYMENT_FAILED: 'failed', + PAYMENT_COMPLETED: 'completed', + }, + }, + failed: {}, + completed: {}, + }, + }, + { + actions: {}, + } +); + +export default paymentStateMachine; +``` + +`paymentService.ts`: This file contains the service that interacts with Stripe API for payment processing. + +```ts +import Stripe from 'stripe'; + +class PaymentService { + private stripe: Stripe; + + constructor(apiKey: string) { + this.stripe = new Stripe(apiKey); + } + + async createPaymentIntent(amount: number, currency: string): Promise { + // Call Stripe API to create a Payment Intent and return the ID + const paymentIntent = await this.stripe.paymentIntents.create({ + amount, + currency, + }); + return paymentIntent.id; + } + + // Other methods for handling payment processing with Stripe +} + +export default PaymentService; +``` + +`paymentController.ts`: This file contains the controller logic for handling payment requests and invoking the state machine. + +```ts +import { interpret } from 'xstate'; +import paymentStateMachine, { PaymentEvent } from './paymentStateMachine'; + +class PaymentController { + private machineService = interpret(paymentStateMachine).start(); + + requestPayment(): void { + const event: PaymentEvent = { type: 'PAYMENT_REQUESTED' }; + this.machineService.send(event); + } + + // Other methods for handling payment events and transitions +} + +export default PaymentController; +``` + +`main.ts`: This is the entry point file where you can initialize the payment controller and handle payment requests. + +```ts +import PaymentController from './paymentController'; + +const paymentController = new PaymentController(); +paymentController.requestPayment(); +``` + +// Other code for handling application logic +Please note that this is a simplified example, and you may need to customize it based on your specific requirements and integration with Stripe. Additionally, you would need to install the necessary packages (xstate, stripe, etc.) using a package manager like npm or yarn. + +Make sure to replace the placeholder values and add the necessary error handling, async/await, and API integration logic as per your application's requirements. + +Remember to set up the Stripe API keys and configure your environment accordingly to ensure proper integration with Stripe's services. \ No newline at end of file diff --git a/ai-team/frontend/strategy b/ai-team/frontend/strategy new file mode 100644 index 0000000..16e730d --- /dev/null +++ b/ai-team/frontend/strategy @@ -0,0 +1 @@ +To build the frontend the frontend team will work closely with the ui-ux team and the API team. diff --git a/ai-team/marketing/name b/ai-team/marketing/name new file mode 100644 index 0000000..13250c4 --- /dev/null +++ b/ai-team/marketing/name @@ -0,0 +1,17 @@ +Please generate a good name for a: Web Agency working for Governments and Large Corporations +=== + +Sure! Here are a few name suggestions for a web agency working for governments and large corporations: + +GovWeb Solutions +CorporateTech Agency +EliteWeb Solutions +PublicSector Webworks +GovernmentEdge Web Agency +CorpDigital Solutions +NexusWeb Consultants +ExecutiveWeb Partners +CivicTech Innovators +EnterpriseWeb Studios + +Feel free to mix and match words or modify them to create a unique and fitting name for your web agency. Remember to consider the branding and image you want to convey, as well as the target audience of governments and large corporations. \ No newline at end of file diff --git a/ai-team/marketing/strategy b/ai-team/marketing/strategy new file mode 100644 index 0000000..e69de29 diff --git a/ai-team/product/proposal.md b/ai-team/product/proposal.md new file mode 100644 index 0000000..47fc51f --- /dev/null +++ b/ai-team/product/proposal.md @@ -0,0 +1,84 @@ +Write a detailed proposal for a property administration system +=== + +[Your Name] +[Your Position] +[Your Organization] +[Date] + +[Client's Name] +[Client's Position] +[Client's Organization] +[Address] + +Subject: Proposal for Property Administration System + +Dear [Client's Name], + +We are pleased to present this proposal for the development and implementation of a Property Administration System for [Client's Organization]. This system aims to streamline and automate the property management processes, enhancing efficiency, accuracy, and overall effectiveness in managing properties owned and operated by your organization. + +Overview: +The Property Administration System is a comprehensive software solution designed to address the specific needs of property management. It will provide a centralized platform to manage property listings, tenant information, maintenance requests, financial transactions, and reporting. The system will be tailored to meet your organization's requirements and integrate seamlessly with your existing infrastructure. + +Key Features: +The proposed system will include the following key features: + +2.1 Property Listings: + +Create and manage property listings with detailed information, including location, features, and media attachments. +Enable property search and filtering for potential tenants or buyers. +Track property availability and occupancy status. +2.2 Tenant Management: + +Maintain a database of tenant information, including contact details, lease agreements, and payment history. +Automate rent collection, generate invoices, and track payment records. +Handle tenant communication and notifications. +2.3 Maintenance Management: + +Streamline maintenance requests from tenants, enabling online submission and tracking. +Assign and manage maintenance tasks to maintenance staff or contractors. +Monitor progress, track costs, and maintain a maintenance history log. +2.4 Financial Management: + +Manage property-related financial transactions, including rent payments, security deposits, and expenses. +Generate financial reports, such as rent rolls, income statements, and balance sheets. +Integrate with accounting systems for seamless financial data synchronization. +2.5 Reporting and Analytics: + +Generate customized reports on property performance, occupancy rates, financial summaries, and maintenance activities. +Provide data analytics and insights to support strategic decision-making. +Technology Stack: +The Property Administration System will be developed using modern and reliable technologies. Our recommended technology stack includes: +Front-end: React.js for a responsive and interactive user interface. +Back-end: Node.js for scalable and efficient server-side development. +Database: PostgreSQL for robust data storage and management. +API Integration: RESTful APIs for seamless integration with third-party services. +Security: Implement industry-standard security measures to protect sensitive data. +Project Timeline: +Based on our initial assessment, we estimate a project timeline of [duration] to deliver the Property Administration System. The timeline includes the following stages: +Requirements Gathering and Analysis +System Design and Architecture +Development and Testing +Deployment and User Acceptance Testing +Training and Documentation +System Launch and Post-launch Support +We will work closely with your team throughout the development process, ensuring regular communication and feedback to deliver the system on schedule. + +Budget: +The total cost for developing and implementing the Property Administration System is estimated at [cost]. This cost includes all stages of the project, including development, testing, deployment, training, and support. Any additional requirements or customizations beyond the proposed scope may incur additional charges, which will be discussed and agreed upon separately. + +Support and Maintenance: +Upon successful deployment, we offer ongoing support and maintenance services to ensure the system's smooth operation. Our team will be available to address any technical issues, provide updates and enhancements, and ensure the system remains up-to-date with evolving technology trends. + +Next Steps: +If this proposal meets your requirements and aligns with your organization's vision, we would be delighted to further discuss the project details, answer any questions, and initiate the next steps towards the development of the Property Administration System. We are confident that our expertise and experience in property management systems make us an ideal partner for this project. + +Please feel free to reach out to us at [contact details] to arrange a meeting or further discuss any specific requirements. + +Thank you for considering our proposal. We look forward to the opportunity of working with you and helping [Client's Organization] streamline its property management processes. + +Yours sincerely, + +[Your Name] +[Your Position] +[Your Organization] \ No newline at end of file diff --git a/ai-team/ui-ux/accessibility.md b/ai-team/ui-ux/accessibility.md new file mode 100644 index 0000000..5cdfde9 --- /dev/null +++ b/ai-team/ui-ux/accessibility.md @@ -0,0 +1,124 @@ +Please provide some Accessibility and Usability Guidelines for the use case. +Suggest a few libraries that can be used effectively to ensure accessibility. +Provide a few reusable components that incorporates good accessibility practices and can be used across the app. +=== + +Accessibility and Usability Guidelines: + +Provide proper semantic structure: Use HTML5 elements appropriately to give meaning to the content and structure of the page. Use headings, lists, and semantic elements like