From 5b1aaee60534ce7ad6ebb5e7ad199e839b50d81e Mon Sep 17 00:00:00 2001 From: Jin Yucong Date: Fri, 5 Jul 2024 14:19:53 +0800 Subject: [PATCH 01/56] chore: Update eslint and prettier configurations --- .editorconfig | 14 + .eslintignore | 2 + .eslintrc.json | 11 + .prettierrc | 5 + package.json | 6 +- src/agents/academicSearchAgent.ts | 2 +- src/agents/redditSearchAgent.ts | 2 +- src/agents/webSearchAgent.ts | 2 +- src/agents/wolframAlphaSearchAgent.ts | 7 +- src/agents/writingAssistant.ts | 5 +- src/agents/youtubeSearchAgent.ts | 2 +- src/config.ts | 2 +- src/lib/huggingfaceTransformer.ts | 1 + src/lib/providers.ts | 2 + src/routes/images.ts | 5 +- src/routes/suggestions.ts | 5 +- src/routes/videos.ts | 5 +- ui/.eslintignore | 2 + ui/.eslintrc.json | 2 +- ui/.prettierrc | 5 + ui/.prettierrc.js | 11 - ui/app/library/page.tsx | 2 +- ui/components/ChatWindow.tsx | 1 + yarn.lock | 763 +++++++++++++++++++++++++- 24 files changed, 826 insertions(+), 38 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .prettierrc create mode 100644 ui/.eslintignore create mode 100644 ui/.prettierrc delete mode 100644 ui/.prettierrc.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..37dfa3ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# editorconfig.org +root = true + +[*] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +quote_type = double +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..76add878 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..829d010b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ] +} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..3a181a15 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "endOfLine": "auto", + "trailingComma": "all", + "arrowParens": "avoid" +} diff --git a/package.json b/package.json index 4f2bb32b..097f054a 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,17 @@ "build": "tsc", "dev": "nodemon src/app.ts", "db:push": "drizzle-kit push sqlite", - "format": "prettier . --check", - "format:write": "prettier . --write" + "lint": "eslint ." }, "devDependencies": { "@types/better-sqlite3": "^7.6.10", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/readable-stream": "^4.0.11", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", "drizzle-kit": "^0.22.7", + "eslint": "^8", "nodemon": "^3.1.0", "prettier": "^3.2.5", "ts-node": "^10.9.2", diff --git a/src/agents/academicSearchAgent.ts b/src/agents/academicSearchAgent.ts index 5c11307a..43bd88da 100644 --- a/src/agents/academicSearchAgent.ts +++ b/src/agents/academicSearchAgent.ts @@ -66,7 +66,7 @@ const basicAcademicSearchResponsePrompt = ` const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { diff --git a/src/agents/redditSearchAgent.ts b/src/agents/redditSearchAgent.ts index 34e9ec2b..2f3fb667 100644 --- a/src/agents/redditSearchAgent.ts +++ b/src/agents/redditSearchAgent.ts @@ -66,7 +66,7 @@ const basicRedditSearchResponsePrompt = ` const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { diff --git a/src/agents/webSearchAgent.ts b/src/agents/webSearchAgent.ts index 1364742b..9e36d52c 100644 --- a/src/agents/webSearchAgent.ts +++ b/src/agents/webSearchAgent.ts @@ -66,7 +66,7 @@ const basicWebSearchResponsePrompt = ` const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { diff --git a/src/agents/wolframAlphaSearchAgent.ts b/src/agents/wolframAlphaSearchAgent.ts index f810a1e0..5b915532 100644 --- a/src/agents/wolframAlphaSearchAgent.ts +++ b/src/agents/wolframAlphaSearchAgent.ts @@ -65,7 +65,7 @@ const basicWolframAlphaSearchResponsePrompt = ` const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { @@ -153,7 +153,7 @@ const createBasicWolframAlphaSearchAnsweringChain = (llm: BaseChatModel) => { chat_history: formatChatHistoryAsString(input.chat_history), }), basicWolframAlphaSearchRetrieverChain - .pipe(({ query, docs }) => { + .pipe(({ docs }) => { return docs; }) .withConfig({ @@ -210,7 +210,8 @@ const handleWolframAlphaSearch = ( message: string, history: BaseMessage[], llm: BaseChatModel, - embeddings: Embeddings, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _embeddings: Embeddings, ) => { const emitter = basicWolframAlphaSearch(message, history, llm); return emitter; diff --git a/src/agents/writingAssistant.ts b/src/agents/writingAssistant.ts index 7c2cb498..6156c96b 100644 --- a/src/agents/writingAssistant.ts +++ b/src/agents/writingAssistant.ts @@ -19,7 +19,7 @@ Since you are a writing assistant, you would not perform web searches. If you th const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { @@ -59,7 +59,8 @@ const handleWritingAssistant = ( query: string, history: BaseMessage[], llm: BaseChatModel, - embeddings: Embeddings, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _embeddings: Embeddings, ) => { const emitter = new eventEmitter(); diff --git a/src/agents/youtubeSearchAgent.ts b/src/agents/youtubeSearchAgent.ts index 4e82cc78..7c5b8a37 100644 --- a/src/agents/youtubeSearchAgent.ts +++ b/src/agents/youtubeSearchAgent.ts @@ -66,7 +66,7 @@ const basicYoutubeSearchResponsePrompt = ` const strParser = new StringOutputParser(); const handleStream = async ( - stream: AsyncGenerator, + stream: AsyncGenerator, emitter: eventEmitter, ) => { for await (const event of stream) { diff --git a/src/config.ts b/src/config.ts index 7c0c7f14..8d3c9da2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -26,7 +26,7 @@ type RecursivePartial = { const loadConfig = () => toml.parse( fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'), - ) as any as Config; + ) as unknown as Config; export const getPort = () => loadConfig().GENERAL.PORT; diff --git a/src/lib/huggingfaceTransformer.ts b/src/lib/huggingfaceTransformer.ts index 7a959ca3..02c085ac 100644 --- a/src/lib/huggingfaceTransformer.ts +++ b/src/lib/huggingfaceTransformer.ts @@ -28,6 +28,7 @@ export class HuggingFaceTransformersEmbeddings timeout?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any private pipelinePromise: Promise; constructor(fields?: Partial) { diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 32231936..62cad14b 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -102,6 +102,7 @@ export const getAvailableChatModelProviders = async () => { }, }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const { models: ollamaModels } = (await response.json()) as any; models['ollama'] = ollamaModels.reduce((acc, model) => { @@ -153,6 +154,7 @@ export const getAvailableEmbeddingModelProviders = async () => { }, }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const { models: ollamaModels } = (await response.json()) as any; models['ollama'] = ollamaModels.reduce((acc, model) => { diff --git a/src/routes/images.ts b/src/routes/images.ts index 6bd43d3c..2650ab6f 100644 --- a/src/routes/images.ts +++ b/src/routes/images.ts @@ -9,9 +9,10 @@ const router = express.Router(); router.post('/', async (req, res) => { try { - let { query, chat_history, chat_model_provider, chat_model } = req.body; + const { query, chat_history: raw_chat_history, chat_model_provider, chat_model } = req.body; - chat_history = chat_history.map((msg: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const chat_history = raw_chat_history.map((msg: any) => { if (msg.role === 'user') { return new HumanMessage(msg.content); } else if (msg.role === 'assistant') { diff --git a/src/routes/suggestions.ts b/src/routes/suggestions.ts index b15ff5f8..e8a7e1fa 100644 --- a/src/routes/suggestions.ts +++ b/src/routes/suggestions.ts @@ -9,9 +9,10 @@ const router = express.Router(); router.post('/', async (req, res) => { try { - let { chat_history, chat_model, chat_model_provider } = req.body; + const { chat_history: raw_chat_history, chat_model, chat_model_provider } = req.body; - chat_history = chat_history.map((msg: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const chat_history = raw_chat_history.map((msg: any) => { if (msg.role === 'user') { return new HumanMessage(msg.content); } else if (msg.role === 'assistant') { diff --git a/src/routes/videos.ts b/src/routes/videos.ts index 0ffdb2c6..eef20337 100644 --- a/src/routes/videos.ts +++ b/src/routes/videos.ts @@ -9,9 +9,10 @@ const router = express.Router(); router.post('/', async (req, res) => { try { - let { query, chat_history, chat_model_provider, chat_model } = req.body; + const { query, chat_history: raw_chat_history, chat_model_provider, chat_model } = req.body; - chat_history = chat_history.map((msg: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const chat_history = raw_chat_history.map((msg: any) => { if (msg.role === 'user') { return new HumanMessage(msg.content); } else if (msg.role === 'assistant') { diff --git a/ui/.eslintignore b/ui/.eslintignore new file mode 100644 index 00000000..76add878 --- /dev/null +++ b/ui/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json index bffb357a..84c85a38 100644 --- a/ui/.eslintrc.json +++ b/ui/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": "next/core-web-vitals" + "overrides": [] } diff --git a/ui/.prettierrc b/ui/.prettierrc new file mode 100644 index 00000000..3a181a15 --- /dev/null +++ b/ui/.prettierrc @@ -0,0 +1,5 @@ +{ + "endOfLine": "auto", + "trailingComma": "all", + "arrowParens": "avoid" +} diff --git a/ui/.prettierrc.js b/ui/.prettierrc.js deleted file mode 100644 index 8ca480f1..00000000 --- a/ui/.prettierrc.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import("prettier").Config} */ - -const config = { - printWidth: 80, - trailingComma: 'all', - endOfLine: 'auto', - singleQuote: true, - tabWidth: 2, -}; - -module.exports = config; diff --git a/ui/app/library/page.tsx b/ui/app/library/page.tsx index 8294fc1b..586765d0 100644 --- a/ui/app/library/page.tsx +++ b/ui/app/library/page.tsx @@ -2,7 +2,7 @@ import DeleteChat from '@/components/DeleteChat'; import { formatTimeDifference } from '@/lib/utils'; -import { BookOpenText, ClockIcon, Delete, ScanEye } from 'lucide-react'; +import { BookOpenText, ClockIcon } from 'lucide-react'; import Link from 'next/link'; import { useEffect, useState } from 'react'; diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index 675df49d..d66c999a 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; import { useEffect, useRef, useState } from 'react'; diff --git a/yarn.lock b/yarn.lock index dceddbd0..b220888f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -279,11 +279,62 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + 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" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + "@huggingface/jinja@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@huggingface/jinja/-/jinja-0.2.2.tgz#faeb205a9d6995089bef52655ddd8245d3190627" integrity sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA== +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + "@iarna/toml@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -413,6 +464,27 @@ "@langchain/core" ">0.2.0 <0.3.0" js-tiktoken "^1.0.12" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -629,6 +701,92 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== +"@typescript-eslint/eslint-plugin@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz#8eaf396ac2992d2b8f874b68eb3fcd6b179cb7f3" + integrity sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.15.0" + "@typescript-eslint/type-utils" "7.15.0" + "@typescript-eslint/utils" "7.15.0" + "@typescript-eslint/visitor-keys" "7.15.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.15.0.tgz#f4a536e5fc6a1c05c82c4d263a2bfad2da235c80" + integrity sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A== + dependencies: + "@typescript-eslint/scope-manager" "7.15.0" + "@typescript-eslint/types" "7.15.0" + "@typescript-eslint/typescript-estree" "7.15.0" + "@typescript-eslint/visitor-keys" "7.15.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz#201b34b0720be8b1447df17b963941bf044999b2" + integrity sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw== + dependencies: + "@typescript-eslint/types" "7.15.0" + "@typescript-eslint/visitor-keys" "7.15.0" + +"@typescript-eslint/type-utils@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz#5b83c904c6de91802fb399305a50a56d10472c39" + integrity sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg== + dependencies: + "@typescript-eslint/typescript-estree" "7.15.0" + "@typescript-eslint/utils" "7.15.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.15.0.tgz#fb894373a6e3882cbb37671ffddce44f934f62fc" + integrity sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw== + +"@typescript-eslint/typescript-estree@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz#e323bfa3966e1485b638ce751f219fc1f31eba37" + integrity sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ== + dependencies: + "@typescript-eslint/types" "7.15.0" + "@typescript-eslint/visitor-keys" "7.15.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.15.0.tgz#9e6253c4599b6e7da2fb64ba3f549c73eb8c1960" + integrity sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.15.0" + "@typescript-eslint/types" "7.15.0" + "@typescript-eslint/typescript-estree" "7.15.0" + +"@typescript-eslint/visitor-keys@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz#1da0726201a859343fe6a05742a7c1792fff5b66" + integrity sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw== + dependencies: + "@typescript-eslint/types" "7.15.0" + eslint-visitor-keys "^3.4.3" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@xenova/transformers@^2.17.1": version "2.17.1" resolved "https://registry.yarnpkg.com/@xenova/transformers/-/transformers-2.17.1.tgz#712f7a72c76c8aa2075749382f83dc7dd4e5a9a5" @@ -660,6 +818,11 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-walk@^8.1.1: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" @@ -670,6 +833,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -677,6 +845,28 @@ agentkeepalive@^4.2.1: dependencies: humanize-ms "^1.2.1" +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + ansi-styles@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" @@ -705,6 +895,11 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + async@^3.2.3: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" @@ -837,6 +1032,20 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -873,11 +1082,24 @@ call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase@6: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + charenc@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -1037,6 +1259,15 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypt@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" @@ -1056,7 +1287,7 @@ debug@^4: dependencies: ms "2.1.2" -debug@^4.3.4: +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== @@ -1080,6 +1311,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -1122,6 +1358,20 @@ digest-fetch@^1.3.0: base-64 "^0.1.0" md5 "^2.3.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dotenv@^16.4.5: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" @@ -1244,6 +1494,101 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + 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.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -1306,16 +1651,56 @@ express@^4.19.2: utils-merge "1.0.1" vary "~1.1.2" +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-fifo@^1.1.0, fast-fifo@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -1328,6 +1713,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -1341,6 +1733,23 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" @@ -1351,6 +1760,11 @@ flatbuffers@^1.12.0: resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-1.12.0.tgz#72e87d1726cb1b216e839ef02658aa87dcef68aa" integrity sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ== +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" @@ -1398,6 +1812,11 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -1431,13 +1850,51 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== -glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -1445,6 +1902,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + guid-typescript@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/guid-typescript/-/guid-typescript-1.0.9.tgz#e35f77003535b0297ea08548f5ace6adb1480ddc" @@ -1455,6 +1917,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -1514,7 +1981,33 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1556,7 +2049,7 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1568,11 +2061,21 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + js-tiktoken@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.12.tgz#af0f5cf58e5e7318240d050c8413234019424211" @@ -1594,11 +2097,33 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + jsonpointer@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kuler@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" @@ -1677,6 +2202,26 @@ langsmith@~0.1.30: p-retry "4" uuid "^9.0.0" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -1730,11 +2275,24 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -1757,13 +2315,20 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@^3.1.2: +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.3: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -1835,6 +2400,11 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -1914,7 +2484,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -1993,11 +2563,37 @@ openapi-types@^12.1.3: resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" @@ -2021,17 +2617,44 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -picomatch@^2.0.4, picomatch@^2.2.1: +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -2059,6 +2682,11 @@ prebuild-install@^7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prettier@^3.2.5: version "3.2.5" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" @@ -2109,6 +2737,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -2116,6 +2749,11 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + queue-tick@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" @@ -2162,6 +2800,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" @@ -2172,6 +2815,25 @@ retry@^0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -2199,6 +2861,11 @@ semver@^7.3.5, semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -2259,6 +2926,18 @@ sharp@^0.32.0: tar-fs "^3.0.4" tunnel-agent "^0.6.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + side-channel@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -2297,6 +2976,11 @@ simple-update-notifier@^2.0.0: dependencies: semver "^7.5.3" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -2337,6 +3021,18 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -2349,6 +3045,13 @@ supports-color@^5.5.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -2395,6 +3098,11 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2424,6 +3132,11 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" @@ -2450,6 +3163,18 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -2478,6 +3203,13 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -2536,6 +3268,13 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + winston-transport@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" @@ -2562,6 +3301,11 @@ winston@^3.13.0: triple-beam "^1.3.0" winston-transport "^4.7.0" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -2587,6 +3331,11 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zod-to-json-schema@^3.22.3: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673" From 3b737a078aaedd26e42f202c1049ff293cfbe394 Mon Sep 17 00:00:00 2001 From: Jin Yucong Date: Fri, 5 Jul 2024 14:36:50 +0800 Subject: [PATCH 02/56] prettier --- .eslintrc.json | 6 +- drizzle.config.ts | 10 +- package.json | 2 + src/agents/academicSearchAgent.ts | 145 +++----- src/agents/imageSearchAgent.ts | 27 +- src/agents/redditSearchAgent.ts | 142 +++---- src/agents/suggestionGeneratorAgent.ts | 24 +- src/agents/videoSearchAgent.ts | 34 +- src/agents/webSearchAgent.ts | 139 +++---- src/agents/wolframAlphaSearchAgent.ts | 109 ++---- src/agents/writingAssistant.ts | 58 +-- src/agents/youtubeSearchAgent.ts | 142 +++---- src/app.ts | 22 +- src/config.ts | 30 +- src/db/index.ts | 8 +- src/db/schema.ts | 28 +- src/lib/huggingfaceTransformer.ts | 36 +- src/lib/outputParsers/listLineOutputParser.ts | 19 +- src/lib/providers.ts | 94 +++-- src/lib/searxng.ts | 15 +- src/routes/chats.ts | 31 +- src/routes/config.ts | 38 +- src/routes/images.ts | 22 +- src/routes/index.ts | 26 +- src/routes/models.ts | 13 +- src/routes/suggestions.ts | 22 +- src/routes/videos.ts | 22 +- src/utils/computeSimilarity.ts | 12 +- src/utils/formatHistory.ts | 6 +- src/utils/logger.ts | 16 +- src/websocket/connectionManager.ts | 77 ++-- src/websocket/index.ts | 8 +- src/websocket/messageHandler.ts | 103 +++--- src/websocket/websocketServer.ts | 16 +- ui/app/c/[chatId]/page.tsx | 2 +- ui/app/layout.tsx | 31 +- ui/app/library/layout.tsx | 6 +- ui/app/library/page.tsx | 34 +- ui/app/page.tsx | 10 +- ui/components/Chat.tsx | 25 +- ui/components/ChatWindow.tsx | 237 +++++------- ui/components/DeleteChat.tsx | 27 +- ui/components/EmptyChat.tsx | 12 +- ui/components/EmptyChatMessageInput.tsx | 35 +- ui/components/MessageActions/Copy.tsx | 14 +- ui/components/MessageActions/Rewrite.tsx | 10 +- ui/components/MessageBox.tsx | 105 ++---- ui/components/MessageInput.tsx | 70 ++-- ui/components/MessageInputActions/Attach.tsx | 2 +- ui/components/MessageInputActions/Copilot.tsx | 16 +- ui/components/MessageInputActions/Focus.tsx | 97 ++--- ui/components/MessageSources.tsx | 25 +- ui/components/Navbar.tsx | 41 +-- ui/components/SearchImages.tsx | 63 ++-- ui/components/SearchVideos.tsx | 73 ++-- ui/components/SettingsDialog.tsx | 346 +++++++----------- ui/components/Sidebar.tsx | 64 ++-- ui/components/theme/Provider.tsx | 10 +- ui/components/theme/Switcher.tsx | 30 +- ui/lib/actions.ts | 10 +- ui/lib/utils.ts | 25 +- ui/tailwind.config.ts | 26 +- yarn.lock | 43 +++ 63 files changed, 1135 insertions(+), 1856 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 829d010b..65d539ec 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,10 +2,12 @@ "root": true, "extends": [ "eslint:recommended", - "plugin:@typescript-eslint/recommended" + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" ], "parser": "@typescript-eslint/parser", "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "prettier" ] } \ No newline at end of file diff --git a/drizzle.config.ts b/drizzle.config.ts index 9ac3ec50..802a5fda 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from 'drizzle-kit'; +import { defineConfig } from "drizzle-kit"; export default defineConfig({ - dialect: 'sqlite', - schema: './src/db/schema.ts', - out: './drizzle', + dialect: "sqlite", + schema: "./src/db/schema.ts", + out: "./drizzle", dbCredentials: { - url: './data/db.sqlite', + url: "./data/db.sqlite", }, }); diff --git a/package.json b/package.json index 097f054a..b6f3d684 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "@typescript-eslint/parser": "^7.15.0", "drizzle-kit": "^0.22.7", "eslint": "^8", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", "nodemon": "^3.1.0", "prettier": "^3.2.5", "ts-node": "^10.9.2", diff --git a/src/agents/academicSearchAgent.ts b/src/agents/academicSearchAgent.ts index 43bd88da..870981c6 100644 --- a/src/agents/academicSearchAgent.ts +++ b/src/agents/academicSearchAgent.ts @@ -1,24 +1,16 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - PromptTemplate, - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { Document } from '@langchain/core/documents'; -import { searchSearxng } from '../lib/searxng'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import eventEmitter from 'events'; -import computeSimilarity from '../utils/computeSimilarity'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { PromptTemplate, ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { Document } from "@langchain/core/documents"; +import { searchSearxng } from "../lib/searxng"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import eventEmitter from "events"; +import computeSimilarity from "../utils/computeSimilarity"; +import logger from "../utils/logger"; const basicAcademicSearchRetrieverPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information. @@ -65,34 +57,16 @@ const basicAcademicSearchResponsePrompt = ` const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_end' && - event.name === 'FinalSourceRetriever' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'sources', data: event.data.output }), - ); + if (event.event === "on_chain_end" && event.name === "FinalSourceRetriever") { + emitter.emit("data", JSON.stringify({ type: "sources", data: event.data.output })); } - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -108,22 +82,17 @@ const createBasicAcademicSearchRetrieverChain = (llm: BaseChatModel) => { llm, strParser, RunnableLambda.from(async (input: string) => { - if (input === 'not_needed') { - return { query: '', docs: [] }; + if (input === "not_needed") { + return { query: "", docs: [] }; } const res = await searchSearxng(input, { - language: 'en', - engines: [ - 'arxiv', - 'google scholar', - 'internetarchivescholar', - 'pubmed', - ], + language: "en", + engines: ["arxiv", "google scholar", "internetarchivescholar", "pubmed"], }); const documents = res.results.map( - (result) => + result => new Document({ pageContent: result.content, metadata: { @@ -139,36 +108,22 @@ const createBasicAcademicSearchRetrieverChain = (llm: BaseChatModel) => { ]); }; -const createBasicAcademicSearchAnsweringChain = ( - llm: BaseChatModel, - embeddings: Embeddings, -) => { - const basicAcademicSearchRetrieverChain = - createBasicAcademicSearchRetrieverChain(llm); +const createBasicAcademicSearchAnsweringChain = (llm: BaseChatModel, embeddings: Embeddings) => { + const basicAcademicSearchRetrieverChain = createBasicAcademicSearchRetrieverChain(llm); const processDocs = async (docs: Document[]) => { - return docs - .map((_, index) => `${index + 1}. ${docs[index].pageContent}`) - .join('\n'); + return docs.map((_, index) => `${index + 1}. ${docs[index].pageContent}`).join("\n"); }; - const rerankDocs = async ({ - query, - docs, - }: { - query: string; - docs: Document[]; - }) => { + const rerankDocs = async ({ query, docs }: { query: string; docs: Document[] }) => { if (docs.length === 0) { return docs; } - const docsWithContent = docs.filter( - (doc) => doc.pageContent && doc.pageContent.length > 0, - ); + const docsWithContent = docs.filter(doc => doc.pageContent && doc.pageContent.length > 0); const [docEmbeddings, queryEmbedding] = await Promise.all([ - embeddings.embedDocuments(docsWithContent.map((doc) => doc.pageContent)), + embeddings.embedDocuments(docsWithContent.map(doc => doc.pageContent)), embeddings.embedQuery(query), ]); @@ -184,7 +139,7 @@ const createBasicAcademicSearchAnsweringChain = ( const sortedDocs = similarity .sort((a, b) => b.similarity - a.similarity) .slice(0, 15) - .map((sim) => docsWithContent[sim.index]); + .map(sim => docsWithContent[sim.index]); return sortedDocs; }; @@ -194,41 +149,35 @@ const createBasicAcademicSearchAnsweringChain = ( query: (input: BasicChainInput) => input.query, chat_history: (input: BasicChainInput) => input.chat_history, context: RunnableSequence.from([ - (input) => ({ + input => ({ query: input.query, chat_history: formatChatHistoryAsString(input.chat_history), }), basicAcademicSearchRetrieverChain .pipe(rerankDocs) .withConfig({ - runName: 'FinalSourceRetriever', + runName: "FinalSourceRetriever", }) .pipe(processDocs), ]), }), ChatPromptTemplate.fromMessages([ - ['system', basicAcademicSearchResponsePrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", basicAcademicSearchResponsePrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; -const basicAcademicSearch = ( - query: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const basicAcademicSearch = (query: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = new eventEmitter(); try { - const basicAcademicSearchAnsweringChain = - createBasicAcademicSearchAnsweringChain(llm, embeddings); + const basicAcademicSearchAnsweringChain = createBasicAcademicSearchAnsweringChain(llm, embeddings); const stream = basicAcademicSearchAnsweringChain.streamEvents( { @@ -236,28 +185,20 @@ const basicAcademicSearch = ( query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in academic search: ${err}`); } return emitter; }; -const handleAcademicSearch = ( - message: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const handleAcademicSearch = (message: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = basicAcademicSearch(message, history, llm, embeddings); return emitter; }; diff --git a/src/agents/imageSearchAgent.ts b/src/agents/imageSearchAgent.ts index 167019ff..08b7ca1d 100644 --- a/src/agents/imageSearchAgent.ts +++ b/src/agents/imageSearchAgent.ts @@ -1,14 +1,10 @@ -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { PromptTemplate } from '@langchain/core/prompts'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import { BaseMessage } from '@langchain/core/messages'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { searchSearxng } from '../lib/searxng'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { PromptTemplate } from "@langchain/core/prompts"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import { BaseMessage } from "@langchain/core/messages"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { searchSearxng } from "../lib/searxng"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; const imageSearchChainPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search the web for images. @@ -53,12 +49,12 @@ const createImageSearchChain = (llm: BaseChatModel) => { strParser, RunnableLambda.from(async (input: string) => { const res = await searchSearxng(input, { - engines: ['bing images', 'google images'], + engines: ["bing images", "google images"], }); const images = []; - res.results.forEach((result) => { + res.results.forEach(result => { if (result.img_src && result.url && result.title) { images.push({ img_src: result.img_src, @@ -73,10 +69,7 @@ const createImageSearchChain = (llm: BaseChatModel) => { ]); }; -const handleImageSearch = ( - input: ImageSearchChainInput, - llm: BaseChatModel, -) => { +const handleImageSearch = (input: ImageSearchChainInput, llm: BaseChatModel) => { const imageSearchChain = createImageSearchChain(llm); return imageSearchChain.invoke(input); }; diff --git a/src/agents/redditSearchAgent.ts b/src/agents/redditSearchAgent.ts index 2f3fb667..1bf9baa6 100644 --- a/src/agents/redditSearchAgent.ts +++ b/src/agents/redditSearchAgent.ts @@ -1,24 +1,16 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - PromptTemplate, - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { Document } from '@langchain/core/documents'; -import { searchSearxng } from '../lib/searxng'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import eventEmitter from 'events'; -import computeSimilarity from '../utils/computeSimilarity'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { PromptTemplate, ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { Document } from "@langchain/core/documents"; +import { searchSearxng } from "../lib/searxng"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import eventEmitter from "events"; +import computeSimilarity from "../utils/computeSimilarity"; +import logger from "../utils/logger"; const basicRedditSearchRetrieverPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information. @@ -65,34 +57,16 @@ const basicRedditSearchResponsePrompt = ` const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_end' && - event.name === 'FinalSourceRetriever' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'sources', data: event.data.output }), - ); + if (event.event === "on_chain_end" && event.name === "FinalSourceRetriever") { + emitter.emit("data", JSON.stringify({ type: "sources", data: event.data.output })); } - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -108,17 +82,17 @@ const createBasicRedditSearchRetrieverChain = (llm: BaseChatModel) => { llm, strParser, RunnableLambda.from(async (input: string) => { - if (input === 'not_needed') { - return { query: '', docs: [] }; + if (input === "not_needed") { + return { query: "", docs: [] }; } const res = await searchSearxng(input, { - language: 'en', - engines: ['reddit'], + language: "en", + engines: ["reddit"], }); const documents = res.results.map( - (result) => + result => new Document({ pageContent: result.content ? result.content : result.title, metadata: { @@ -134,36 +108,22 @@ const createBasicRedditSearchRetrieverChain = (llm: BaseChatModel) => { ]); }; -const createBasicRedditSearchAnsweringChain = ( - llm: BaseChatModel, - embeddings: Embeddings, -) => { - const basicRedditSearchRetrieverChain = - createBasicRedditSearchRetrieverChain(llm); +const createBasicRedditSearchAnsweringChain = (llm: BaseChatModel, embeddings: Embeddings) => { + const basicRedditSearchRetrieverChain = createBasicRedditSearchRetrieverChain(llm); const processDocs = async (docs: Document[]) => { - return docs - .map((_, index) => `${index + 1}. ${docs[index].pageContent}`) - .join('\n'); + return docs.map((_, index) => `${index + 1}. ${docs[index].pageContent}`).join("\n"); }; - const rerankDocs = async ({ - query, - docs, - }: { - query: string; - docs: Document[]; - }) => { + const rerankDocs = async ({ query, docs }: { query: string; docs: Document[] }) => { if (docs.length === 0) { return docs; } - const docsWithContent = docs.filter( - (doc) => doc.pageContent && doc.pageContent.length > 0, - ); + const docsWithContent = docs.filter(doc => doc.pageContent && doc.pageContent.length > 0); const [docEmbeddings, queryEmbedding] = await Promise.all([ - embeddings.embedDocuments(docsWithContent.map((doc) => doc.pageContent)), + embeddings.embedDocuments(docsWithContent.map(doc => doc.pageContent)), embeddings.embedQuery(query), ]); @@ -179,8 +139,8 @@ const createBasicRedditSearchAnsweringChain = ( const sortedDocs = similarity .sort((a, b) => b.similarity - a.similarity) .slice(0, 15) - .filter((sim) => sim.similarity > 0.3) - .map((sim) => docsWithContent[sim.index]); + .filter(sim => sim.similarity > 0.3) + .map(sim => docsWithContent[sim.index]); return sortedDocs; }; @@ -190,69 +150,55 @@ const createBasicRedditSearchAnsweringChain = ( query: (input: BasicChainInput) => input.query, chat_history: (input: BasicChainInput) => input.chat_history, context: RunnableSequence.from([ - (input) => ({ + input => ({ query: input.query, chat_history: formatChatHistoryAsString(input.chat_history), }), basicRedditSearchRetrieverChain .pipe(rerankDocs) .withConfig({ - runName: 'FinalSourceRetriever', + runName: "FinalSourceRetriever", }) .pipe(processDocs), ]), }), ChatPromptTemplate.fromMessages([ - ['system', basicRedditSearchResponsePrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", basicRedditSearchResponsePrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; -const basicRedditSearch = ( - query: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const basicRedditSearch = (query: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = new eventEmitter(); try { - const basicRedditSearchAnsweringChain = - createBasicRedditSearchAnsweringChain(llm, embeddings); + const basicRedditSearchAnsweringChain = createBasicRedditSearchAnsweringChain(llm, embeddings); const stream = basicRedditSearchAnsweringChain.streamEvents( { chat_history: history, query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in RedditSearch: ${err}`); } return emitter; }; -const handleRedditSearch = ( - message: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const handleRedditSearch = (message: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = basicRedditSearch(message, history, llm, embeddings); return emitter; }; diff --git a/src/agents/suggestionGeneratorAgent.ts b/src/agents/suggestionGeneratorAgent.ts index 0efdfa9b..7e29f111 100644 --- a/src/agents/suggestionGeneratorAgent.ts +++ b/src/agents/suggestionGeneratorAgent.ts @@ -1,10 +1,10 @@ -import { RunnableSequence, RunnableMap } from '@langchain/core/runnables'; -import ListLineOutputParser from '../lib/outputParsers/listLineOutputParser'; -import { PromptTemplate } from '@langchain/core/prompts'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import { BaseMessage } from '@langchain/core/messages'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { ChatOpenAI } from '@langchain/openai'; +import { RunnableSequence, RunnableMap } from "@langchain/core/runnables"; +import ListLineOutputParser from "../lib/outputParsers/listLineOutputParser"; +import { PromptTemplate } from "@langchain/core/prompts"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import { BaseMessage } from "@langchain/core/messages"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import { ChatOpenAI } from "@langchain/openai"; const suggestionGeneratorPrompt = ` You are an AI suggestion generator for an AI powered search engine. You will be given a conversation below. You need to generate 4-5 suggestions based on the conversation. The suggestion should be relevant to the conversation that can be used by the user to ask the chat model for more information. @@ -28,14 +28,13 @@ type SuggestionGeneratorInput = { }; const outputParser = new ListLineOutputParser({ - key: 'suggestions', + key: "suggestions", }); const createSuggestionGeneratorChain = (llm: BaseChatModel) => { return RunnableSequence.from([ RunnableMap.from({ - chat_history: (input: SuggestionGeneratorInput) => - formatChatHistoryAsString(input.chat_history), + chat_history: (input: SuggestionGeneratorInput) => formatChatHistoryAsString(input.chat_history), }), PromptTemplate.fromTemplate(suggestionGeneratorPrompt), llm, @@ -43,10 +42,7 @@ const createSuggestionGeneratorChain = (llm: BaseChatModel) => { ]); }; -const generateSuggestions = ( - input: SuggestionGeneratorInput, - llm: BaseChatModel, -) => { +const generateSuggestions = (input: SuggestionGeneratorInput, llm: BaseChatModel) => { (llm as ChatOpenAI).temperature = 0; const suggestionGeneratorChain = createSuggestionGeneratorChain(llm); return suggestionGeneratorChain.invoke(input); diff --git a/src/agents/videoSearchAgent.ts b/src/agents/videoSearchAgent.ts index cdd1ac06..ebab1d82 100644 --- a/src/agents/videoSearchAgent.ts +++ b/src/agents/videoSearchAgent.ts @@ -1,14 +1,10 @@ -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { PromptTemplate } from '@langchain/core/prompts'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import { BaseMessage } from '@langchain/core/messages'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { searchSearxng } from '../lib/searxng'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { PromptTemplate } from "@langchain/core/prompts"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import { BaseMessage } from "@langchain/core/messages"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { searchSearxng } from "../lib/searxng"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; const VideoSearchChainPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search Youtube for videos. @@ -53,18 +49,13 @@ const createVideoSearchChain = (llm: BaseChatModel) => { strParser, RunnableLambda.from(async (input: string) => { const res = await searchSearxng(input, { - engines: ['youtube'], + engines: ["youtube"], }); const videos = []; - res.results.forEach((result) => { - if ( - result.thumbnail && - result.url && - result.title && - result.iframe_src - ) { + res.results.forEach(result => { + if (result.thumbnail && result.url && result.title && result.iframe_src) { videos.push({ img_src: result.thumbnail, url: result.url, @@ -79,10 +70,7 @@ const createVideoSearchChain = (llm: BaseChatModel) => { ]); }; -const handleVideoSearch = ( - input: VideoSearchChainInput, - llm: BaseChatModel, -) => { +const handleVideoSearch = (input: VideoSearchChainInput, llm: BaseChatModel) => { const VideoSearchChain = createVideoSearchChain(llm); return VideoSearchChain.invoke(input); }; diff --git a/src/agents/webSearchAgent.ts b/src/agents/webSearchAgent.ts index 9e36d52c..b90f5092 100644 --- a/src/agents/webSearchAgent.ts +++ b/src/agents/webSearchAgent.ts @@ -1,24 +1,16 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - PromptTemplate, - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { Document } from '@langchain/core/documents'; -import { searchSearxng } from '../lib/searxng'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import eventEmitter from 'events'; -import computeSimilarity from '../utils/computeSimilarity'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { PromptTemplate, ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { Document } from "@langchain/core/documents"; +import { searchSearxng } from "../lib/searxng"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import eventEmitter from "events"; +import computeSimilarity from "../utils/computeSimilarity"; +import logger from "../utils/logger"; const basicSearchRetrieverPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information. @@ -65,34 +57,16 @@ const basicWebSearchResponsePrompt = ` const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_end' && - event.name === 'FinalSourceRetriever' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'sources', data: event.data.output }), - ); + if (event.event === "on_chain_end" && event.name === "FinalSourceRetriever") { + emitter.emit("data", JSON.stringify({ type: "sources", data: event.data.output })); } - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -108,16 +82,16 @@ const createBasicWebSearchRetrieverChain = (llm: BaseChatModel) => { llm, strParser, RunnableLambda.from(async (input: string) => { - if (input === 'not_needed') { - return { query: '', docs: [] }; + if (input === "not_needed") { + return { query: "", docs: [] }; } const res = await searchSearxng(input, { - language: 'en', + language: "en", }); const documents = res.results.map( - (result) => + result => new Document({ pageContent: result.content, metadata: { @@ -133,35 +107,22 @@ const createBasicWebSearchRetrieverChain = (llm: BaseChatModel) => { ]); }; -const createBasicWebSearchAnsweringChain = ( - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const createBasicWebSearchAnsweringChain = (llm: BaseChatModel, embeddings: Embeddings) => { const basicWebSearchRetrieverChain = createBasicWebSearchRetrieverChain(llm); const processDocs = async (docs: Document[]) => { - return docs - .map((_, index) => `${index + 1}. ${docs[index].pageContent}`) - .join('\n'); + return docs.map((_, index) => `${index + 1}. ${docs[index].pageContent}`).join("\n"); }; - const rerankDocs = async ({ - query, - docs, - }: { - query: string; - docs: Document[]; - }) => { + const rerankDocs = async ({ query, docs }: { query: string; docs: Document[] }) => { if (docs.length === 0) { return docs; } - const docsWithContent = docs.filter( - (doc) => doc.pageContent && doc.pageContent.length > 0, - ); + const docsWithContent = docs.filter(doc => doc.pageContent && doc.pageContent.length > 0); const [docEmbeddings, queryEmbedding] = await Promise.all([ - embeddings.embedDocuments(docsWithContent.map((doc) => doc.pageContent)), + embeddings.embedDocuments(docsWithContent.map(doc => doc.pageContent)), embeddings.embedQuery(query), ]); @@ -176,9 +137,9 @@ const createBasicWebSearchAnsweringChain = ( const sortedDocs = similarity .sort((a, b) => b.similarity - a.similarity) - .filter((sim) => sim.similarity > 0.5) + .filter(sim => sim.similarity > 0.5) .slice(0, 15) - .map((sim) => docsWithContent[sim.index]); + .map(sim => docsWithContent[sim.index]); return sortedDocs; }; @@ -188,43 +149,35 @@ const createBasicWebSearchAnsweringChain = ( query: (input: BasicChainInput) => input.query, chat_history: (input: BasicChainInput) => input.chat_history, context: RunnableSequence.from([ - (input) => ({ + input => ({ query: input.query, chat_history: formatChatHistoryAsString(input.chat_history), }), basicWebSearchRetrieverChain .pipe(rerankDocs) .withConfig({ - runName: 'FinalSourceRetriever', + runName: "FinalSourceRetriever", }) .pipe(processDocs), ]), }), ChatPromptTemplate.fromMessages([ - ['system', basicWebSearchResponsePrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", basicWebSearchResponsePrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; -const basicWebSearch = ( - query: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const basicWebSearch = (query: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = new eventEmitter(); try { - const basicWebSearchAnsweringChain = createBasicWebSearchAnsweringChain( - llm, - embeddings, - ); + const basicWebSearchAnsweringChain = createBasicWebSearchAnsweringChain(llm, embeddings); const stream = basicWebSearchAnsweringChain.streamEvents( { @@ -232,28 +185,20 @@ const basicWebSearch = ( query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in websearch: ${err}`); } return emitter; }; -const handleWebSearch = ( - message: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const handleWebSearch = (message: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = basicWebSearch(message, history, llm, embeddings); return emitter; }; diff --git a/src/agents/wolframAlphaSearchAgent.ts b/src/agents/wolframAlphaSearchAgent.ts index 5b915532..d8a07e7f 100644 --- a/src/agents/wolframAlphaSearchAgent.ts +++ b/src/agents/wolframAlphaSearchAgent.ts @@ -1,23 +1,15 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - PromptTemplate, - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { Document } from '@langchain/core/documents'; -import { searchSearxng } from '../lib/searxng'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import eventEmitter from 'events'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { PromptTemplate, ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { Document } from "@langchain/core/documents"; +import { searchSearxng } from "../lib/searxng"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import eventEmitter from "events"; +import logger from "../utils/logger"; const basicWolframAlphaSearchRetrieverPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information. @@ -64,34 +56,16 @@ const basicWolframAlphaSearchResponsePrompt = ` const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_end' && - event.name === 'FinalSourceRetriever' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'sources', data: event.data.output }), - ); + if (event.event === "on_chain_end" && event.name === "FinalSourceRetriever") { + emitter.emit("data", JSON.stringify({ type: "sources", data: event.data.output })); } - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -107,17 +81,17 @@ const createBasicWolframAlphaSearchRetrieverChain = (llm: BaseChatModel) => { llm, strParser, RunnableLambda.from(async (input: string) => { - if (input === 'not_needed') { - return { query: '', docs: [] }; + if (input === "not_needed") { + return { query: "", docs: [] }; } const res = await searchSearxng(input, { - language: 'en', - engines: ['wolframalpha'], + language: "en", + engines: ["wolframalpha"], }); const documents = res.results.map( - (result) => + result => new Document({ pageContent: result.content, metadata: { @@ -134,13 +108,10 @@ const createBasicWolframAlphaSearchRetrieverChain = (llm: BaseChatModel) => { }; const createBasicWolframAlphaSearchAnsweringChain = (llm: BaseChatModel) => { - const basicWolframAlphaSearchRetrieverChain = - createBasicWolframAlphaSearchRetrieverChain(llm); + const basicWolframAlphaSearchRetrieverChain = createBasicWolframAlphaSearchRetrieverChain(llm); const processDocs = (docs: Document[]) => { - return docs - .map((_, index) => `${index + 1}. ${docs[index].pageContent}`) - .join('\n'); + return docs.map((_, index) => `${index + 1}. ${docs[index].pageContent}`).join("\n"); }; return RunnableSequence.from([ @@ -148,7 +119,7 @@ const createBasicWolframAlphaSearchAnsweringChain = (llm: BaseChatModel) => { query: (input: BasicChainInput) => input.query, chat_history: (input: BasicChainInput) => input.chat_history, context: RunnableSequence.from([ - (input) => ({ + input => ({ query: input.query, chat_history: formatChatHistoryAsString(input.chat_history), }), @@ -157,49 +128,41 @@ const createBasicWolframAlphaSearchAnsweringChain = (llm: BaseChatModel) => { return docs; }) .withConfig({ - runName: 'FinalSourceRetriever', + runName: "FinalSourceRetriever", }) .pipe(processDocs), ]), }), ChatPromptTemplate.fromMessages([ - ['system', basicWolframAlphaSearchResponsePrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", basicWolframAlphaSearchResponsePrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; -const basicWolframAlphaSearch = ( - query: string, - history: BaseMessage[], - llm: BaseChatModel, -) => { +const basicWolframAlphaSearch = (query: string, history: BaseMessage[], llm: BaseChatModel) => { const emitter = new eventEmitter(); try { - const basicWolframAlphaSearchAnsweringChain = - createBasicWolframAlphaSearchAnsweringChain(llm); + const basicWolframAlphaSearchAnsweringChain = createBasicWolframAlphaSearchAnsweringChain(llm); const stream = basicWolframAlphaSearchAnsweringChain.streamEvents( { chat_history: history, query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in WolframAlphaSearch: ${err}`); } diff --git a/src/agents/writingAssistant.ts b/src/agents/writingAssistant.ts index 6156c96b..3c96167f 100644 --- a/src/agents/writingAssistant.ts +++ b/src/agents/writingAssistant.ts @@ -1,15 +1,12 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { RunnableSequence } from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import eventEmitter from 'events'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import eventEmitter from "events"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import logger from "../utils/logger"; const writingAssistantPrompt = ` You are Perplexica, an AI model who is expert at searching the web and answering user's queries. You are currently set on focus mode 'Writing Assistant', this means you will be helping the user write a response to a given query. @@ -18,25 +15,13 @@ Since you are a writing assistant, you would not perform web searches. If you th const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -44,14 +29,14 @@ const handleStream = async ( const createWritingAssistantChain = (llm: BaseChatModel) => { return RunnableSequence.from([ ChatPromptTemplate.fromMessages([ - ['system', writingAssistantPrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", writingAssistantPrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; @@ -72,16 +57,13 @@ const handleWritingAssistant = ( query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in writing assistant: ${err}`); } diff --git a/src/agents/youtubeSearchAgent.ts b/src/agents/youtubeSearchAgent.ts index 7c5b8a37..db8640eb 100644 --- a/src/agents/youtubeSearchAgent.ts +++ b/src/agents/youtubeSearchAgent.ts @@ -1,24 +1,16 @@ -import { BaseMessage } from '@langchain/core/messages'; -import { - PromptTemplate, - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { - RunnableSequence, - RunnableMap, - RunnableLambda, -} from '@langchain/core/runnables'; -import { StringOutputParser } from '@langchain/core/output_parsers'; -import { Document } from '@langchain/core/documents'; -import { searchSearxng } from '../lib/searxng'; -import type { StreamEvent } from '@langchain/core/tracers/log_stream'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import formatChatHistoryAsString from '../utils/formatHistory'; -import eventEmitter from 'events'; -import computeSimilarity from '../utils/computeSimilarity'; -import logger from '../utils/logger'; +import { BaseMessage } from "@langchain/core/messages"; +import { PromptTemplate, ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableSequence, RunnableMap, RunnableLambda } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { Document } from "@langchain/core/documents"; +import { searchSearxng } from "../lib/searxng"; +import type { StreamEvent } from "@langchain/core/tracers/log_stream"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import formatChatHistoryAsString from "../utils/formatHistory"; +import eventEmitter from "events"; +import computeSimilarity from "../utils/computeSimilarity"; +import logger from "../utils/logger"; const basicYoutubeSearchRetrieverPrompt = ` You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information. @@ -65,34 +57,16 @@ const basicYoutubeSearchResponsePrompt = ` const strParser = new StringOutputParser(); -const handleStream = async ( - stream: AsyncGenerator, - emitter: eventEmitter, -) => { +const handleStream = async (stream: AsyncGenerator, emitter: eventEmitter) => { for await (const event of stream) { - if ( - event.event === 'on_chain_end' && - event.name === 'FinalSourceRetriever' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'sources', data: event.data.output }), - ); + if (event.event === "on_chain_end" && event.name === "FinalSourceRetriever") { + emitter.emit("data", JSON.stringify({ type: "sources", data: event.data.output })); } - if ( - event.event === 'on_chain_stream' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit( - 'data', - JSON.stringify({ type: 'response', data: event.data.chunk }), - ); + if (event.event === "on_chain_stream" && event.name === "FinalResponseGenerator") { + emitter.emit("data", JSON.stringify({ type: "response", data: event.data.chunk })); } - if ( - event.event === 'on_chain_end' && - event.name === 'FinalResponseGenerator' - ) { - emitter.emit('end'); + if (event.event === "on_chain_end" && event.name === "FinalResponseGenerator") { + emitter.emit("end"); } } }; @@ -108,17 +82,17 @@ const createBasicYoutubeSearchRetrieverChain = (llm: BaseChatModel) => { llm, strParser, RunnableLambda.from(async (input: string) => { - if (input === 'not_needed') { - return { query: '', docs: [] }; + if (input === "not_needed") { + return { query: "", docs: [] }; } const res = await searchSearxng(input, { - language: 'en', - engines: ['youtube'], + language: "en", + engines: ["youtube"], }); const documents = res.results.map( - (result) => + result => new Document({ pageContent: result.content ? result.content : result.title, metadata: { @@ -134,36 +108,22 @@ const createBasicYoutubeSearchRetrieverChain = (llm: BaseChatModel) => { ]); }; -const createBasicYoutubeSearchAnsweringChain = ( - llm: BaseChatModel, - embeddings: Embeddings, -) => { - const basicYoutubeSearchRetrieverChain = - createBasicYoutubeSearchRetrieverChain(llm); +const createBasicYoutubeSearchAnsweringChain = (llm: BaseChatModel, embeddings: Embeddings) => { + const basicYoutubeSearchRetrieverChain = createBasicYoutubeSearchRetrieverChain(llm); const processDocs = async (docs: Document[]) => { - return docs - .map((_, index) => `${index + 1}. ${docs[index].pageContent}`) - .join('\n'); + return docs.map((_, index) => `${index + 1}. ${docs[index].pageContent}`).join("\n"); }; - const rerankDocs = async ({ - query, - docs, - }: { - query: string; - docs: Document[]; - }) => { + const rerankDocs = async ({ query, docs }: { query: string; docs: Document[] }) => { if (docs.length === 0) { return docs; } - const docsWithContent = docs.filter( - (doc) => doc.pageContent && doc.pageContent.length > 0, - ); + const docsWithContent = docs.filter(doc => doc.pageContent && doc.pageContent.length > 0); const [docEmbeddings, queryEmbedding] = await Promise.all([ - embeddings.embedDocuments(docsWithContent.map((doc) => doc.pageContent)), + embeddings.embedDocuments(docsWithContent.map(doc => doc.pageContent)), embeddings.embedQuery(query), ]); @@ -179,8 +139,8 @@ const createBasicYoutubeSearchAnsweringChain = ( const sortedDocs = similarity .sort((a, b) => b.similarity - a.similarity) .slice(0, 15) - .filter((sim) => sim.similarity > 0.3) - .map((sim) => docsWithContent[sim.index]); + .filter(sim => sim.similarity > 0.3) + .map(sim => docsWithContent[sim.index]); return sortedDocs; }; @@ -190,41 +150,35 @@ const createBasicYoutubeSearchAnsweringChain = ( query: (input: BasicChainInput) => input.query, chat_history: (input: BasicChainInput) => input.chat_history, context: RunnableSequence.from([ - (input) => ({ + input => ({ query: input.query, chat_history: formatChatHistoryAsString(input.chat_history), }), basicYoutubeSearchRetrieverChain .pipe(rerankDocs) .withConfig({ - runName: 'FinalSourceRetriever', + runName: "FinalSourceRetriever", }) .pipe(processDocs), ]), }), ChatPromptTemplate.fromMessages([ - ['system', basicYoutubeSearchResponsePrompt], - new MessagesPlaceholder('chat_history'), - ['user', '{query}'], + ["system", basicYoutubeSearchResponsePrompt], + new MessagesPlaceholder("chat_history"), + ["user", "{query}"], ]), llm, strParser, ]).withConfig({ - runName: 'FinalResponseGenerator', + runName: "FinalResponseGenerator", }); }; -const basicYoutubeSearch = ( - query: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const basicYoutubeSearch = (query: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = new eventEmitter(); try { - const basicYoutubeSearchAnsweringChain = - createBasicYoutubeSearchAnsweringChain(llm, embeddings); + const basicYoutubeSearchAnsweringChain = createBasicYoutubeSearchAnsweringChain(llm, embeddings); const stream = basicYoutubeSearchAnsweringChain.streamEvents( { @@ -232,28 +186,20 @@ const basicYoutubeSearch = ( query: query, }, { - version: 'v1', + version: "v1", }, ); handleStream(stream, emitter); } catch (err) { - emitter.emit( - 'error', - JSON.stringify({ data: 'An error has occurred please try again later' }), - ); + emitter.emit("error", JSON.stringify({ data: "An error has occurred please try again later" })); logger.error(`Error in youtube search: ${err}`); } return emitter; }; -const handleYoutubeSearch = ( - message: string, - history: BaseMessage[], - llm: BaseChatModel, - embeddings: Embeddings, -) => { +const handleYoutubeSearch = (message: string, history: BaseMessage[], llm: BaseChatModel, embeddings: Embeddings) => { const emitter = basicYoutubeSearch(message, history, llm, embeddings); return emitter; }; diff --git a/src/app.ts b/src/app.ts index b8c23716..05be4200 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,10 @@ -import { startWebSocketServer } from './websocket'; -import express from 'express'; -import cors from 'cors'; -import http from 'http'; -import routes from './routes'; -import { getPort } from './config'; -import logger from './utils/logger'; +import { startWebSocketServer } from "./websocket"; +import express from "express"; +import cors from "cors"; +import http from "http"; +import routes from "./routes"; +import { getPort } from "./config"; +import logger from "./utils/logger"; const port = getPort(); @@ -12,15 +12,15 @@ const app = express(); const server = http.createServer(app); const corsOptions = { - origin: '*', + origin: "*", }; app.use(cors(corsOptions)); app.use(express.json()); -app.use('/api', routes); -app.get('/api', (_, res) => { - res.status(200).json({ status: 'ok' }); +app.use("/api", routes); +app.get("/api", (_, res) => { + res.status(200).json({ status: "ok" }); }); server.listen(port, () => { diff --git a/src/config.ts b/src/config.ts index 8d3c9da2..82764d59 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,8 +1,8 @@ -import fs from 'fs'; -import path from 'path'; -import toml from '@iarna/toml'; +import fs from "fs"; +import path from "path"; +import toml from "@iarna/toml"; -const configFileName = 'config.toml'; +const configFileName = "config.toml"; interface Config { GENERAL: { @@ -24,14 +24,11 @@ type RecursivePartial = { }; const loadConfig = () => - toml.parse( - fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'), - ) as unknown as Config; + toml.parse(fs.readFileSync(path.join(__dirname, `../${configFileName}`), "utf-8")) as unknown as Config; export const getPort = () => loadConfig().GENERAL.PORT; -export const getSimilarityMeasure = () => - loadConfig().GENERAL.SIMILARITY_MEASURE; +export const getSimilarityMeasure = () => loadConfig().GENERAL.SIMILARITY_MEASURE; export const getOpenaiApiKey = () => loadConfig().API_KEYS.OPENAI; @@ -47,23 +44,16 @@ export const updateConfig = (config: RecursivePartial) => { for (const key in currentConfig) { if (!config[key]) config[key] = {}; - if (typeof currentConfig[key] === 'object' && currentConfig[key] !== null) { + if (typeof currentConfig[key] === "object" && currentConfig[key] !== null) { for (const nestedKey in currentConfig[key]) { - if ( - !config[key][nestedKey] && - currentConfig[key][nestedKey] && - config[key][nestedKey] !== '' - ) { + if (!config[key][nestedKey] && currentConfig[key][nestedKey] && config[key][nestedKey] !== "") { config[key][nestedKey] = currentConfig[key][nestedKey]; } } - } else if (currentConfig[key] && config[key] !== '') { + } else if (currentConfig[key] && config[key] !== "") { config[key] = currentConfig[key]; } } - fs.writeFileSync( - path.join(__dirname, `../${configFileName}`), - toml.stringify(config), - ); + fs.writeFileSync(path.join(__dirname, `../${configFileName}`), toml.stringify(config)); }; diff --git a/src/db/index.ts b/src/db/index.ts index b431b47f..77dc1998 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,8 +1,8 @@ -import { drizzle } from 'drizzle-orm/better-sqlite3'; -import Database from 'better-sqlite3'; -import * as schema from './schema'; +import { drizzle } from "drizzle-orm/better-sqlite3"; +import Database from "better-sqlite3"; +import * as schema from "./schema"; -const sqlite = new Database('data/db.sqlite'); +const sqlite = new Database("data/db.sqlite"); const db = drizzle(sqlite, { schema: schema, }); diff --git a/src/db/schema.ts b/src/db/schema.ts index 9eefa558..fae4f9e4 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,19 +1,19 @@ -import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core'; +import { text, integer, sqliteTable } from "drizzle-orm/sqlite-core"; -export const messages = sqliteTable('messages', { - id: integer('id').primaryKey(), - content: text('content').notNull(), - chatId: text('chatId').notNull(), - messageId: text('messageId').notNull(), - role: text('type', { enum: ['assistant', 'user'] }), - metadata: text('metadata', { - mode: 'json', +export const messages = sqliteTable("messages", { + id: integer("id").primaryKey(), + content: text("content").notNull(), + chatId: text("chatId").notNull(), + messageId: text("messageId").notNull(), + role: text("type", { enum: ["assistant", "user"] }), + metadata: text("metadata", { + mode: "json", }), }); -export const chats = sqliteTable('chats', { - id: text('id').primaryKey(), - title: text('title').notNull(), - createdAt: text('createdAt').notNull(), - focusMode: text('focusMode').notNull(), +export const chats = sqliteTable("chats", { + id: text("id").primaryKey(), + title: text("title").notNull(), + createdAt: text("createdAt").notNull(), + focusMode: text("focusMode").notNull(), }); diff --git a/src/lib/huggingfaceTransformer.ts b/src/lib/huggingfaceTransformer.ts index 02c085ac..ff77f22e 100644 --- a/src/lib/huggingfaceTransformer.ts +++ b/src/lib/huggingfaceTransformer.ts @@ -1,8 +1,7 @@ -import { Embeddings, type EmbeddingsParams } from '@langchain/core/embeddings'; -import { chunkArray } from '@langchain/core/utils/chunk_array'; +import { Embeddings, type EmbeddingsParams } from "@langchain/core/embeddings"; +import { chunkArray } from "@langchain/core/utils/chunk_array"; -export interface HuggingFaceTransformersEmbeddingsParams - extends EmbeddingsParams { +export interface HuggingFaceTransformersEmbeddingsParams extends EmbeddingsParams { modelName: string; model: string; @@ -14,13 +13,10 @@ export interface HuggingFaceTransformersEmbeddingsParams stripNewLines?: boolean; } -export class HuggingFaceTransformersEmbeddings - extends Embeddings - implements HuggingFaceTransformersEmbeddingsParams -{ - modelName = 'Xenova/all-MiniLM-L6-v2'; +export class HuggingFaceTransformersEmbeddings extends Embeddings implements HuggingFaceTransformersEmbeddingsParams { + modelName = "Xenova/all-MiniLM-L6-v2"; - model = 'Xenova/all-MiniLM-L6-v2'; + model = "Xenova/all-MiniLM-L6-v2"; batchSize = 512; @@ -41,12 +37,9 @@ export class HuggingFaceTransformersEmbeddings } async embedDocuments(texts: string[]): Promise { - const batches = chunkArray( - this.stripNewLines ? texts.map((t) => t.replace(/\n/g, ' ')) : texts, - this.batchSize, - ); + const batches = chunkArray(this.stripNewLines ? texts.map(t => t.replace(/\n/g, " ")) : texts, this.batchSize); - const batchRequests = batches.map((batch) => this.runEmbedding(batch)); + const batchRequests = batches.map(batch => this.runEmbedding(batch)); const batchResponses = await Promise.all(batchRequests); const embeddings: number[][] = []; @@ -61,22 +54,17 @@ export class HuggingFaceTransformersEmbeddings } async embedQuery(text: string): Promise { - const data = await this.runEmbedding([ - this.stripNewLines ? text.replace(/\n/g, ' ') : text, - ]); + const data = await this.runEmbedding([this.stripNewLines ? text.replace(/\n/g, " ") : text]); return data[0]; } private async runEmbedding(texts: string[]) { - const { pipeline } = await import('@xenova/transformers'); + const { pipeline } = await import("@xenova/transformers"); - const pipe = await (this.pipelinePromise ??= pipeline( - 'feature-extraction', - this.model, - )); + const pipe = await (this.pipelinePromise ??= pipeline("feature-extraction", this.model)); return this.caller.call(async () => { - const output = await pipe(texts, { pooling: 'mean', normalize: true }); + const output = await pipe(texts, { pooling: "mean", normalize: true }); return output.tolist(); }); } diff --git a/src/lib/outputParsers/listLineOutputParser.ts b/src/lib/outputParsers/listLineOutputParser.ts index 57a9bbc8..2cb5fd5c 100644 --- a/src/lib/outputParsers/listLineOutputParser.ts +++ b/src/lib/outputParsers/listLineOutputParser.ts @@ -1,11 +1,11 @@ -import { BaseOutputParser } from '@langchain/core/output_parsers'; +import { BaseOutputParser } from "@langchain/core/output_parsers"; interface LineListOutputParserArgs { key?: string; } class LineListOutputParser extends BaseOutputParser { - private key = 'questions'; + private key = "questions"; constructor(args?: LineListOutputParserArgs) { super(); @@ -13,30 +13,29 @@ class LineListOutputParser extends BaseOutputParser { } static lc_name() { - return 'LineListOutputParser'; + return "LineListOutputParser"; } - lc_namespace = ['langchain', 'output_parsers', 'line_list_output_parser']; + lc_namespace = ["langchain", "output_parsers", "line_list_output_parser"]; async parse(text: string): Promise { const regex = /^(\s*(-|\*|\d+\.\s|\d+\)\s|\u2022)\s*)+/; const startKeyIndex = text.indexOf(`<${this.key}>`); const endKeyIndex = text.indexOf(``); - const questionsStartIndex = - startKeyIndex === -1 ? 0 : startKeyIndex + `<${this.key}>`.length; + const questionsStartIndex = startKeyIndex === -1 ? 0 : startKeyIndex + `<${this.key}>`.length; const questionsEndIndex = endKeyIndex === -1 ? text.length : endKeyIndex; const lines = text .slice(questionsStartIndex, questionsEndIndex) .trim() - .split('\n') - .filter((line) => line.trim() !== '') - .map((line) => line.replace(regex, '')); + .split("\n") + .filter(line => line.trim() !== "") + .map(line => line.replace(regex, "")); return lines; } getFormatInstructions(): string { - throw new Error('Not implemented.'); + throw new Error("Not implemented."); } } diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 62cad14b..26c4b520 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -1,13 +1,9 @@ -import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai'; -import { ChatOllama } from '@langchain/community/chat_models/ollama'; -import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama'; -import { HuggingFaceTransformersEmbeddings } from './huggingfaceTransformer'; -import { - getGroqApiKey, - getOllamaApiEndpoint, - getOpenaiApiKey, -} from '../config'; -import logger from '../utils/logger'; +import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai"; +import { ChatOllama } from "@langchain/community/chat_models/ollama"; +import { OllamaEmbeddings } from "@langchain/community/embeddings/ollama"; +import { HuggingFaceTransformersEmbeddings } from "./huggingfaceTransformer"; +import { getGroqApiKey, getOllamaApiEndpoint, getOpenaiApiKey } from "../config"; +import logger from "../utils/logger"; export const getAvailableChatModelProviders = async () => { const openAIApiKey = getOpenaiApiKey(); @@ -18,25 +14,25 @@ export const getAvailableChatModelProviders = async () => { if (openAIApiKey) { try { - models['openai'] = { - 'GPT-3.5 turbo': new ChatOpenAI({ + models["openai"] = { + "GPT-3.5 turbo": new ChatOpenAI({ openAIApiKey, - modelName: 'gpt-3.5-turbo', + modelName: "gpt-3.5-turbo", temperature: 0.7, }), - 'GPT-4': new ChatOpenAI({ + "GPT-4": new ChatOpenAI({ openAIApiKey, - modelName: 'gpt-4', + modelName: "gpt-4", temperature: 0.7, }), - 'GPT-4 turbo': new ChatOpenAI({ + "GPT-4 turbo": new ChatOpenAI({ openAIApiKey, - modelName: 'gpt-4-turbo', + modelName: "gpt-4-turbo", temperature: 0.7, }), - 'GPT-4 omni': new ChatOpenAI({ + "GPT-4 omni": new ChatOpenAI({ openAIApiKey, - modelName: 'gpt-4o', + modelName: "gpt-4o", temperature: 0.7, }), }; @@ -47,45 +43,45 @@ export const getAvailableChatModelProviders = async () => { if (groqApiKey) { try { - models['groq'] = { - 'LLaMA3 8b': new ChatOpenAI( + models["groq"] = { + "LLaMA3 8b": new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'llama3-8b-8192', + modelName: "llama3-8b-8192", temperature: 0.7, }, { - baseURL: 'https://api.groq.com/openai/v1', + baseURL: "https://api.groq.com/openai/v1", }, ), - 'LLaMA3 70b': new ChatOpenAI( + "LLaMA3 70b": new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'llama3-70b-8192', + modelName: "llama3-70b-8192", temperature: 0.7, }, { - baseURL: 'https://api.groq.com/openai/v1', + baseURL: "https://api.groq.com/openai/v1", }, ), - 'Mixtral 8x7b': new ChatOpenAI( + "Mixtral 8x7b": new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'mixtral-8x7b-32768', + modelName: "mixtral-8x7b-32768", temperature: 0.7, }, { - baseURL: 'https://api.groq.com/openai/v1', + baseURL: "https://api.groq.com/openai/v1", }, ), - 'Gemma 7b': new ChatOpenAI( + "Gemma 7b": new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'gemma-7b-it', + modelName: "gemma-7b-it", temperature: 0.7, }, { - baseURL: 'https://api.groq.com/openai/v1', + baseURL: "https://api.groq.com/openai/v1", }, ), }; @@ -98,14 +94,14 @@ export const getAvailableChatModelProviders = async () => { try { const response = await fetch(`${ollamaEndpoint}/api/tags`, { headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const { models: ollamaModels } = (await response.json()) as any; - models['ollama'] = ollamaModels.reduce((acc, model) => { + models["ollama"] = ollamaModels.reduce((acc, model) => { acc[model.model] = new ChatOllama({ baseUrl: ollamaEndpoint, model: model.model, @@ -118,7 +114,7 @@ export const getAvailableChatModelProviders = async () => { } } - models['custom_openai'] = {}; + models["custom_openai"] = {}; return models; }; @@ -131,14 +127,14 @@ export const getAvailableEmbeddingModelProviders = async () => { if (openAIApiKey) { try { - models['openai'] = { - 'Text embedding 3 small': new OpenAIEmbeddings({ + models["openai"] = { + "Text embedding 3 small": new OpenAIEmbeddings({ openAIApiKey, - modelName: 'text-embedding-3-small', + modelName: "text-embedding-3-small", }), - 'Text embedding 3 large': new OpenAIEmbeddings({ + "Text embedding 3 large": new OpenAIEmbeddings({ openAIApiKey, - modelName: 'text-embedding-3-large', + modelName: "text-embedding-3-large", }), }; } catch (err) { @@ -150,14 +146,14 @@ export const getAvailableEmbeddingModelProviders = async () => { try { const response = await fetch(`${ollamaEndpoint}/api/tags`, { headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const { models: ollamaModels } = (await response.json()) as any; - models['ollama'] = ollamaModels.reduce((acc, model) => { + models["ollama"] = ollamaModels.reduce((acc, model) => { acc[model.model] = new OllamaEmbeddings({ baseUrl: ollamaEndpoint, model: model.model, @@ -170,15 +166,15 @@ export const getAvailableEmbeddingModelProviders = async () => { } try { - models['local'] = { - 'BGE Small': new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/bge-small-en-v1.5', + models["local"] = { + "BGE Small": new HuggingFaceTransformersEmbeddings({ + modelName: "Xenova/bge-small-en-v1.5", }), - 'GTE Small': new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/gte-small', + "GTE Small": new HuggingFaceTransformersEmbeddings({ + modelName: "Xenova/gte-small", }), - 'Bert Multilingual': new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/bert-base-multilingual-uncased', + "Bert Multilingual": new HuggingFaceTransformersEmbeddings({ + modelName: "Xenova/bert-base-multilingual-uncased", }), }; } catch (err) { diff --git a/src/lib/searxng.ts b/src/lib/searxng.ts index da62457b..d32c5904 100644 --- a/src/lib/searxng.ts +++ b/src/lib/searxng.ts @@ -1,5 +1,5 @@ -import axios from 'axios'; -import { getSearxngApiEndpoint } from '../config'; +import axios from "axios"; +import { getSearxngApiEndpoint } from "../config"; interface SearxngSearchOptions { categories?: string[]; @@ -19,19 +19,16 @@ interface SearxngSearchResult { iframe_src?: string; } -export const searchSearxng = async ( - query: string, - opts?: SearxngSearchOptions, -) => { +export const searchSearxng = async (query: string, opts?: SearxngSearchOptions) => { const searxngURL = getSearxngApiEndpoint(); const url = new URL(`${searxngURL}/search?format=json`); - url.searchParams.append('q', query); + url.searchParams.append("q", query); if (opts) { - Object.keys(opts).forEach((key) => { + Object.keys(opts).forEach(key => { if (Array.isArray(opts[key])) { - url.searchParams.append(key, opts[key].join(',')); + url.searchParams.append(key, opts[key].join(",")); return; } url.searchParams.append(key, opts[key]); diff --git a/src/routes/chats.ts b/src/routes/chats.ts index afa74f97..effe6156 100644 --- a/src/routes/chats.ts +++ b/src/routes/chats.ts @@ -1,12 +1,12 @@ -import express from 'express'; -import logger from '../utils/logger'; -import db from '../db/index'; -import { eq } from 'drizzle-orm'; -import { chats, messages } from '../db/schema'; +import express from "express"; +import logger from "../utils/logger"; +import db from "../db/index"; +import { eq } from "drizzle-orm"; +import { chats, messages } from "../db/schema"; const router = express.Router(); -router.get('/', async (_, res) => { +router.get("/", async (_, res) => { try { let chats = await db.query.chats.findMany(); @@ -14,19 +14,19 @@ router.get('/', async (_, res) => { return res.status(200).json({ chats: chats }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in getting chats: ${err.message}`); } }); -router.get('/:id', async (req, res) => { +router.get("/:id", async (req, res) => { try { const chatExists = await db.query.chats.findFirst({ where: eq(chats.id, req.params.id), }); if (!chatExists) { - return res.status(404).json({ message: 'Chat not found' }); + return res.status(404).json({ message: "Chat not found" }); } const chatMessages = await db.query.messages.findMany({ @@ -35,7 +35,7 @@ router.get('/:id', async (req, res) => { return res.status(200).json({ chat: chatExists, messages: chatMessages }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in getting chat: ${err.message}`); } }); @@ -47,18 +47,15 @@ router.delete(`/:id`, async (req, res) => { }); if (!chatExists) { - return res.status(404).json({ message: 'Chat not found' }); + return res.status(404).json({ message: "Chat not found" }); } await db.delete(chats).where(eq(chats.id, req.params.id)).execute(); - await db - .delete(messages) - .where(eq(messages.chatId, req.params.id)) - .execute(); + await db.delete(messages).where(eq(messages.chatId, req.params.id)).execute(); - return res.status(200).json({ message: 'Chat deleted successfully' }); + return res.status(200).json({ message: "Chat deleted successfully" }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in deleting chat: ${err.message}`); } }); diff --git a/src/routes/config.ts b/src/routes/config.ts index bf13b639..867ed02a 100644 --- a/src/routes/config.ts +++ b/src/routes/config.ts @@ -1,18 +1,10 @@ -import express from 'express'; -import { - getAvailableChatModelProviders, - getAvailableEmbeddingModelProviders, -} from '../lib/providers'; -import { - getGroqApiKey, - getOllamaApiEndpoint, - getOpenaiApiKey, - updateConfig, -} from '../config'; +import express from "express"; +import { getAvailableChatModelProviders, getAvailableEmbeddingModelProviders } from "../lib/providers"; +import { getGroqApiKey, getOllamaApiEndpoint, getOpenaiApiKey, updateConfig } from "../config"; const router = express.Router(); -router.get('/', async (_, res) => { +router.get("/", async (_, res) => { const config = {}; const [chatModelProviders, embeddingModelProviders] = await Promise.all([ @@ -20,29 +12,25 @@ router.get('/', async (_, res) => { getAvailableEmbeddingModelProviders(), ]); - config['chatModelProviders'] = {}; - config['embeddingModelProviders'] = {}; + config["chatModelProviders"] = {}; + config["embeddingModelProviders"] = {}; for (const provider in chatModelProviders) { - config['chatModelProviders'][provider] = Object.keys( - chatModelProviders[provider], - ); + config["chatModelProviders"][provider] = Object.keys(chatModelProviders[provider]); } for (const provider in embeddingModelProviders) { - config['embeddingModelProviders'][provider] = Object.keys( - embeddingModelProviders[provider], - ); + config["embeddingModelProviders"][provider] = Object.keys(embeddingModelProviders[provider]); } - config['openaiApiKey'] = getOpenaiApiKey(); - config['ollamaApiUrl'] = getOllamaApiEndpoint(); - config['groqApiKey'] = getGroqApiKey(); + config["openaiApiKey"] = getOpenaiApiKey(); + config["ollamaApiUrl"] = getOllamaApiEndpoint(); + config["groqApiKey"] = getGroqApiKey(); res.status(200).json(config); }); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { const config = req.body; const updatedConfig = { @@ -57,7 +45,7 @@ router.post('/', async (req, res) => { updateConfig(updatedConfig); - res.status(200).json({ message: 'Config updated' }); + res.status(200).json({ message: "Config updated" }); }); export default router; diff --git a/src/routes/images.ts b/src/routes/images.ts index 2650ab6f..17a1b5d6 100644 --- a/src/routes/images.ts +++ b/src/routes/images.ts @@ -1,21 +1,21 @@ -import express from 'express'; -import handleImageSearch from '../agents/imageSearchAgent'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { getAvailableChatModelProviders } from '../lib/providers'; -import { HumanMessage, AIMessage } from '@langchain/core/messages'; -import logger from '../utils/logger'; +import express from "express"; +import handleImageSearch from "../agents/imageSearchAgent"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import { getAvailableChatModelProviders } from "../lib/providers"; +import { HumanMessage, AIMessage } from "@langchain/core/messages"; +import logger from "../utils/logger"; const router = express.Router(); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { try { const { query, chat_history: raw_chat_history, chat_model_provider, chat_model } = req.body; // eslint-disable-next-line @typescript-eslint/no-explicit-any const chat_history = raw_chat_history.map((msg: any) => { - if (msg.role === 'user') { + if (msg.role === "user") { return new HumanMessage(msg.content); - } else if (msg.role === 'assistant') { + } else if (msg.role === "assistant") { return new AIMessage(msg.content); } }); @@ -31,7 +31,7 @@ router.post('/', async (req, res) => { } if (!llm) { - res.status(500).json({ message: 'Invalid LLM model selected' }); + res.status(500).json({ message: "Invalid LLM model selected" }); return; } @@ -39,7 +39,7 @@ router.post('/', async (req, res) => { res.status(200).json({ images }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in image search: ${err.message}`); } }); diff --git a/src/routes/index.ts b/src/routes/index.ts index af928abe..79283f64 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,18 +1,18 @@ -import express from 'express'; -import imagesRouter from './images'; -import videosRouter from './videos'; -import configRouter from './config'; -import modelsRouter from './models'; -import suggestionsRouter from './suggestions'; -import chatsRouter from './chats'; +import express from "express"; +import imagesRouter from "./images"; +import videosRouter from "./videos"; +import configRouter from "./config"; +import modelsRouter from "./models"; +import suggestionsRouter from "./suggestions"; +import chatsRouter from "./chats"; const router = express.Router(); -router.use('/images', imagesRouter); -router.use('/videos', videosRouter); -router.use('/config', configRouter); -router.use('/models', modelsRouter); -router.use('/suggestions', suggestionsRouter); -router.use('/chats', chatsRouter); +router.use("/images", imagesRouter); +router.use("/videos", videosRouter); +router.use("/config", configRouter); +router.use("/models", modelsRouter); +router.use("/suggestions", suggestionsRouter); +router.use("/chats", chatsRouter); export default router; diff --git a/src/routes/models.ts b/src/routes/models.ts index 36df25a5..43d48c8a 100644 --- a/src/routes/models.ts +++ b/src/routes/models.ts @@ -1,13 +1,10 @@ -import express from 'express'; -import logger from '../utils/logger'; -import { - getAvailableChatModelProviders, - getAvailableEmbeddingModelProviders, -} from '../lib/providers'; +import express from "express"; +import logger from "../utils/logger"; +import { getAvailableChatModelProviders, getAvailableEmbeddingModelProviders } from "../lib/providers"; const router = express.Router(); -router.get('/', async (req, res) => { +router.get("/", async (req, res) => { try { const [chatModelProviders, embeddingModelProviders] = await Promise.all([ getAvailableChatModelProviders(), @@ -16,7 +13,7 @@ router.get('/', async (req, res) => { res.status(200).json({ chatModelProviders, embeddingModelProviders }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(err.message); } }); diff --git a/src/routes/suggestions.ts b/src/routes/suggestions.ts index e8a7e1fa..34a4fb02 100644 --- a/src/routes/suggestions.ts +++ b/src/routes/suggestions.ts @@ -1,21 +1,21 @@ -import express from 'express'; -import generateSuggestions from '../agents/suggestionGeneratorAgent'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { getAvailableChatModelProviders } from '../lib/providers'; -import { HumanMessage, AIMessage } from '@langchain/core/messages'; -import logger from '../utils/logger'; +import express from "express"; +import generateSuggestions from "../agents/suggestionGeneratorAgent"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import { getAvailableChatModelProviders } from "../lib/providers"; +import { HumanMessage, AIMessage } from "@langchain/core/messages"; +import logger from "../utils/logger"; const router = express.Router(); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { try { const { chat_history: raw_chat_history, chat_model, chat_model_provider } = req.body; // eslint-disable-next-line @typescript-eslint/no-explicit-any const chat_history = raw_chat_history.map((msg: any) => { - if (msg.role === 'user') { + if (msg.role === "user") { return new HumanMessage(msg.content); - } else if (msg.role === 'assistant') { + } else if (msg.role === "assistant") { return new AIMessage(msg.content); } }); @@ -31,7 +31,7 @@ router.post('/', async (req, res) => { } if (!llm) { - res.status(500).json({ message: 'Invalid LLM model selected' }); + res.status(500).json({ message: "Invalid LLM model selected" }); return; } @@ -39,7 +39,7 @@ router.post('/', async (req, res) => { res.status(200).json({ suggestions: suggestions }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in generating suggestions: ${err.message}`); } }); diff --git a/src/routes/videos.ts b/src/routes/videos.ts index eef20337..e5ca4468 100644 --- a/src/routes/videos.ts +++ b/src/routes/videos.ts @@ -1,21 +1,21 @@ -import express from 'express'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { getAvailableChatModelProviders } from '../lib/providers'; -import { HumanMessage, AIMessage } from '@langchain/core/messages'; -import logger from '../utils/logger'; -import handleVideoSearch from '../agents/videoSearchAgent'; +import express from "express"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import { getAvailableChatModelProviders } from "../lib/providers"; +import { HumanMessage, AIMessage } from "@langchain/core/messages"; +import logger from "../utils/logger"; +import handleVideoSearch from "../agents/videoSearchAgent"; const router = express.Router(); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { try { const { query, chat_history: raw_chat_history, chat_model_provider, chat_model } = req.body; // eslint-disable-next-line @typescript-eslint/no-explicit-any const chat_history = raw_chat_history.map((msg: any) => { - if (msg.role === 'user') { + if (msg.role === "user") { return new HumanMessage(msg.content); - } else if (msg.role === 'assistant') { + } else if (msg.role === "assistant") { return new AIMessage(msg.content); } }); @@ -31,7 +31,7 @@ router.post('/', async (req, res) => { } if (!llm) { - res.status(500).json({ message: 'Invalid LLM model selected' }); + res.status(500).json({ message: "Invalid LLM model selected" }); return; } @@ -39,7 +39,7 @@ router.post('/', async (req, res) => { res.status(200).json({ videos }); } catch (err) { - res.status(500).json({ message: 'An error has occurred.' }); + res.status(500).json({ message: "An error has occurred." }); logger.error(`Error in video search: ${err.message}`); } }); diff --git a/src/utils/computeSimilarity.ts b/src/utils/computeSimilarity.ts index 6e36b759..ac088374 100644 --- a/src/utils/computeSimilarity.ts +++ b/src/utils/computeSimilarity.ts @@ -1,17 +1,17 @@ -import dot from 'compute-dot'; -import cosineSimilarity from 'compute-cosine-similarity'; -import { getSimilarityMeasure } from '../config'; +import dot from "compute-dot"; +import cosineSimilarity from "compute-cosine-similarity"; +import { getSimilarityMeasure } from "../config"; const computeSimilarity = (x: number[], y: number[]): number => { const similarityMeasure = getSimilarityMeasure(); - if (similarityMeasure === 'cosine') { + if (similarityMeasure === "cosine") { return cosineSimilarity(x, y); - } else if (similarityMeasure === 'dot') { + } else if (similarityMeasure === "dot") { return dot(x, y); } - throw new Error('Invalid similarity measure'); + throw new Error("Invalid similarity measure"); }; export default computeSimilarity; diff --git a/src/utils/formatHistory.ts b/src/utils/formatHistory.ts index 6d0d309c..ab783cbe 100644 --- a/src/utils/formatHistory.ts +++ b/src/utils/formatHistory.ts @@ -1,9 +1,7 @@ -import { BaseMessage } from '@langchain/core/messages'; +import { BaseMessage } from "@langchain/core/messages"; const formatChatHistoryAsString = (history: BaseMessage[]) => { - return history - .map((message) => `${message._getType()}: ${message.content}`) - .join('\n'); + return history.map(message => `${message._getType()}: ${message.content}`).join("\n"); }; export default formatChatHistoryAsString; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 1c81eb9d..8cbf0d78 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,20 +1,14 @@ -import winston from 'winston'; +import winston from "winston"; const logger = winston.createLogger({ - level: 'info', + level: "info", transports: [ new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.simple(), - ), + format: winston.format.combine(winston.format.colorize(), winston.format.simple()), }), new winston.transports.File({ - filename: 'app.log', - format: winston.format.combine( - winston.format.timestamp(), - winston.format.json(), - ), + filename: "app.log", + format: winston.format.combine(winston.format.timestamp(), winston.format.json()), }), ], }); diff --git a/src/websocket/connectionManager.ts b/src/websocket/connectionManager.ts index 5cb075b6..3dac8ca1 100644 --- a/src/websocket/connectionManager.ts +++ b/src/websocket/connectionManager.ts @@ -1,41 +1,28 @@ -import { WebSocket } from 'ws'; -import { handleMessage } from './messageHandler'; -import { - getAvailableEmbeddingModelProviders, - getAvailableChatModelProviders, -} from '../lib/providers'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import type { IncomingMessage } from 'http'; -import logger from '../utils/logger'; -import { ChatOpenAI } from '@langchain/openai'; +import { WebSocket } from "ws"; +import { handleMessage } from "./messageHandler"; +import { getAvailableEmbeddingModelProviders, getAvailableChatModelProviders } from "../lib/providers"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import type { IncomingMessage } from "http"; +import logger from "../utils/logger"; +import { ChatOpenAI } from "@langchain/openai"; -export const handleConnection = async ( - ws: WebSocket, - request: IncomingMessage, -) => { +export const handleConnection = async (ws: WebSocket, request: IncomingMessage) => { try { - const searchParams = new URL(request.url, `http://${request.headers.host}`) - .searchParams; + const searchParams = new URL(request.url, `http://${request.headers.host}`).searchParams; const [chatModelProviders, embeddingModelProviders] = await Promise.all([ getAvailableChatModelProviders(), getAvailableEmbeddingModelProviders(), ]); - const chatModelProvider = - searchParams.get('chatModelProvider') || - Object.keys(chatModelProviders)[0]; - const chatModel = - searchParams.get('chatModel') || - Object.keys(chatModelProviders[chatModelProvider])[0]; + const chatModelProvider = searchParams.get("chatModelProvider") || Object.keys(chatModelProviders)[0]; + const chatModel = searchParams.get("chatModel") || Object.keys(chatModelProviders[chatModelProvider])[0]; const embeddingModelProvider = - searchParams.get('embeddingModelProvider') || - Object.keys(embeddingModelProviders)[0]; + searchParams.get("embeddingModelProvider") || Object.keys(embeddingModelProviders)[0]; const embeddingModel = - searchParams.get('embeddingModel') || - Object.keys(embeddingModelProviders[embeddingModelProvider])[0]; + searchParams.get("embeddingModel") || Object.keys(embeddingModelProviders[embeddingModelProvider])[0]; let llm: BaseChatModel | undefined; let embeddings: Embeddings | undefined; @@ -43,18 +30,16 @@ export const handleConnection = async ( if ( chatModelProviders[chatModelProvider] && chatModelProviders[chatModelProvider][chatModel] && - chatModelProvider != 'custom_openai' + chatModelProvider != "custom_openai" ) { - llm = chatModelProviders[chatModelProvider][chatModel] as - | BaseChatModel - | undefined; - } else if (chatModelProvider == 'custom_openai') { + llm = chatModelProviders[chatModelProvider][chatModel] as BaseChatModel | undefined; + } else if (chatModelProvider == "custom_openai") { llm = new ChatOpenAI({ modelName: chatModel, - openAIApiKey: searchParams.get('openAIApiKey'), + openAIApiKey: searchParams.get("openAIApiKey"), temperature: 0.7, configuration: { - baseURL: searchParams.get('openAIBaseURL'), + baseURL: searchParams.get("openAIBaseURL"), }, }); } @@ -63,35 +48,29 @@ export const handleConnection = async ( embeddingModelProviders[embeddingModelProvider] && embeddingModelProviders[embeddingModelProvider][embeddingModel] ) { - embeddings = embeddingModelProviders[embeddingModelProvider][ - embeddingModel - ] as Embeddings | undefined; + embeddings = embeddingModelProviders[embeddingModelProvider][embeddingModel] as Embeddings | undefined; } if (!llm || !embeddings) { ws.send( JSON.stringify({ - type: 'error', - data: 'Invalid LLM or embeddings model selected, please refresh the page and try again.', - key: 'INVALID_MODEL_SELECTED', + type: "error", + data: "Invalid LLM or embeddings model selected, please refresh the page and try again.", + key: "INVALID_MODEL_SELECTED", }), ); ws.close(); } - ws.on( - 'message', - async (message) => - await handleMessage(message.toString(), ws, llm, embeddings), - ); + ws.on("message", async message => await handleMessage(message.toString(), ws, llm, embeddings)); - ws.on('close', () => logger.debug('Connection closed')); + ws.on("close", () => logger.debug("Connection closed")); } catch (err) { ws.send( JSON.stringify({ - type: 'error', - data: 'Internal server error.', - key: 'INTERNAL_SERVER_ERROR', + type: "error", + data: "Internal server error.", + key: "INTERNAL_SERVER_ERROR", }), ); ws.close(); diff --git a/src/websocket/index.ts b/src/websocket/index.ts index 1b9ae770..53f2dc48 100644 --- a/src/websocket/index.ts +++ b/src/websocket/index.ts @@ -1,8 +1,6 @@ -import { initServer } from './websocketServer'; -import http from 'http'; +import { initServer } from "./websocketServer"; +import http from "http"; -export const startWebSocketServer = ( - server: http.Server, -) => { +export const startWebSocketServer = (server: http.Server) => { initServer(server); }; diff --git a/src/websocket/messageHandler.ts b/src/websocket/messageHandler.ts index 0afda9f4..2426bec5 100644 --- a/src/websocket/messageHandler.ts +++ b/src/websocket/messageHandler.ts @@ -1,18 +1,18 @@ -import { EventEmitter, WebSocket } from 'ws'; -import { BaseMessage, AIMessage, HumanMessage } from '@langchain/core/messages'; -import handleWebSearch from '../agents/webSearchAgent'; -import handleAcademicSearch from '../agents/academicSearchAgent'; -import handleWritingAssistant from '../agents/writingAssistant'; -import handleWolframAlphaSearch from '../agents/wolframAlphaSearchAgent'; -import handleYoutubeSearch from '../agents/youtubeSearchAgent'; -import handleRedditSearch from '../agents/redditSearchAgent'; -import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { Embeddings } from '@langchain/core/embeddings'; -import logger from '../utils/logger'; -import db from '../db'; -import { chats, messages } from '../db/schema'; -import { eq } from 'drizzle-orm'; -import crypto from 'crypto'; +import { EventEmitter, WebSocket } from "ws"; +import { BaseMessage, AIMessage, HumanMessage } from "@langchain/core/messages"; +import handleWebSearch from "../agents/webSearchAgent"; +import handleAcademicSearch from "../agents/academicSearchAgent"; +import handleWritingAssistant from "../agents/writingAssistant"; +import handleWolframAlphaSearch from "../agents/wolframAlphaSearchAgent"; +import handleYoutubeSearch from "../agents/youtubeSearchAgent"; +import handleRedditSearch from "../agents/redditSearchAgent"; +import type { BaseChatModel } from "@langchain/core/language_models/chat_models"; +import type { Embeddings } from "@langchain/core/embeddings"; +import logger from "../utils/logger"; +import db from "../db"; +import { chats, messages } from "../db/schema"; +import { eq } from "drizzle-orm"; +import crypto from "crypto"; type Message = { messageId: string; @@ -37,30 +37,25 @@ const searchHandlers = { redditSearch: handleRedditSearch, }; -const handleEmitterEvents = ( - emitter: EventEmitter, - ws: WebSocket, - messageId: string, - chatId: string, -) => { - let recievedMessage = ''; +const handleEmitterEvents = (emitter: EventEmitter, ws: WebSocket, messageId: string, chatId: string) => { + let recievedMessage = ""; let sources = []; - emitter.on('data', (data) => { + emitter.on("data", data => { const parsedData = JSON.parse(data); - if (parsedData.type === 'response') { + if (parsedData.type === "response") { ws.send( JSON.stringify({ - type: 'message', + type: "message", data: parsedData.data, messageId: messageId, }), ); recievedMessage += parsedData.data; - } else if (parsedData.type === 'sources') { + } else if (parsedData.type === "sources") { ws.send( JSON.stringify({ - type: 'sources', + type: "sources", data: parsedData.data, messageId: messageId, }), @@ -68,15 +63,15 @@ const handleEmitterEvents = ( sources = parsedData.data; } }); - emitter.on('end', () => { - ws.send(JSON.stringify({ type: 'messageEnd', messageId: messageId })); + emitter.on("end", () => { + ws.send(JSON.stringify({ type: "messageEnd", messageId: messageId })); db.insert(messages) .values({ content: recievedMessage, chatId: chatId, messageId: messageId, - role: 'assistant', + role: "assistant", metadata: JSON.stringify({ createdAt: new Date(), ...(sources && sources.length > 0 && { sources }), @@ -84,41 +79,36 @@ const handleEmitterEvents = ( }) .execute(); }); - emitter.on('error', (data) => { + emitter.on("error", data => { const parsedData = JSON.parse(data); ws.send( JSON.stringify({ - type: 'error', + type: "error", data: parsedData.data, - key: 'CHAIN_ERROR', + key: "CHAIN_ERROR", }), ); }); }; -export const handleMessage = async ( - message: string, - ws: WebSocket, - llm: BaseChatModel, - embeddings: Embeddings, -) => { +export const handleMessage = async (message: string, ws: WebSocket, llm: BaseChatModel, embeddings: Embeddings) => { try { const parsedWSMessage = JSON.parse(message) as WSMessage; const parsedMessage = parsedWSMessage.message; - const id = crypto.randomBytes(7).toString('hex'); + const id = crypto.randomBytes(7).toString("hex"); if (!parsedMessage.content) return ws.send( JSON.stringify({ - type: 'error', - data: 'Invalid message format', - key: 'INVALID_FORMAT', + type: "error", + data: "Invalid message format", + key: "INVALID_FORMAT", }), ); - const history: BaseMessage[] = parsedWSMessage.history.map((msg) => { - if (msg[0] === 'human') { + const history: BaseMessage[] = parsedWSMessage.history.map(msg => { + if (msg[0] === "human") { return new HumanMessage({ content: msg[1], }); @@ -129,16 +119,11 @@ export const handleMessage = async ( } }); - if (parsedWSMessage.type === 'message') { + if (parsedWSMessage.type === "message") { const handler = searchHandlers[parsedWSMessage.focusMode]; if (handler) { - const emitter = handler( - parsedMessage.content, - history, - llm, - embeddings, - ); + const emitter = handler(parsedMessage.content, history, llm, embeddings); handleEmitterEvents(emitter, ws, id, parsedMessage.chatId); @@ -164,7 +149,7 @@ export const handleMessage = async ( content: parsedMessage.content, chatId: parsedMessage.chatId, messageId: id, - role: 'user', + role: "user", metadata: JSON.stringify({ createdAt: new Date(), }), @@ -173,9 +158,9 @@ export const handleMessage = async ( } else { ws.send( JSON.stringify({ - type: 'error', - data: 'Invalid focus mode', - key: 'INVALID_FOCUS_MODE', + type: "error", + data: "Invalid focus mode", + key: "INVALID_FOCUS_MODE", }), ); } @@ -183,9 +168,9 @@ export const handleMessage = async ( } catch (err) { ws.send( JSON.stringify({ - type: 'error', - data: 'Invalid message format', - key: 'INVALID_FORMAT', + type: "error", + data: "Invalid message format", + key: "INVALID_FORMAT", }), ); logger.error(`Failed to handle message: ${err}`); diff --git a/src/websocket/websocketServer.ts b/src/websocket/websocketServer.ts index 3ab0b519..b3357af3 100644 --- a/src/websocket/websocketServer.ts +++ b/src/websocket/websocketServer.ts @@ -1,16 +1,14 @@ -import { WebSocketServer } from 'ws'; -import { handleConnection } from './connectionManager'; -import http from 'http'; -import { getPort } from '../config'; -import logger from '../utils/logger'; +import { WebSocketServer } from "ws"; +import { handleConnection } from "./connectionManager"; +import http from "http"; +import { getPort } from "../config"; +import logger from "../utils/logger"; -export const initServer = ( - server: http.Server, -) => { +export const initServer = (server: http.Server) => { const port = getPort(); const wss = new WebSocketServer({ server }); - wss.on('connection', handleConnection); + wss.on("connection", handleConnection); logger.info(`WebSocket server started on port ${port}`); }; diff --git a/ui/app/c/[chatId]/page.tsx b/ui/app/c/[chatId]/page.tsx index dc3c92a0..4c8ab95f 100644 --- a/ui/app/c/[chatId]/page.tsx +++ b/ui/app/c/[chatId]/page.tsx @@ -1,4 +1,4 @@ -import ChatWindow from '@/components/ChatWindow'; +import ChatWindow from "@/components/ChatWindow"; const Page = ({ params }: { params: { chatId: string } }) => { return ; diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index 2edbf942..27c27c47 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -1,22 +1,21 @@ -import type { Metadata } from 'next'; -import { Montserrat } from 'next/font/google'; -import './globals.css'; -import { cn } from '@/lib/utils'; -import Sidebar from '@/components/Sidebar'; -import { Toaster } from 'sonner'; -import ThemeProvider from '@/components/theme/Provider'; +import type { Metadata } from "next"; +import { Montserrat } from "next/font/google"; +import "./globals.css"; +import { cn } from "@/lib/utils"; +import Sidebar from "@/components/Sidebar"; +import { Toaster } from "sonner"; +import ThemeProvider from "@/components/theme/Provider"; const montserrat = Montserrat({ - weight: ['300', '400', '500', '700'], - subsets: ['latin'], - display: 'swap', - fallback: ['Arial', 'sans-serif'], + weight: ["300", "400", "500", "700"], + subsets: ["latin"], + display: "swap", + fallback: ["Arial", "sans-serif"], }); export const metadata: Metadata = { - title: 'Perplexica - Chat with the internet', - description: - 'Perplexica is an AI powered chatbot that is connected to the internet.', + title: "Perplexica - Chat with the internet", + description: "Perplexica is an AI powered chatbot that is connected to the internet.", }; export default function RootLayout({ @@ -26,7 +25,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/ui/app/library/layout.tsx b/ui/app/library/layout.tsx index 00d4a3bc..f7db5db8 100644 --- a/ui/app/library/layout.tsx +++ b/ui/app/library/layout.tsx @@ -1,8 +1,8 @@ -import { Metadata } from 'next'; -import React from 'react'; +import { Metadata } from "next"; +import React from "react"; export const metadata: Metadata = { - title: 'Library - Perplexica', + title: "Library - Perplexica", }; const Layout = ({ children }: { children: React.ReactNode }) => { diff --git a/ui/app/library/page.tsx b/ui/app/library/page.tsx index 586765d0..ac112012 100644 --- a/ui/app/library/page.tsx +++ b/ui/app/library/page.tsx @@ -1,10 +1,10 @@ -'use client'; +"use client"; -import DeleteChat from '@/components/DeleteChat'; -import { formatTimeDifference } from '@/lib/utils'; -import { BookOpenText, ClockIcon } from 'lucide-react'; -import Link from 'next/link'; -import { useEffect, useState } from 'react'; +import DeleteChat from "@/components/DeleteChat"; +import { formatTimeDifference } from "@/lib/utils"; +import { BookOpenText, ClockIcon } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useState } from "react"; export interface Chat { id: string; @@ -22,9 +22,9 @@ const Page = () => { setLoading(true); const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chats`, { - method: 'GET', + method: "GET", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }); @@ -61,16 +61,12 @@ const Page = () => {
-

