diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 424b4447..dc22258e 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -2,6 +2,7 @@ import builtins from "builtin-modules"; import esbuild from "esbuild"; import svgPlugin from "esbuild-plugin-svg"; import process from "process"; +import wasmPlugin from "./wasmPlugin.mjs"; const banner = `/* @@ -39,7 +40,7 @@ const context = await esbuild.context({ sourcemap: prod ? false : "inline", treeShaking: true, outfile: "main.js", - plugins: [svgPlugin()], + plugins: [svgPlugin(), wasmPlugin], }); if (prod) { diff --git a/package-lock.json b/package-lock.json index 65d9dca7..43a5deeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "axios": "^1.3.4", "esbuild-plugin-svg": "^0.1.0", "eventsource-parser": "^1.0.0", + "langchain": "^0.0.75", "next-i18next": "^13.2.2", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -61,6 +62,15 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.4.3.tgz", + "integrity": "sha512-SZrlXvjUUYT9rPmSzlTtmVk1OjVNpkCzILRluhiYwNcxXfQyvPJDi0CI6PyymygcgtqEF5EVqhKmC/PtPsNEIw==", + "dependencies": { + "@fortaine/fetch-event-source": "^3.0.6", + "cross-fetch": "^3.1.5" + } + }, "node_modules/@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -677,6 +687,11 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@dqbd/tiktoken": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.7.tgz", + "integrity": "sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==" + }, "node_modules/@esbuild/android-arm": { "version": "0.17.3", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", @@ -1089,6 +1104,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.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -2255,6 +2278,11 @@ "@types/react": "*" } }, + "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/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -2808,6 +2836,19 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "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/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2835,6 +2876,11 @@ "node": ">=8" } }, + "node_modules/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==" + }, "node_modules/browserslist": { "version": "4.21.5", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", @@ -3138,6 +3184,14 @@ "url": "https://opencollective.com/core-js" } }, + "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", @@ -3963,6 +4017,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/eventsource-parser": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz", @@ -4019,6 +4078,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "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/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4143,6 +4207,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -4773,6 +4845,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "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-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -6437,6 +6514,14 @@ "node": ">=6" } }, + "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/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6446,6 +6531,172 @@ "node": ">=6" } }, + "node_modules/langchain": { + "version": "0.0.75", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.0.75.tgz", + "integrity": "sha512-5bulBe3pEDnCuO3W84E+pmns6+7oKdNomsApPD1PqjplEwT3szSYaY8bvqJVxYz2Hq1wcS3sL1OyL9rbC/s0NA==", + "dependencies": { + "@anthropic-ai/sdk": "^0.4.3", + "@dqbd/tiktoken": "^1.0.7", + "ansi-styles": "^5.0.0", + "binary-extensions": "^2.2.0", + "browser-or-node": "^2.1.1", + "expr-eval": "^2.0.2", + "flat": "^5.0.2", + "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", + "@clickhouse/client": "^0.0.14", + "@getmetal/metal-sdk": "*", + "@huggingface/inference": "^1.5.1", + "@opensearch-project/opensearch": "*", + "@pinecone-database/pinecone": "*", + "@supabase/supabase-js": "^2.10.0", + "@tensorflow-models/universal-sentence-encoder": "*", + "@tensorflow/tfjs-converter": "*", + "@tensorflow/tfjs-core": "*", + "@zilliz/milvus2-sdk-node": "^2.2.0", + "axios": "*", + "cheerio": "^1.0.0-rc.12", + "chromadb": "^1.4.0", + "cohere-ai": "^5.0.2", + "d3-dsv": "^2.0.0", + "epub2": "^3.0.1", + "hnswlib-node": "^1.4.2", + "html-to-text": "^9.0.5", + "mammoth": "*", + "mongodb": "^5.2.0", + "pdf-parse": "1.1.1", + "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 + }, + "@clickhouse/client": { + "optional": true + }, + "@getmetal/metal-sdk": { + "optional": true + }, + "@huggingface/inference": { + "optional": true + }, + "@opensearch-project/opensearch": { + "optional": true + }, + "@pinecone-database/pinecone": { + "optional": true + }, + "@supabase/supabase-js": { + "optional": true + }, + "@tensorflow-models/universal-sentence-encoder": { + "optional": true + }, + "@tensorflow/tfjs-converter": { + "optional": true + }, + "@tensorflow/tfjs-core": { + "optional": true + }, + "@zilliz/milvus2-sdk-node": { + "optional": true + }, + "axios": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "chromadb": { + "optional": true + }, + "cohere-ai": { + "optional": true + }, + "d3-dsv": { + "optional": true + }, + "epub2": { + "optional": true + }, + "hnswlib-node": { + "optional": true + }, + "html-to-text": { + "optional": true + }, + "mammoth": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "pdf-parse": { + "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/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -7163,6 +7414,46 @@ "node": "*" } }, + "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.0", + "resolved": "https://registry.npmjs.org/ml-distance/-/ml-distance-4.0.0.tgz", + "integrity": "sha512-zj7+UGZpHk3uL7n79XTfGNUjIGnhLn8xVvrxYvBHvXFxo3jq1q+/UjP311hZxnLVhbxbXCjUniThX8gozjacYA==", + "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/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -7297,6 +7588,44 @@ "react-i18next": "^12.2.0" } }, + "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-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/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/node-fetch/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/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7341,6 +7670,17 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "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/nwsapi": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", @@ -7355,6 +7695,14 @@ "node": ">=0.10.0" } }, + "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/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -7445,6 +7793,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/openai/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/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -7463,6 +7828,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", @@ -7494,6 +7867,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-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": "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-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8133,6 +8544,14 @@ "node": ">=10" } }, + "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", @@ -8932,6 +9351,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/uvu": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", @@ -9255,6 +9682,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", @@ -9293,6 +9728,22 @@ "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" + } } } } diff --git a/package.json b/package.json index 6b2aac53..c70d36e8 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "axios": "^1.3.4", "esbuild-plugin-svg": "^0.1.0", "eventsource-parser": "^1.0.0", + "langchain": "^0.0.75", "next-i18next": "^13.2.2", "prop-types": "^15.8.1", "react": "^18.2.0", diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index 9c388cfa..05c3e5ae 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -1,10 +1,10 @@ import ChatIcons from '@/components/ChatComponents/ChatIcons'; import ChatInput from '@/components/ChatComponents/ChatInput'; import ChatMessages from '@/components/ChatComponents/ChatMessages'; -import { USER_SENDER } from '@/constants'; +import { DEFAULT_SYSTEM_PROMPT, USER_SENDER } from '@/constants'; import { AppContext } from '@/context'; +import { LangChainParams, getAIResponse } from '@/langchainStream'; import { CopilotSettings } from '@/main'; -import { OpenAIRequestManager, OpenAiParams, getAIResponse } from '@/openAiStream'; import SharedState, { ChatMessage, useSharedState } from '@/sharedState'; import { createChangeToneSelectionPrompt, @@ -43,7 +43,6 @@ interface ChatProps { settings: CopilotSettings; model: string; emitter: EventEmitter; - streamManager: OpenAIRequestManager; stream: boolean; getChatVisibility: () => Promise; userSystemPrompt: string; @@ -51,7 +50,7 @@ interface ChatProps { } const Chat: React.FC = ({ - sharedState, settings, model, emitter, streamManager, stream, getChatVisibility, + sharedState, settings, model, emitter, stream, getChatVisibility, userSystemPrompt, debug }) => { const [ @@ -60,6 +59,7 @@ const Chat: React.FC = ({ const [inputMessage, setInputMessage] = useState(''); const [currentAiMessage, setCurrentAiMessage] = useState(''); const [currentModel, setCurrentModel] = useState(model); + const [abortController, setAbortController] = useState(null); const app = useContext(AppContext); const { @@ -68,11 +68,14 @@ const Chat: React.FC = ({ maxTokens, contextTurns, } = sanitizeSettings(settings); - const openAiParams: OpenAiParams = { + + const systemPrompt = userSystemPrompt || DEFAULT_SYSTEM_PROMPT; + const langChainParams: LangChainParams = { key: openAiApiKey, model: currentModel, temperature: Number(temperature), maxTokens: Number(maxTokens), + systemMessage: systemPrompt, } // The number of past conversation turns to use as context for the AI // The number of messages is doubled. @@ -96,13 +99,12 @@ const Chat: React.FC = ({ await getAIResponse( userMessage, chatContext, - openAiParams, - streamManager, + langChainParams, setCurrentAiMessage, addMessage, + setAbortController, stream, debug, - userSystemPrompt, ); }; @@ -161,13 +163,12 @@ const Chat: React.FC = ({ await getAIResponse( promptMessage, chatContext, - openAiParams, - streamManager, + langChainParams, setCurrentAiMessage, addMessage, + setAbortController, stream, debug, - userSystemPrompt, ); }; @@ -176,7 +177,10 @@ const Chat: React.FC = ({ }; const handleStopGenerating = () => { - streamManager.stopStreaming(); + if (abortController) { + console.log("User stopping generation..."); + abortController.abort(); + } }; // Create an effect for each event type (command) @@ -195,18 +199,18 @@ const Chat: React.FC = ({ }; // Have a hardcoded custom temperature for some commands that need more strictness - const updatedOpenAiParams = { - ...openAiParams, + const updatedLangChainParams = { + ...langChainParams, ...(custom_temperature && { temperature: custom_temperature }), }; await getAIResponse( promptMessage, [], - updatedOpenAiParams, - streamManager, + updatedLangChainParams, setCurrentAiMessage, addMessage, + setAbortController, stream, debug, ); diff --git a/src/components/CopilotView.tsx b/src/components/CopilotView.tsx index 775018a7..9b26e4b3 100644 --- a/src/components/CopilotView.tsx +++ b/src/components/CopilotView.tsx @@ -2,7 +2,6 @@ import Chat from '@/components/Chat'; import { CHAT_VIEWTYPE } from '@/constants'; import { AppContext } from '@/context'; import CopilotPlugin, { CopilotSettings } from '@/main'; -import { OpenAIRequestManager } from '@/openAiStream'; import SharedState from '@/sharedState'; import { EventEmitter } from 'events'; import { ItemView, WorkspaceLeaf } from 'obsidian'; @@ -18,7 +17,6 @@ export default class CopilotView extends ItemView { private stream = true; private debug = false; emitter: EventEmitter; - streamManager: OpenAIRequestManager; userSystemPrompt = ''; useNotesAsContext = false; @@ -31,7 +29,6 @@ export default class CopilotView extends ItemView { this.stream = plugin.settings.stream; this.debug = plugin.settings.debug; this.emitter = new EventEmitter(); - this.streamManager = new OpenAIRequestManager(); this.getChatVisibility = this.getChatVisibility.bind(this); this.userSystemPrompt = plugin.settings.userSystemPrompt; this.useNotesAsContext = plugin.settings.useNotesAsContext; @@ -72,7 +69,6 @@ export default class CopilotView extends ItemView { settings={this.settings} model={this.model} emitter={this.emitter} - streamManager={this.streamManager} stream={this.stream} getChatVisibility={this.getChatVisibility} userSystemPrompt={this.userSystemPrompt} diff --git a/src/constants.ts b/src/constants.ts index 43281d88..43fa4335 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,10 +1,10 @@ import { CopilotSettings } from '@/main'; -export const CHAR_LENGTH_LIMIT = 5800; +export const CHAR_LENGTH_LIMIT = 5800; // TODO: Remove this after unlimited context support export const CHAT_VIEWTYPE = 'copilot-chat-view'; export const USER_SENDER = 'user'; export const AI_SENDER = 'ai'; -export const DEFAULT_SYSTEM_PROMPT = 'You are a helpful assistant named Copilot for Obsidian.'; +export const DEFAULT_SYSTEM_PROMPT = 'You are Obsidian Copilot, a helpful assistant that integrates AI to Obsidian note-taking.'; export const DEFAULT_SETTINGS: CopilotSettings = { openAiApiKey: '', defaultModel: 'gpt-3.5-turbo', diff --git a/src/langchainStream.ts b/src/langchainStream.ts new file mode 100644 index 00000000..fc362fcb --- /dev/null +++ b/src/langchainStream.ts @@ -0,0 +1,89 @@ +import { AI_SENDER, USER_SENDER } from '@/constants'; +import { ChatMessage } from '@/sharedState'; +import { ChatOpenAI } from 'langchain/chat_models/openai'; +import { AIChatMessage, HumanChatMessage, SystemChatMessage } from 'langchain/schema'; +import { Notice } from 'obsidian'; + +export type Role = 'assistant' | 'user' | 'system'; + +export interface LangChainParams { + key: string, + model: string, + temperature: number, + maxTokens: number, + systemMessage: string, +} + +export const getAIResponse = async ( + userMessage: ChatMessage, + chatContext: ChatMessage[], + langChainParams: LangChainParams, + updateCurrentAiMessage: (message: string) => void, + addMessage: (message: ChatMessage) => void, + updateShouldAbort: (abortController: AbortController | null) => void, + stream = true, + debug = false, +) => { + const { + key, model, temperature, maxTokens, systemMessage, + } = langChainParams; + + const messages = [ + new SystemChatMessage(systemMessage), + ...chatContext.map((chatMessage) => { + return chatMessage.sender === USER_SENDER + ? new HumanChatMessage(chatMessage.message) + : new AIChatMessage(chatMessage.message); + }), + new HumanChatMessage(userMessage.message), + ]; + + if (debug) { + console.log('langChainParams:', langChainParams); + console.log('system message:', systemMessage); + for (const [i, chatMessage] of chatContext.entries()) { + console.log( + `chat message ${i}:\nsender: ${chatMessage.sender}\n${chatMessage.message}` + ); + } + } + + const abortController = new AbortController(); + + try { + let fullAIResponse = ''; + const chatOpenAI = new ChatOpenAI({ + openAIApiKey: key, + modelName: model, + temperature: temperature, + maxTokens: maxTokens, + streaming: stream, + callbacks: [ + { + handleLLMNewToken: (token) => { + fullAIResponse += token; + updateCurrentAiMessage(fullAIResponse); + }, + }, + ], + }); + + updateShouldAbort(abortController); + await chatOpenAI.call( + messages, + { signal: abortController.signal } + ); + addMessage({ + message: fullAIResponse, + sender: AI_SENDER, + isVisible: true, + }); + updateCurrentAiMessage(''); + + } catch (error) { + const errorData = error?.response?.data?.error || error; + const errorCode = errorData?.code || error; + new Notice(`LangChain error: ${errorCode}`); + console.error(errorData); + } +}; diff --git a/src/settings.ts b/src/settings.ts index 5f7756fc..ac705525 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -84,21 +84,6 @@ export class CopilotSettingTab extends PluginSettingTab { }); }); - new Setting(containerEl) - .setName("Streaming mode") - .setDesc( - createFragment((frag) => { - frag.appendText("Stream the response from the API as it comes in. It can take a while for the API to respond, so keeping it on is recommended."); - }) - ) - .addToggle(toggle => toggle - .setValue(this.plugin.settings.stream) - .onChange(async (value) => { - this.plugin.settings.stream = value; - await this.plugin.saveSettings(); - }) - ); - containerEl.createEl( 'h6', { diff --git a/wasmPlugin.mjs b/wasmPlugin.mjs new file mode 100644 index 00000000..cd53a712 --- /dev/null +++ b/wasmPlugin.mjs @@ -0,0 +1,38 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; + +const wasmPlugin = { + name: 'wasm', + setup(build) { + build.onResolve({ filter: /\.wasm$/ }, args => { + if (args.namespace === 'wasm-stub') { + return { + path: args.path, + namespace: 'wasm-binary', + } + } + + if (args.resolveDir === '') { + return + } + return { + path: path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path), + namespace: 'wasm-stub', + } + }) + + build.onLoad({ filter: /.*/, namespace: 'wasm-stub' }, async (args) => ({ + contents: `import wasm from ${JSON.stringify(args.path)} + export default (imports) => + WebAssembly.instantiate(wasm, imports).then( + result => result.instance.exports)`, + })) + + build.onLoad({ filter: /.*/, namespace: 'wasm-binary' }, async (args) => ({ + contents: await fs.readFile(args.path), + loader: 'binary', + })) + }, +} + +export default wasmPlugin;