- Library -

+

Library

{chats.length === 0 && (
-

- No chats found. -

+

No chats found.

)} {chats.length > 0 && ( @@ -89,15 +85,9 @@ const Page = () => {
-

- {formatTimeDifference(new Date(), chat.createdAt)} Ago -

+

{formatTimeDifference(new Date(), chat.createdAt)} Ago

- +
))} diff --git a/ui/app/page.tsx b/ui/app/page.tsx index e18aca9d..b1397748 100644 --- a/ui/app/page.tsx +++ b/ui/app/page.tsx @@ -1,10 +1,10 @@ -import ChatWindow from '@/components/ChatWindow'; -import { Metadata } from 'next'; -import { Suspense } from 'react'; +import ChatWindow from "@/components/ChatWindow"; +import { Metadata } from "next"; +import { Suspense } from "react"; export const metadata: Metadata = { - title: 'Chat - Perplexica', - description: 'Chat with the internet, chat with Perplexica.', + title: "Chat - Perplexica", + description: "Chat with the internet, chat with Perplexica.", }; const Home = () => { diff --git a/ui/components/Chat.tsx b/ui/components/Chat.tsx index 8c0fb804..3eed12d1 100644 --- a/ui/components/Chat.tsx +++ b/ui/components/Chat.tsx @@ -1,10 +1,10 @@ -'use client'; +"use client"; -import { Fragment, useEffect, useRef, useState } from 'react'; -import MessageInput from './MessageInput'; -import { Message } from './ChatWindow'; -import MessageBox from './MessageBox'; -import MessageBoxLoading from './MessageBoxLoading'; +import { Fragment, useEffect, useRef, useState } from "react"; +import MessageInput from "./MessageInput"; +import { Message } from "./ChatWindow"; +import MessageBox from "./MessageBox"; +import MessageBoxLoading from "./MessageBoxLoading"; const Chat = ({ loading, @@ -32,15 +32,15 @@ const Chat = ({ updateDividerWidth(); - window.addEventListener('resize', updateDividerWidth); + window.addEventListener("resize", updateDividerWidth); return () => { - window.removeEventListener('resize', updateDividerWidth); + window.removeEventListener("resize", updateDividerWidth); }; }); useEffect(() => { - messageEnd.current?.scrollIntoView({ behavior: 'smooth' }); + messageEnd.current?.scrollIntoView({ behavior: "smooth" }); if (messages.length === 1) { document.title = `${messages[0].content.substring(0, 30)} - Perplexica`; @@ -65,7 +65,7 @@ const Chat = ({ rewrite={rewrite} sendMessage={sendMessage} /> - {!isLast && msg.role === 'assistant' && ( + {!isLast && msg.role === "assistant" && (
)} @@ -74,10 +74,7 @@ const Chat = ({ {loading && !messageAppeared && }
{dividerWidth > 0 && ( -
+
)} diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index d66c999a..5ba68824 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -1,110 +1,79 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -'use client'; - -import { useEffect, useRef, useState } from 'react'; -import { Document } from '@langchain/core/documents'; -import Navbar from './Navbar'; -import Chat from './Chat'; -import EmptyChat from './EmptyChat'; -import crypto from 'crypto'; -import { toast } from 'sonner'; -import { useSearchParams } from 'next/navigation'; -import { getSuggestions } from '@/lib/actions'; -import Error from 'next/error'; +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { Document } from "@langchain/core/documents"; +import Navbar from "./Navbar"; +import Chat from "./Chat"; +import EmptyChat from "./EmptyChat"; +import crypto from "crypto"; +import { toast } from "sonner"; +import { useSearchParams } from "next/navigation"; +import { getSuggestions } from "@/lib/actions"; +import Error from "next/error"; export type Message = { messageId: string; chatId: string; createdAt: Date; content: string; - role: 'user' | 'assistant'; + role: "user" | "assistant"; suggestions?: string[]; sources?: Document[]; }; -const useSocket = ( - url: string, - setIsWSReady: (ready: boolean) => void, - setError: (error: boolean) => void, -) => { +const useSocket = (url: string, setIsWSReady: (ready: boolean) => void, setError: (error: boolean) => void) => { const [ws, setWs] = useState(null); useEffect(() => { if (!ws) { const connectWs = async () => { - let chatModel = localStorage.getItem('chatModel'); - let chatModelProvider = localStorage.getItem('chatModelProvider'); - let embeddingModel = localStorage.getItem('embeddingModel'); - let embeddingModelProvider = localStorage.getItem( - 'embeddingModelProvider', - ); - - if ( - !chatModel || - !chatModelProvider || - !embeddingModel || - !embeddingModelProvider - ) { - const providers = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/models`, - { - headers: { - 'Content-Type': 'application/json', - }, + let chatModel = localStorage.getItem("chatModel"); + let chatModelProvider = localStorage.getItem("chatModelProvider"); + let embeddingModel = localStorage.getItem("embeddingModel"); + let embeddingModelProvider = localStorage.getItem("embeddingModelProvider"); + + if (!chatModel || !chatModelProvider || !embeddingModel || !embeddingModelProvider) { + const providers = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/models`, { + headers: { + "Content-Type": "application/json", }, - ).then(async (res) => await res.json()); + }).then(async res => await res.json()); const chatModelProviders = providers.chatModelProviders; const embeddingModelProviders = providers.embeddingModelProviders; - if ( - !chatModelProviders || - Object.keys(chatModelProviders).length === 0 - ) - return toast.error('No chat models available'); + if (!chatModelProviders || Object.keys(chatModelProviders).length === 0) + return toast.error("No chat models available"); - if ( - !embeddingModelProviders || - Object.keys(embeddingModelProviders).length === 0 - ) - return toast.error('No embedding models available'); + if (!embeddingModelProviders || Object.keys(embeddingModelProviders).length === 0) + return toast.error("No embedding models available"); chatModelProvider = Object.keys(chatModelProviders)[0]; chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]; embeddingModelProvider = Object.keys(embeddingModelProviders)[0]; - embeddingModel = Object.keys( - embeddingModelProviders[embeddingModelProvider], - )[0]; - - localStorage.setItem('chatModel', chatModel!); - localStorage.setItem('chatModelProvider', chatModelProvider); - localStorage.setItem('embeddingModel', embeddingModel!); - localStorage.setItem( - 'embeddingModelProvider', - embeddingModelProvider, - ); + embeddingModel = Object.keys(embeddingModelProviders[embeddingModelProvider])[0]; + + localStorage.setItem("chatModel", chatModel!); + localStorage.setItem("chatModelProvider", chatModelProvider); + localStorage.setItem("embeddingModel", embeddingModel!); + localStorage.setItem("embeddingModelProvider", embeddingModelProvider); } const wsURL = new URL(url); const searchParams = new URLSearchParams({}); - searchParams.append('chatModel', chatModel!); - searchParams.append('chatModelProvider', chatModelProvider); + searchParams.append("chatModel", chatModel!); + searchParams.append("chatModelProvider", chatModelProvider); - if (chatModelProvider === 'custom_openai') { - searchParams.append( - 'openAIApiKey', - localStorage.getItem('openAIApiKey')!, - ); - searchParams.append( - 'openAIBaseURL', - localStorage.getItem('openAIBaseURL')!, - ); + if (chatModelProvider === "custom_openai") { + searchParams.append("openAIApiKey", localStorage.getItem("openAIApiKey")!); + searchParams.append("openAIBaseURL", localStorage.getItem("openAIBaseURL")!); } - searchParams.append('embeddingModel', embeddingModel!); - searchParams.append('embeddingModelProvider', embeddingModelProvider); + searchParams.append("embeddingModel", embeddingModel!); + searchParams.append("embeddingModelProvider", embeddingModelProvider); wsURL.search = searchParams.toString(); @@ -114,14 +83,12 @@ const useSocket = ( if (ws.readyState !== 1) { ws.close(); setError(true); - toast.error( - 'Failed to connect to the server. Please try again later.', - ); + toast.error("Failed to connect to the server. Please try again later."); } }, 10000); ws.onopen = () => { - console.log('[DEBUG] open'); + console.log("[DEBUG] open"); clearTimeout(timeoutId); setError(false); setIsWSReady(true); @@ -130,13 +97,13 @@ const useSocket = ( ws.onerror = () => { clearTimeout(timeoutId); setError(true); - toast.error('WebSocket connection error.'); + toast.error("WebSocket connection error."); }; ws.onclose = () => { clearTimeout(timeoutId); setError(true); - console.log('[DEBUG] closed'); + console.log("[DEBUG] closed"); }; setWs(ws); @@ -147,7 +114,7 @@ const useSocket = ( return () => { ws?.close(); - console.log('[DEBUG] closed'); + console.log("[DEBUG] closed"); }; }, [ws, url, setIsWSReady, setError]); @@ -162,15 +129,12 @@ const loadMessages = async ( setFocusMode: (mode: string) => void, setNotFound: (notFound: boolean) => void, ) => { - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", }, - ); + }); if (res.status === 404) { setNotFound(true); @@ -189,11 +153,11 @@ const loadMessages = async ( setMessages(messages); - const history = messages.map((msg) => { + const history = messages.map(msg => { return [msg.role, msg.content]; }) as [string, string][]; - console.log('[DEBUG] messages loaded'); + console.log("[DEBUG] messages loaded"); document.title = messages[0].content; @@ -204,7 +168,7 @@ const loadMessages = async ( const ChatWindow = ({ id }: { id?: string }) => { const searchParams = useSearchParams(); - const initialMessage = searchParams.get('q'); + const initialMessage = searchParams.get("q"); const [chatId, setChatId] = useState(id); const [newChatCreated, setNewChatCreated] = useState(false); @@ -213,11 +177,7 @@ const ChatWindow = ({ id }: { id?: string }) => { const [isReady, setIsReady] = useState(false); const [isWSReady, setIsWSReady] = useState(false); - const ws = useSocket( - process.env.NEXT_PUBLIC_WS_URL!, - setIsWSReady, - setHasError, - ); + const ws = useSocket(process.env.NEXT_PUBLIC_WS_URL!, setIsWSReady, setHasError); const [loading, setLoading] = useState(false); const [messageAppeared, setMessageAppeared] = useState(false); @@ -225,31 +185,19 @@ const ChatWindow = ({ id }: { id?: string }) => { const [chatHistory, setChatHistory] = useState<[string, string][]>([]); const [messages, setMessages] = useState([]); - const [focusMode, setFocusMode] = useState('webSearch'); + const [focusMode, setFocusMode] = useState("webSearch"); const [isMessagesLoaded, setIsMessagesLoaded] = useState(false); const [notFound, setNotFound] = useState(false); useEffect(() => { - if ( - chatId && - !newChatCreated && - !isMessagesLoaded && - messages.length === 0 - ) { - loadMessages( - chatId, - setMessages, - setIsMessagesLoaded, - setChatHistory, - setFocusMode, - setNotFound, - ); + if (chatId && !newChatCreated && !isMessagesLoaded && messages.length === 0) { + loadMessages(chatId, setMessages, setIsMessagesLoaded, setChatHistory, setFocusMode, setNotFound); } else if (!chatId) { setNewChatCreated(true); setIsMessagesLoaded(true); - setChatId(crypto.randomBytes(20).toString('hex')); + setChatId(crypto.randomBytes(20).toString("hex")); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -272,30 +220,30 @@ const ChatWindow = ({ id }: { id?: string }) => { setMessageAppeared(false); let sources: Document[] | undefined = undefined; - let recievedMessage = ''; + let recievedMessage = ""; let added = false; - const messageId = crypto.randomBytes(7).toString('hex'); + const messageId = crypto.randomBytes(7).toString("hex"); ws?.send( JSON.stringify({ - type: 'message', + type: "message", message: { chatId: chatId!, content: message, }, focusMode: focusMode, - history: [...chatHistory, ['human', message]], + history: [...chatHistory, ["human", message]], }), ); - setMessages((prevMessages) => [ + setMessages(prevMessages => [ ...prevMessages, { content: message, messageId: messageId, chatId: chatId!, - role: 'user', + role: "user", createdAt: new Date(), }, ]); @@ -303,22 +251,22 @@ const ChatWindow = ({ id }: { id?: string }) => { const messageHandler = async (e: MessageEvent) => { const data = JSON.parse(e.data); - if (data.type === 'error') { + if (data.type === "error") { toast.error(data.data); setLoading(false); return; } - if (data.type === 'sources') { + if (data.type === "sources") { sources = data.data; if (!added) { - setMessages((prevMessages) => [ + setMessages(prevMessages => [ ...prevMessages, { - content: '', + content: "", messageId: data.messageId, chatId: chatId!, - role: 'assistant', + role: "assistant", sources: sources, createdAt: new Date(), }, @@ -328,15 +276,15 @@ const ChatWindow = ({ id }: { id?: string }) => { setMessageAppeared(true); } - if (data.type === 'message') { + if (data.type === "message") { if (!added) { - setMessages((prevMessages) => [ + setMessages(prevMessages => [ ...prevMessages, { content: data.data, messageId: data.messageId, chatId: chatId!, - role: 'assistant', + role: "assistant", sources: sources, createdAt: new Date(), }, @@ -344,8 +292,8 @@ const ChatWindow = ({ id }: { id?: string }) => { added = true; } - setMessages((prev) => - prev.map((message) => { + setMessages(prev => + prev.map(message => { if (message.messageId === data.messageId) { return { ...message, content: message.content + data.data }; } @@ -358,27 +306,18 @@ const ChatWindow = ({ id }: { id?: string }) => { setMessageAppeared(true); } - if (data.type === 'messageEnd') { - setChatHistory((prevHistory) => [ - ...prevHistory, - ['human', message], - ['assistant', recievedMessage], - ]); + if (data.type === "messageEnd") { + setChatHistory(prevHistory => [...prevHistory, ["human", message], ["assistant", recievedMessage]]); - ws?.removeEventListener('message', messageHandler); + ws?.removeEventListener("message", messageHandler); setLoading(false); const lastMsg = messagesRef.current[messagesRef.current.length - 1]; - if ( - lastMsg.role === 'assistant' && - lastMsg.sources && - lastMsg.sources.length > 0 && - !lastMsg.suggestions - ) { + if (lastMsg.role === "assistant" && lastMsg.sources && lastMsg.sources.length > 0 && !lastMsg.suggestions) { const suggestions = await getSuggestions(messagesRef.current); - setMessages((prev) => - prev.map((msg) => { + setMessages(prev => + prev.map(msg => { if (msg.messageId === lastMsg.messageId) { return { ...msg, suggestions: suggestions }; } @@ -389,20 +328,20 @@ const ChatWindow = ({ id }: { id?: string }) => { } }; - ws?.addEventListener('message', messageHandler); + ws?.addEventListener("message", messageHandler); }; const rewrite = (messageId: string) => { - const index = messages.findIndex((msg) => msg.messageId === messageId); + const index = messages.findIndex(msg => msg.messageId === messageId); if (index === -1) return; const message = messages[index - 1]; - setMessages((prev) => { + setMessages(prev => { return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)]; }); - setChatHistory((prev) => { + setChatHistory(prev => { return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)]; }); @@ -443,11 +382,7 @@ const ChatWindow = ({ id }: { id?: string }) => { /> ) : ( - + )}
) diff --git a/ui/components/DeleteChat.tsx b/ui/components/DeleteChat.tsx index 165f86e3..366bb6fe 100644 --- a/ui/components/DeleteChat.tsx +++ b/ui/components/DeleteChat.tsx @@ -1,8 +1,8 @@ -import { Delete, Trash } from 'lucide-react'; -import { Dialog, Transition } from '@headlessui/react'; -import { Fragment, useState } from 'react'; -import { toast } from 'sonner'; -import { Chat } from '@/app/library/page'; +import { Delete, Trash } from "lucide-react"; +import { Dialog, Transition } from "@headlessui/react"; +import { Fragment, useState } from "react"; +import { toast } from "sonner"; +import { Chat } from "@/app/library/page"; const DeleteChat = ({ chatId, @@ -19,21 +19,18 @@ const DeleteChat = ({ const handleDelete = async () => { setLoading(true); try { - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, - { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", }, - ); + }); if (res.status != 200) { - throw new Error('Failed to delete chat'); + throw new Error("Failed to delete chat"); } - const newChats = chats.filter((chat) => chat.id !== chatId); + const newChats = chats.filter(chat => chat.id !== chatId); setChats(newChats); } catch (err: any) { diff --git a/ui/components/EmptyChat.tsx b/ui/components/EmptyChat.tsx index ea3642b6..eac3f2c9 100644 --- a/ui/components/EmptyChat.tsx +++ b/ui/components/EmptyChat.tsx @@ -1,4 +1,4 @@ -import EmptyChatMessageInput from './EmptyChatMessageInput'; +import EmptyChatMessageInput from "./EmptyChatMessageInput"; const EmptyChat = ({ sendMessage, @@ -12,14 +12,8 @@ const EmptyChat = ({ return (
-

- Research begins here. -

- +

Research begins here.

+
); diff --git a/ui/components/EmptyChatMessageInput.tsx b/ui/components/EmptyChatMessageInput.tsx index 0ff9b2e3..bd106bed 100644 --- a/ui/components/EmptyChatMessageInput.tsx +++ b/ui/components/EmptyChatMessageInput.tsx @@ -1,8 +1,8 @@ -import { ArrowRight } from 'lucide-react'; -import { useEffect, useRef, useState } from 'react'; -import TextareaAutosize from 'react-textarea-autosize'; -import CopilotToggle from './MessageInputActions/Copilot'; -import Focus from './MessageInputActions/Focus'; +import { ArrowRight } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import TextareaAutosize from "react-textarea-autosize"; +import CopilotToggle from "./MessageInputActions/Copilot"; +import Focus from "./MessageInputActions/Focus"; const EmptyChatMessageInput = ({ sendMessage, @@ -14,37 +14,37 @@ const EmptyChatMessageInput = ({ setFocusMode: (mode: string) => void; }) => { const [copilotEnabled, setCopilotEnabled] = useState(false); - const [message, setMessage] = useState(''); + const [message, setMessage] = useState(""); const inputRef = useRef(null); const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === '/') { + if (e.key === "/") { e.preventDefault(); inputRef.current?.focus(); } }; useEffect(() => { - document.addEventListener('keydown', handleKeyDown); + document.addEventListener("keydown", handleKeyDown); return () => { - document.removeEventListener('keydown', handleKeyDown); + document.removeEventListener("keydown", handleKeyDown); }; }, []); return (
{ + onSubmit={e => { e.preventDefault(); sendMessage(message); - setMessage(''); + setMessage(""); }} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { + onKeyDown={e => { + if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(message); - setMessage(''); + setMessage(""); } }} className="w-full" @@ -53,7 +53,7 @@ const EmptyChatMessageInput = ({ setMessage(e.target.value)} + onChange={e => setMessage(e.target.value)} minRows={2} className="bg-transparent placeholder:text-black/50 dark:placeholder:text-white/50 text-sm text-black dark:text-white resize-none focus:outline-none w-full max-h-24 lg:max-h-36 xl:max-h-48" placeholder="Ask anything..." @@ -64,10 +64,7 @@ const EmptyChatMessageInput = ({ {/* */}
- +
@@ -145,7 +118,7 @@ const MessageBox = ({ {isLast && message.suggestions && message.suggestions.length > 0 && - message.role === 'assistant' && + message.role === "assistant" && !loading && ( <>
@@ -156,10 +129,7 @@ const MessageBox = ({
{message.suggestions.map((suggestion, i) => ( -
+
{ @@ -167,13 +137,8 @@ const MessageBox = ({ }} className="cursor-pointer flex flex-row justify-between font-medium space-x-2 items-center" > -

- {suggestion} -

- +

{suggestion}

+
))} @@ -184,14 +149,8 @@ const MessageBox = ({
- - + +
)} diff --git a/ui/components/MessageInput.tsx b/ui/components/MessageInput.tsx index 2229cdf3..072c7581 100644 --- a/ui/components/MessageInput.tsx +++ b/ui/components/MessageInput.tsx @@ -1,84 +1,75 @@ -import { cn } from '@/lib/utils'; -import { ArrowUp } from 'lucide-react'; -import { useEffect, useRef, useState } from 'react'; -import TextareaAutosize from 'react-textarea-autosize'; -import Attach from './MessageInputActions/Attach'; -import CopilotToggle from './MessageInputActions/Copilot'; +import { cn } from "@/lib/utils"; +import { ArrowUp } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import TextareaAutosize from "react-textarea-autosize"; +import Attach from "./MessageInputActions/Attach"; +import CopilotToggle from "./MessageInputActions/Copilot"; -const MessageInput = ({ - sendMessage, - loading, -}: { - sendMessage: (message: string) => void; - loading: boolean; -}) => { +const MessageInput = ({ sendMessage, loading }: { sendMessage: (message: string) => void; loading: boolean }) => { const [copilotEnabled, setCopilotEnabled] = useState(false); - const [message, setMessage] = useState(''); + const [message, setMessage] = useState(""); const [textareaRows, setTextareaRows] = useState(1); - const [mode, setMode] = useState<'multi' | 'single'>('single'); + const [mode, setMode] = useState<"multi" | "single">("single"); useEffect(() => { - if (textareaRows >= 2 && message && mode === 'single') { - setMode('multi'); - } else if (!message && mode === 'multi') { - setMode('single'); + if (textareaRows >= 2 && message && mode === "single") { + setMode("multi"); + } else if (!message && mode === "multi") { + setMode("single"); } }, [textareaRows, mode, message]); const inputRef = useRef(null); const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === '/') { + if (e.key === "/") { e.preventDefault(); inputRef.current?.focus(); } }; useEffect(() => { - document.addEventListener('keydown', handleKeyDown); + document.addEventListener("keydown", handleKeyDown); return () => { - document.removeEventListener('keydown', handleKeyDown); + document.removeEventListener("keydown", handleKeyDown); }; }, []); return ( { + onSubmit={e => { if (loading) return; e.preventDefault(); sendMessage(message); - setMessage(''); + setMessage(""); }} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey && !loading) { + onKeyDown={e => { + if (e.key === "Enter" && !e.shiftKey && !loading) { e.preventDefault(); sendMessage(message); - setMessage(''); + setMessage(""); } }} className={cn( - 'bg-light-secondary dark:bg-dark-secondary p-4 flex items-center overflow-hidden border border-light-200 dark:border-dark-200', - mode === 'multi' ? 'flex-col rounded-lg' : 'flex-row rounded-full', + "bg-light-secondary dark:bg-dark-secondary p-4 flex items-center overflow-hidden border border-light-200 dark:border-dark-200", + mode === "multi" ? "flex-col rounded-lg" : "flex-row rounded-full", )} > - {mode === 'single' && } + {mode === "single" && } setMessage(e.target.value)} + onChange={e => setMessage(e.target.value)} onHeightChange={(height, props) => { setTextareaRows(Math.ceil(height / props.rowHeight)); }} className="transition bg-transparent dark:placeholder:text-white/50 placeholder:text-sm text-sm dark:text-white resize-none focus:outline-none w-full px-2 max-h-24 lg:max-h-36 xl:max-h-48 flex-grow flex-shrink" placeholder="Ask a follow-up" /> - {mode === 'single' && ( + {mode === "single" && (
- +
)} - {mode === 'multi' && ( + {mode === "multi" && (
- +
diff --git a/ui/components/MessageSources.tsx b/ui/components/MessageSources.tsx index 71eebf83..87249887 100644 --- a/ui/components/MessageSources.tsx +++ b/ui/components/MessageSources.tsx @@ -1,19 +1,19 @@ /* eslint-disable @next/next/no-img-element */ -import { Dialog, Transition } from '@headlessui/react'; -import { Document } from '@langchain/core/documents'; -import { Fragment, useState } from 'react'; +import { Dialog, Transition } from "@headlessui/react"; +import { Document } from "@langchain/core/documents"; +import { Fragment, useState } from "react"; const MessageSources = ({ sources }: { sources: Document[] }) => { const [isDialogOpen, setIsDialogOpen] = useState(false); const closeModal = () => { setIsDialogOpen(false); - document.body.classList.remove('overflow-hidden-scrollable'); + document.body.classList.remove("overflow-hidden-scrollable"); }; const openModal = () => { setIsDialogOpen(true); - document.body.classList.add('overflow-hidden-scrollable'); + document.body.classList.add("overflow-hidden-scrollable"); }; return ( @@ -38,7 +38,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { className="rounded-lg h-4 w-4" />

- {source.metadata.url.replace(/.+\/\/|www.|\..+/g, '')} + {source.metadata.url.replace(/.+\/\/|www.|\..+/g, "")}

@@ -65,9 +65,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { /> ))}
-

- View {sources.length - 3} more -

+

View {sources.length - 3} more

)} @@ -84,9 +82,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { leaveTo="opacity-0 scale-95" > - - Sources - + Sources
diff --git a/ui/components/Navbar.tsx b/ui/components/Navbar.tsx index 020dfb4f..184926a4 100644 --- a/ui/components/Navbar.tsx +++ b/ui/components/Navbar.tsx @@ -1,23 +1,18 @@ -import { Clock, Edit, Share, Trash } from 'lucide-react'; -import { Message } from './ChatWindow'; -import { useEffect, useState } from 'react'; -import { formatTimeDifference } from '@/lib/utils'; +import { Clock, Edit, Share, Trash } from "lucide-react"; +import { Message } from "./ChatWindow"; +import { useEffect, useState } from "react"; +import { formatTimeDifference } from "@/lib/utils"; const Navbar = ({ messages }: { messages: Message[] }) => { - const [title, setTitle] = useState(''); - const [timeAgo, setTimeAgo] = useState(''); + const [title, setTitle] = useState(""); + const [timeAgo, setTimeAgo] = useState(""); useEffect(() => { if (messages.length > 0) { const newTitle = - messages[0].content.length > 20 - ? `${messages[0].content.substring(0, 20).trim()}...` - : messages[0].content; + messages[0].content.length > 20 ? `${messages[0].content.substring(0, 20).trim()}...` : messages[0].content; setTitle(newTitle); - const newTimeAgo = formatTimeDifference( - new Date(), - messages[0].createdAt, - ); + const newTimeAgo = formatTimeDifference(new Date(), messages[0].createdAt); setTimeAgo(newTimeAgo); } }, [messages]); @@ -25,10 +20,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => { useEffect(() => { const intervalId = setInterval(() => { if (messages.length > 0) { - const newTimeAgo = formatTimeDifference( - new Date(), - messages[0].createdAt, - ); + const newTimeAgo = formatTimeDifference(new Date(), messages[0].createdAt); setTimeAgo(newTimeAgo); } }, 1000); @@ -39,10 +31,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => { return (
- +

{timeAgo} ago

@@ -50,14 +39,8 @@ const Navbar = ({ messages }: { messages: Message[] }) => {

{title}

- - + +
); diff --git a/ui/components/SearchImages.tsx b/ui/components/SearchImages.tsx index b53b8b0b..9fa1850e 100644 --- a/ui/components/SearchImages.tsx +++ b/ui/components/SearchImages.tsx @@ -1,9 +1,9 @@ /* eslint-disable @next/next/no-img-element */ -import { ImagesIcon, PlusIcon } from 'lucide-react'; -import { useState } from 'react'; -import Lightbox from 'yet-another-react-lightbox'; -import 'yet-another-react-lightbox/styles.css'; -import { Message } from './ChatWindow'; +import { ImagesIcon, PlusIcon } from "lucide-react"; +import { useState } from "react"; +import Lightbox from "yet-another-react-lightbox"; +import "yet-another-react-lightbox/styles.css"; +import { Message } from "./ChatWindow"; type Image = { url: string; @@ -11,13 +11,7 @@ type Image = { title: string; }; -const SearchImages = ({ - query, - chat_history, -}: { - query: string; - chat_history: Message[]; -}) => { +const SearchImages = ({ query, chat_history }: { query: string; chat_history: Message[] }) => { const [images, setImages] = useState(null); const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); @@ -30,24 +24,21 @@ const SearchImages = ({ onClick={async () => { setLoading(true); - const chatModelProvider = localStorage.getItem('chatModelProvider'); - const chatModel = localStorage.getItem('chatModel'); + const chatModelProvider = localStorage.getItem("chatModelProvider"); + const chatModel = localStorage.getItem("chatModel"); - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/images`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: query, - chat_history: chat_history, - chat_model_provider: chatModelProvider, - chat_model: chatModel, - }), + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/images`, { + method: "POST", + headers: { + "Content-Type": "application/json", }, - ); + body: JSON.stringify({ + query: query, + chat_history: chat_history, + chat_model_provider: chatModelProvider, + chat_model: chatModel, + }), + }); const data = await res.json(); @@ -89,11 +80,7 @@ const SearchImages = ({ { setOpen(true); - setSlides([ - slides[i], - ...slides.slice(0, i), - ...slides.slice(i + 1), - ]); + setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]); }} key={i} src={image.img_src} @@ -105,11 +92,7 @@ const SearchImages = ({ { setOpen(true); - setSlides([ - slides[i], - ...slides.slice(0, i), - ...slides.slice(i + 1), - ]); + setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]); }} key={i} src={image.img_src} @@ -132,9 +115,7 @@ const SearchImages = ({ /> ))}
-

- View {images.length - 3} more -

+

View {images.length - 3} more

)}
diff --git a/ui/components/SearchVideos.tsx b/ui/components/SearchVideos.tsx index 26463228..1d244ab8 100644 --- a/ui/components/SearchVideos.tsx +++ b/ui/components/SearchVideos.tsx @@ -1,9 +1,9 @@ /* eslint-disable @next/next/no-img-element */ -import { PlayCircle, PlayIcon, PlusIcon, VideoIcon } from 'lucide-react'; -import { useState } from 'react'; -import Lightbox, { GenericSlide, VideoSlide } from 'yet-another-react-lightbox'; -import 'yet-another-react-lightbox/styles.css'; -import { Message } from './ChatWindow'; +import { PlayCircle, PlayIcon, PlusIcon, VideoIcon } from "lucide-react"; +import { useState } from "react"; +import Lightbox, { GenericSlide, VideoSlide } from "yet-another-react-lightbox"; +import "yet-another-react-lightbox/styles.css"; +import { Message } from "./ChatWindow"; type Video = { url: string; @@ -12,25 +12,19 @@ type Video = { iframe_src: string; }; -declare module 'yet-another-react-lightbox' { +declare module "yet-another-react-lightbox" { export interface VideoSlide extends GenericSlide { - type: 'video-slide'; + type: "video-slide"; src: string; iframe_src: string; } interface SlideTypes { - 'video-slide': VideoSlide; + "video-slide": VideoSlide; } } -const Searchvideos = ({ - query, - chat_history, -}: { - query: string; - chat_history: Message[]; -}) => { +const Searchvideos = ({ query, chat_history }: { query: string; chat_history: Message[] }) => { const [videos, setVideos] = useState(null); const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); @@ -43,24 +37,21 @@ const Searchvideos = ({ onClick={async () => { setLoading(true); - const chatModelProvider = localStorage.getItem('chatModelProvider'); - const chatModel = localStorage.getItem('chatModel'); + const chatModelProvider = localStorage.getItem("chatModelProvider"); + const chatModel = localStorage.getItem("chatModel"); - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/videos`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: query, - chat_history: chat_history, - chat_model_provider: chatModelProvider, - chat_model: chatModel, - }), + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/videos`, { + method: "POST", + headers: { + "Content-Type": "application/json", }, - ); + body: JSON.stringify({ + query: query, + chat_history: chat_history, + chat_model_provider: chatModelProvider, + chat_model: chatModel, + }), + }); const data = await res.json(); @@ -69,7 +60,7 @@ const Searchvideos = ({ setSlides( videos.map((video: Video) => { return { - type: 'video-slide', + type: "video-slide", iframe_src: video.iframe_src, src: video.img_src, }; @@ -104,11 +95,7 @@ const Searchvideos = ({
{ setOpen(true); - setSlides([ - slides[i], - ...slides.slice(0, i), - ...slides.slice(i + 1), - ]); + setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]); }} className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer" key={i} @@ -128,11 +115,7 @@ const Searchvideos = ({
{ setOpen(true); - setSlides([ - slides[i], - ...slides.slice(0, i), - ...slides.slice(i + 1), - ]); + setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]); }} className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer" key={i} @@ -163,9 +146,7 @@ const Searchvideos = ({ /> ))}
-

- View {videos.length - 3} more -

+

View {videos.length - 3} more

)}
@@ -175,7 +156,7 @@ const Searchvideos = ({ slides={slides} render={{ slide: ({ slide }) => - slide.type === 'video-slide' ? ( + slide.type === "video-slide" ? (