From 3d83131e5d03e8942f9978bf595a7caca5e2b3cd Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Thu, 25 Apr 2024 12:42:01 +0200 Subject: [PATCH 01/20] Add phi chat prompt template (#1071) --- .env.template | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.template b/.env.template index 0bb470998a6..6c964951012 100644 --- a/.env.template +++ b/.env.template @@ -206,6 +206,7 @@ MODELS=`[ "modelUrl": "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct", "websiteUrl": "https://azure.microsoft.com/en-us/blog/introducing-phi-3-redefining-whats-possible-with-slms/", "preprompt": "", + "chatPromptTemplate": "{{preprompt}}{{#each messages}}{{#ifUser}}<|user|>\n{{content}}<|end|>\n<|assistant|>\n{{/ifUser}}{{#ifAssistant}}{{content}}<|end|>\n{{/ifAssistant}}{{/each}}", "parameters": { "stop": ["<|end|>", "<|endoftext|>", "<|assistant|>"], "max_new_tokens": 1024, From de2df1a3653c3a97635cdac772cd14c15e701065 Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Thu, 25 Apr 2024 18:27:50 +0200 Subject: [PATCH 02/20] Add `ALLOW_INSECURE_COOKIES` feature flag (#1076) --- .env | 1 + README.md | 13 +++++++++++-- src/lib/server/auth.ts | 3 ++- src/routes/logout/+page.server.ts | 4 ++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.env b/.env index cd4b15fd795..232968a92e0 100644 --- a/.env +++ b/.env @@ -153,3 +153,4 @@ WEBHOOK_URL_REPORT_ASSISTANT=#provide webhook url to get notified when an assist ALLOWED_USER_EMAILS=`[]` # if it's defined, only these emails will be allowed to use the app USAGE_LIMITS=`{}` +ALLOW_INSECURE_COOKIES=false # recommended to keep this to false but set to true if you need to run over http without tls diff --git a/README.md b/README.md index 5a299e06dab..9676dd8014c 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ A chat interface using open source models, eg OpenAssistant or Llama. It is a Sv 3. [Web Search](#web-search) 4. [Text Embedding Models](#text-embedding-models) 5. [Extra parameters](#extra-parameters) -6. [Deploying to a HF Space](#deploying-to-a-hf-space) -7. [Building](#building) +6. [Common issues](#common-issues) +7. [Deploying to a HF Space](#deploying-to-a-hf-space) +8. [Building](#building) ## No Setup Deploy @@ -735,6 +736,14 @@ MODELS=`[ ]` ``` +## Common issues + +### 403:You don't have access to this conversation + +Most likely you are running chat-ui over HTTP. The recommended option is to setup something like NGINX to handle HTTPS and proxy the requests to chat-ui. If you really need to run over HTTP you can add `ALLOW_INSECURE_COOKIES=true` to your `.env.local`. + +Make sure to set your `PUBLIC_ORIGIN` in your `.env.local` to the correct URL as well. + ## Deploying to a HF Space Create a `DOTENV_LOCAL` secret to your HF space with the content of your .env.local, and they will be picked up automatically when you run. diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 96e6ec8f345..8a7cc54f5b3 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -10,6 +10,7 @@ import { OPENID_TOLERANCE, OPENID_RESOURCE, OPENID_CONFIG, + ALLOW_INSECURE_COOKIES, } from "$env/static/private"; import { sha256 } from "$lib/utils/sha256"; import { z } from "zod"; @@ -55,7 +56,7 @@ export function refreshSessionCookie(cookies: Cookies, sessionId: string) { path: "/", // So that it works inside the space's iframe sameSite: dev ? "lax" : "none", - secure: !dev, + secure: !dev && !(ALLOW_INSECURE_COOKIES === "true"), httpOnly: true, expires: addWeeks(new Date(), 2), }); diff --git a/src/routes/logout/+page.server.ts b/src/routes/logout/+page.server.ts index 5e91d6df8f6..68af60cecb8 100644 --- a/src/routes/logout/+page.server.ts +++ b/src/routes/logout/+page.server.ts @@ -1,6 +1,6 @@ import { dev } from "$app/environment"; import { base } from "$app/paths"; -import { COOKIE_NAME } from "$env/static/private"; +import { COOKIE_NAME, ALLOW_INSECURE_COOKIES } from "$env/static/private"; import { collections } from "$lib/server/database"; import { redirect } from "@sveltejs/kit"; @@ -12,7 +12,7 @@ export const actions = { path: "/", // So that it works inside the space's iframe sameSite: dev ? "lax" : "none", - secure: !dev, + secure: !dev && !(ALLOW_INSECURE_COOKIES === "true"), httpOnly: true, }); throw redirect(303, `${base}/`); From 745e51e28bab8c380564efb32056a3508ee94f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Pumar?= Date: Thu, 25 Apr 2024 18:30:37 +0200 Subject: [PATCH 03/20] =?UTF-8?q?=F0=9F=8E=A8=20Add=20color=20for=20copy?= =?UTF-8?q?=20icon=20(#1070)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/CopyToClipBoardBtn.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/CopyToClipBoardBtn.svelte b/src/lib/components/CopyToClipBoardBtn.svelte index 53f4c122c66..bafb71c9a58 100644 --- a/src/lib/components/CopyToClipBoardBtn.svelte +++ b/src/lib/components/CopyToClipBoardBtn.svelte @@ -43,7 +43,7 @@ >
- + From bc30bd1a4ca81a9a713903051e812aa61d5b35e3 Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Fri, 26 Apr 2024 12:26:47 +0200 Subject: [PATCH 04/20] set `sameSite` to `lax` when allowing insecure cookies (#1078) --- src/lib/server/auth.ts | 2 +- src/routes/logout/+page.server.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 8a7cc54f5b3..f9a3da1e867 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -55,7 +55,7 @@ export function refreshSessionCookie(cookies: Cookies, sessionId: string) { cookies.set(COOKIE_NAME, sessionId, { path: "/", // So that it works inside the space's iframe - sameSite: dev ? "lax" : "none", + sameSite: dev || ALLOW_INSECURE_COOKIES === "true" ? "lax" : "none", secure: !dev && !(ALLOW_INSECURE_COOKIES === "true"), httpOnly: true, expires: addWeeks(new Date(), 2), diff --git a/src/routes/logout/+page.server.ts b/src/routes/logout/+page.server.ts index 68af60cecb8..dd286eaa51b 100644 --- a/src/routes/logout/+page.server.ts +++ b/src/routes/logout/+page.server.ts @@ -11,7 +11,7 @@ export const actions = { cookies.delete(COOKIE_NAME, { path: "/", // So that it works inside the space's iframe - sameSite: dev ? "lax" : "none", + sameSite: dev || ALLOW_INSECURE_COOKIES === "true" ? "lax" : "none", secure: !dev && !(ALLOW_INSECURE_COOKIES === "true"), httpOnly: true, }); From 09fc057f331d04679ea2284ac4ea7d834ad8461a Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Fri, 26 Apr 2024 17:43:37 +0200 Subject: [PATCH 05/20] Reduce new tokens on command R+ (#1080) --- .env.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 6c964951012..0313c2fa760 100644 --- a/.env.template +++ b/.env.template @@ -10,8 +10,8 @@ MODELS=`[ "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/cohere-logo.png", "parameters": { "stop": ["<|END_OF_TURN_TOKEN|>"], - "truncate" : 24576, - "max_new_tokens" : 8192, + "truncate" : 28672, + "max_new_tokens" : 4096, "temperature" : 0.3 }, "promptExamples" : [ From e3ca107b9dbb93cfa4757c6829dfd47482eef37a Mon Sep 17 00:00:00 2001 From: Gustavo de Rosa Date: Fri, 26 Apr 2024 19:16:13 -0300 Subject: [PATCH 06/20] Adds to Phi-3-mini-4k-instruct template configuration (#1081) Adds to Phi-3-mini-4k-instruct template configuration. --- .env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 0313c2fa760..a4547e53199 100644 --- a/.env.template +++ b/.env.template @@ -206,7 +206,7 @@ MODELS=`[ "modelUrl": "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct", "websiteUrl": "https://azure.microsoft.com/en-us/blog/introducing-phi-3-redefining-whats-possible-with-slms/", "preprompt": "", - "chatPromptTemplate": "{{preprompt}}{{#each messages}}{{#ifUser}}<|user|>\n{{content}}<|end|>\n<|assistant|>\n{{/ifUser}}{{#ifAssistant}}{{content}}<|end|>\n{{/ifAssistant}}{{/each}}", + "chatPromptTemplate": "{{preprompt}}{{#each messages}}{{#ifUser}}<|user|>\n{{content}}<|end|>\n<|assistant|>\n{{/ifUser}}{{#ifAssistant}}{{content}}<|end|>\n{{/ifAssistant}}{{/each}}", "parameters": { "stop": ["<|end|>", "<|endoftext|>", "<|assistant|>"], "max_new_tokens": 1024, From fc53d26c6418239f9c5f641094b80fd221dc250e Mon Sep 17 00:00:00 2001 From: Nishith Jain <167524748+KingNishHF@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:04:37 +0530 Subject: [PATCH 07/20] Sort by Trending to default (#1082) * Sort by Trending to default Switched the default sorting from 'popular' to 'trending'. * Set TRENDING as the default sort key in load function --------- Co-authored-by: Nathan Sarrazin --- src/routes/assistants/+page.server.ts | 2 +- src/routes/assistants/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/assistants/+page.server.ts b/src/routes/assistants/+page.server.ts index a30c964a8d4..5be72ff5412 100644 --- a/src/routes/assistants/+page.server.ts +++ b/src/routes/assistants/+page.server.ts @@ -18,7 +18,7 @@ export const load = async ({ url, locals }) => { const pageIndex = parseInt(url.searchParams.get("p") ?? "0"); const username = url.searchParams.get("user"); const query = url.searchParams.get("q")?.trim() ?? null; - const sort = url.searchParams.get("sort")?.trim() ?? SortKey.POPULAR; + const sort = url.searchParams.get("sort")?.trim() ?? SortKey.TRENDING; const createdByCurrentUser = locals.user?.username && locals.user.username === username; let user: Pick | null = null; diff --git a/src/routes/assistants/+page.svelte b/src/routes/assistants/+page.svelte index b27524aacc3..e8a84bbcfc7 100644 --- a/src/routes/assistants/+page.svelte +++ b/src/routes/assistants/+page.svelte @@ -213,8 +213,8 @@ on:change={sortAssistants} class="rounded-lg border border-gray-300 bg-gray-50 px-2 py-1 text-sm text-gray-900 focus:border-blue-700 focus:ring-blue-700 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" > - +
From ddbe7d2cd252ba5fcd6a89ac28624f0de97905a0 Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Tue, 30 Apr 2024 09:45:22 +0200 Subject: [PATCH 08/20] Use pino for logs (#1086) * Use pino for logs * Use logger for info * Add errorId to the logs --- package-lock.json | 271 +++++++++++++++++- package.json | 2 + src/app.d.ts | 5 + src/hooks.server.ts | 28 +- .../refresh-assistants-counts.ts | 5 +- src/lib/migrations/migrations.ts | 17 +- src/lib/server/abortedGenerations.ts | 3 +- src/lib/server/auth.ts | 3 +- src/lib/server/database.ts | 65 ++--- .../hfApi/embeddingHfApi.ts | 5 +- .../tei/embeddingEndpoints.ts | 3 +- .../cloudflare/endpointCloudflare.ts | 5 +- .../endpoints/langserve/endpointLangserve.ts | 5 +- .../endpoints/llamacpp/endpointLlamacpp.ts | 5 +- src/lib/server/logger.ts | 18 ++ src/lib/server/models.ts | 3 +- src/lib/server/preprocessMessages.ts | 3 +- src/lib/server/summarize.ts | 3 +- src/lib/server/websearch/searchSearxng.ts | 3 +- src/routes/+error.svelte | 5 + src/routes/admin/export/+server.ts | 13 +- src/routes/admin/stats/compute/+server.ts | 11 +- src/routes/conversation/[id]/+server.ts | 3 +- .../assistants/[assistantId]/+page.server.ts | 3 +- 24 files changed, 413 insertions(+), 74 deletions(-) create mode 100644 src/lib/server/logger.ts diff --git a/package-lock.json b/package-lock.json index 66541d0f92e..0d637ce4db4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,8 @@ "nanoid": "^4.0.2", "openid-client": "^5.4.2", "parquetjs": "^0.11.2", + "pino": "^9.0.0", + "pino-pretty": "^11.0.0", "postcss": "^8.4.31", "saslprep": "^1.0.3", "satori": "^0.10.11", @@ -2451,7 +2453,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "optional": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -2612,6 +2613,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -3103,6 +3112,11 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3313,6 +3327,14 @@ "node": ">=6" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3860,11 +3882,18 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "optional": true, "engines": { "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3902,6 +3931,11 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "optional": true }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3957,6 +3991,19 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -4430,6 +4477,11 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, "node_modules/hex-rgb": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz", @@ -4755,6 +4807,14 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "engines": { + "node": ">=10" + } + }, "node_modules/js-base64": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", @@ -5586,6 +5646,14 @@ "node": "^10.13.0 || >=12.0.0" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5920,6 +5988,141 @@ "node": ">=0.10.0" } }, + "node_modules/pino": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.0.0.tgz", + "integrity": "sha512-uI1ThkzTShNSwvsUM6b4ND8ANzWURk9zTELMztFkmnCQeR/4wkomJ+echHee5GMWGovoSfjwdeu80DsFIt7mbA==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.6.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.0.0.tgz", + "integrity": "sha512-YFJZqw59mHIY72wBnBs7XhLGG6qpJMa4pEQTRgEPEbjIYbng2LXEZZF1DoyDg9CfejEy8uZCyzpcBXXG0oOCwQ==", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-pretty/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -6313,6 +6516,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, "node_modules/protobufjs": { "version": "6.11.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", @@ -6421,6 +6637,11 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6481,6 +6702,14 @@ "node": ">=8.10.0" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -6609,6 +6838,14 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -6688,6 +6925,11 @@ "node": ">=v12.22.7" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -6924,6 +7166,14 @@ "npm": ">= 3.0.0" } }, + "node_modules/sonic-boom": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/sorcery": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz", @@ -6963,6 +7213,14 @@ "memory-pager": "^1.0.2" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -7039,7 +7297,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -7471,6 +7728,14 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/thrift": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/thrift/-/thrift-0.11.0.tgz", diff --git a/package.json b/package.json index fdc02ba1eb7..fe0ad318bdf 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,8 @@ "nanoid": "^4.0.2", "openid-client": "^5.4.2", "parquetjs": "^0.11.2", + "pino": "^9.0.0", + "pino-pretty": "^11.0.0", "postcss": "^8.4.31", "saslprep": "^1.0.3", "satori": "^0.10.11", diff --git a/src/app.d.ts b/src/app.d.ts index dfd942be68d..681fd365433 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -12,6 +12,11 @@ declare global { sessionId: string; user?: User; } + + interface Error { + message: string; + errorId?: ReturnType; + } // interface PageData {} // interface Platform {} } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index d749c38e531..4272fe8c17e 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -6,7 +6,7 @@ import { MESSAGES_BEFORE_LOGIN, PARQUET_EXPORT_SECRET, } from "$env/static/private"; -import type { Handle } from "@sveltejs/kit"; +import type { Handle, HandleServerError } from "@sveltejs/kit"; import { PUBLIC_GOOGLE_ANALYTICS_ID, PUBLIC_ORIGIN, @@ -21,6 +21,7 @@ import { addWeeks } from "date-fns"; import { checkAndRunMigrations } from "$lib/migrations/migrations"; import { building } from "$app/environment"; import { refreshAssistantsCounts } from "$lib/assistantStats/refresh-assistants-counts"; +import { logger } from "$lib/server/logger"; if (!building) { await checkAndRunMigrations(); @@ -29,6 +30,31 @@ if (!building) { } } +export const handleError: HandleServerError = async ({ error, event }) => { + // handle 404 + if (event.route.id === null) { + return { + message: `Page ${event.url.pathname} not found`, + }; + } + + const errorId = crypto.randomUUID(); + + logger.error({ + locals: event.locals, + url: event.request.url, + params: event.params, + request: event.request, + error, + errorId, + }); + + return { + message: "An error occurred", + errorId, + }; +}; + export const handle: Handle = async ({ event, resolve }) => { if (event.url.pathname.startsWith(`${base}/api/`) && EXPOSE_API !== "true") { return new Response("API is disabled", { status: 403 }); diff --git a/src/lib/assistantStats/refresh-assistants-counts.ts b/src/lib/assistantStats/refresh-assistants-counts.ts index 4376202638c..07013e1989a 100644 --- a/src/lib/assistantStats/refresh-assistants-counts.ts +++ b/src/lib/assistantStats/refresh-assistants-counts.ts @@ -2,6 +2,7 @@ import { client, collections } from "$lib/server/database"; import { acquireLock, refreshLock } from "$lib/migrations/lock"; import type { ObjectId } from "mongodb"; import { subDays } from "date-fns"; +import { logger } from "$lib/server/logger"; const LOCK_KEY = "assistants.count"; @@ -53,8 +54,8 @@ async function refreshAssistantsCountsHelper() { }) ); } catch (e) { - console.log("Refresh assistants counter failed!"); - console.error(e); + logger.error("Refresh assistants counter failed!"); + logger.error(e); } } diff --git a/src/lib/migrations/migrations.ts b/src/lib/migrations/migrations.ts index 2615f170b5f..6e7da313491 100644 --- a/src/lib/migrations/migrations.ts +++ b/src/lib/migrations/migrations.ts @@ -2,6 +2,7 @@ import { client, collections } from "$lib/server/database"; import { migrations } from "./routines"; import { acquireLock, releaseLock, isDBLocked, refreshLock } from "./lock"; import { isHuggingChat } from "$lib/utils/isHuggingChat"; +import { logger } from "$lib/server/logger"; const LOCK_KEY = "migrations"; @@ -14,7 +15,7 @@ export async function checkAndRunMigrations() { // check if all migrations have already been run const migrationResults = await collections.migrationResults.find().toArray(); - console.log("[MIGRATIONS] Begin check..."); + logger.info("[MIGRATIONS] Begin check..."); // connect to the database const connectedClient = await client.connect(); @@ -23,7 +24,7 @@ export async function checkAndRunMigrations() { if (!lockId) { // another instance already has the lock, so we exit early - console.log( + logger.info( "[MIGRATIONS] Another instance already has the lock. Waiting for DB to be unlocked." ); @@ -50,21 +51,21 @@ export async function checkAndRunMigrations() { // check if the migration has already been applied if (!shouldRun) { - console.log(`[MIGRATIONS] "${migration.name}" already applied. Skipping...`); + logger.info(`[MIGRATIONS] "${migration.name}" already applied. Skipping...`); } else { // check the modifiers to see if some cases match if ( (migration.runForHuggingChat === "only" && !isHuggingChat) || (migration.runForHuggingChat === "never" && isHuggingChat) ) { - console.log( + logger.info( `[MIGRATIONS] "${migration.name}" should not be applied for this run. Skipping...` ); continue; } // otherwise all is good and we can run the migration - console.log( + logger.info( `[MIGRATIONS] "${migration.name}" ${ migration.runEveryTime ? "should run every time" : "not applied yet" }. Applying...` @@ -89,8 +90,8 @@ export async function checkAndRunMigrations() { result = await migration.up(connectedClient); }); } catch (e) { - console.log(`[MIGRATION[] "${migration.name}" failed!`); - console.error(e); + logger.info(`[MIGRATIONS] "${migration.name}" failed!`); + logger.error(e); } finally { await session.endSession(); } @@ -108,7 +109,7 @@ export async function checkAndRunMigrations() { } } - console.log("[MIGRATIONS] All migrations applied. Releasing lock"); + logger.info("[MIGRATIONS] All migrations applied. Releasing lock"); clearInterval(refreshInterval); await releaseLock(LOCK_KEY, lockId); diff --git a/src/lib/server/abortedGenerations.ts b/src/lib/server/abortedGenerations.ts index 575cf637bfe..84ad9ec9041 100644 --- a/src/lib/server/abortedGenerations.ts +++ b/src/lib/server/abortedGenerations.ts @@ -2,6 +2,7 @@ import { setTimeout } from "node:timers/promises"; import { collections } from "./database"; +import { logger } from "$lib/server/logger"; let closed = false; process.on("SIGINT", () => { @@ -21,7 +22,7 @@ async function maintainAbortedGenerations() { aborts.map(({ conversationId, createdAt }) => [conversationId.toString(), createdAt]) ); } catch (err) { - console.error(err); + logger.error(err); } } } diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index f9a3da1e867..9a76d25fd66 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -18,6 +18,7 @@ import { dev } from "$app/environment"; import type { Cookies } from "@sveltejs/kit"; import { collections } from "./database"; import JSON5 from "json5"; +import { logger } from "$lib/server/logger"; export interface OIDCSettings { redirectURI: string; @@ -151,7 +152,7 @@ export async function validateAndParseCsrfToken( return { redirectUrl: data.redirectUrl }; } } catch (e) { - console.error(e); + logger.error(e); } return null; } diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index fa6d2b8f0ff..ba4759ee8a7 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -13,6 +13,7 @@ import type { ConversationStats } from "$lib/types/ConversationStats"; import type { MigrationResult } from "$lib/types/MigrationResult"; import type { Semaphore } from "$lib/types/Semaphore"; import type { AssistantStats } from "$lib/types/AssistantStats"; +import { logger } from "$lib/server/logger"; if (!MONGODB_URL) { throw new Error( @@ -25,7 +26,7 @@ const client = new MongoClient(MONGODB_URL, { directConnection: MONGODB_DIRECT_CONNECTION === "true", }); -export const connectPromise = client.connect().catch(console.error); +export const connectPromise = client.connect().catch(logger.error); export function getCollections(mongoClient: MongoClient) { const db = mongoClient.db(MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "")); @@ -89,25 +90,25 @@ client.on("open", () => { { sessionId: 1, updatedAt: -1 }, { partialFilterExpression: { sessionId: { $exists: true } } } ) - .catch(console.error); + .catch(logger.error); conversations .createIndex( { userId: 1, updatedAt: -1 }, { partialFilterExpression: { userId: { $exists: true } } } ) - .catch(console.error); + .catch(logger.error); conversations .createIndex( { "message.id": 1, "message.ancestors": 1 }, { partialFilterExpression: { userId: { $exists: true } } } ) - .catch(console.error); + .catch(logger.error); // To do stats on conversations - conversations.createIndex({ updatedAt: 1 }).catch(console.error); + conversations.createIndex({ updatedAt: 1 }).catch(logger.error); // Not strictly necessary, could use _id, but more convenient. Also for stats - conversations.createIndex({ createdAt: 1 }).catch(console.error); + conversations.createIndex({ createdAt: 1 }).catch(logger.error); // To do stats on conversation messages - conversations.createIndex({ "messages.createdAt": 1 }, { sparse: true }).catch(console.error); + conversations.createIndex({ "messages.createdAt": 1 }, { sparse: true }).catch(logger.error); // Unique index for stats conversationStats .createIndex( @@ -120,7 +121,7 @@ client.on("open", () => { }, { unique: true } ) - .catch(console.error); + .catch(logger.error); // Allow easy check of last computed stat for given type/dateField conversationStats .createIndex({ @@ -128,34 +129,34 @@ client.on("open", () => { "date.field": 1, "date.at": 1, }) - .catch(console.error); - abortedGenerations.createIndex({ updatedAt: 1 }, { expireAfterSeconds: 30 }).catch(console.error); - abortedGenerations.createIndex({ conversationId: 1 }, { unique: true }).catch(console.error); - sharedConversations.createIndex({ hash: 1 }, { unique: true }).catch(console.error); - settings.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(console.error); - settings.createIndex({ userId: 1 }, { unique: true, sparse: true }).catch(console.error); - settings.createIndex({ assistants: 1 }).catch(console.error); - users.createIndex({ hfUserId: 1 }, { unique: true }).catch(console.error); - users.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(console.error); + .catch(logger.error); + abortedGenerations.createIndex({ updatedAt: 1 }, { expireAfterSeconds: 30 }).catch(logger.error); + abortedGenerations.createIndex({ conversationId: 1 }, { unique: true }).catch(logger.error); + sharedConversations.createIndex({ hash: 1 }, { unique: true }).catch(logger.error); + settings.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(logger.error); + settings.createIndex({ userId: 1 }, { unique: true, sparse: true }).catch(logger.error); + settings.createIndex({ assistants: 1 }).catch(logger.error); + users.createIndex({ hfUserId: 1 }, { unique: true }).catch(logger.error); + users.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(logger.error); // No unicity because due to renames & outdated info from oauth provider, there may be the same username on different users - users.createIndex({ username: 1 }).catch(console.error); - messageEvents.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(console.error); - sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }).catch(console.error); - sessions.createIndex({ sessionId: 1 }, { unique: true }).catch(console.error); - assistants.createIndex({ createdById: 1, userCount: -1 }).catch(console.error); - assistants.createIndex({ userCount: 1 }).catch(console.error); - assistants.createIndex({ featured: 1, userCount: -1 }).catch(console.error); - assistants.createIndex({ modelId: 1, userCount: -1 }).catch(console.error); - assistants.createIndex({ searchTokens: 1 }).catch(console.error); - assistants.createIndex({ last24HoursCount: 1 }).catch(console.error); + users.createIndex({ username: 1 }).catch(logger.error); + messageEvents.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(logger.error); + sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }).catch(logger.error); + sessions.createIndex({ sessionId: 1 }, { unique: true }).catch(logger.error); + assistants.createIndex({ createdById: 1, userCount: -1 }).catch(logger.error); + assistants.createIndex({ userCount: 1 }).catch(logger.error); + assistants.createIndex({ featured: 1, userCount: -1 }).catch(logger.error); + assistants.createIndex({ modelId: 1, userCount: -1 }).catch(logger.error); + assistants.createIndex({ searchTokens: 1 }).catch(logger.error); + assistants.createIndex({ last24HoursCount: 1 }).catch(logger.error); assistantStats // Order of keys is important for the queries .createIndex({ "date.span": 1, "date.at": 1, assistantId: 1 }, { unique: true }) - .catch(console.error); - reports.createIndex({ assistantId: 1 }).catch(console.error); - reports.createIndex({ createdBy: 1, assistantId: 1 }).catch(console.error); + .catch(logger.error); + reports.createIndex({ assistantId: 1 }).catch(logger.error); + reports.createIndex({ createdBy: 1, assistantId: 1 }).catch(logger.error); // Unique index for semaphore and migration results - semaphores.createIndex({ key: 1 }, { unique: true }).catch(console.error); - semaphores.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(console.error); + semaphores.createIndex({ key: 1 }, { unique: true }).catch(logger.error); + semaphores.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(logger.error); }); diff --git a/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts b/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts index 162e8964c00..140f223aa52 100644 --- a/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts +++ b/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import type { EmbeddingEndpoint, Embedding } from "../embeddingEndpoints"; import { chunk } from "$lib/utils/chunk"; import { HF_TOKEN } from "$env/static/private"; +import { logger } from "$lib/server/logger"; export const embeddingEndpointHfApiSchema = z.object({ weight: z.number().int().positive().default(1), @@ -35,8 +36,8 @@ export async function embeddingEndpointHfApi( }); if (!response.ok) { - console.log(await response.text()); - console.error("Failed to get embeddings from Hugging Face API", response); + logger.error(await response.text()); + logger.error("Failed to get embeddings from Hugging Face API", response); return []; } diff --git a/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts b/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts index 0d2c7ae1336..6c2f2f51bb6 100644 --- a/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts +++ b/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import type { EmbeddingEndpoint, Embedding } from "../embeddingEndpoints"; import { chunk } from "$lib/utils/chunk"; import { HF_TOKEN } from "$env/static/private"; +import { logger } from "$lib/server/logger"; export const embeddingEndpointTeiParametersSchema = z.object({ weight: z.number().int().positive().default(1), @@ -29,7 +30,7 @@ const getModelInfoByUrl = async (url: string, authorization?: string) => { const json = await response.json(); return { max_client_batch_size: 32, max_batch_tokens: 16384, ...json }; } catch { - console.log("Could not get info from TEI embedding endpoint. Using defaults."); + logger.debug("Could not get info from TEI embedding endpoint. Using defaults."); return { max_client_batch_size: 32, max_batch_tokens: 16384 }; } }; diff --git a/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts b/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts index 3a1be4d5175..50f9ac57efa 100644 --- a/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts +++ b/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import type { Endpoint } from "../endpoints"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; import { CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN } from "$env/static/private"; +import { logger } from "$lib/server/logger"; export const endpointCloudflareParametersSchema = z.object({ weight: z.number().int().positive().default(1), @@ -104,8 +105,8 @@ export async function endpointCloudflare( try { data = JSON.parse(jsonString); } catch (e) { - console.error("Failed to parse JSON", e); - console.error("Problematic JSON string:", jsonString); + logger.error("Failed to parse JSON", e); + logger.error("Problematic JSON string:", jsonString); continue; // Skip this iteration and try the next chunk } diff --git a/src/lib/server/endpoints/langserve/endpointLangserve.ts b/src/lib/server/endpoints/langserve/endpointLangserve.ts index 2c5a475c967..364765c478d 100644 --- a/src/lib/server/endpoints/langserve/endpointLangserve.ts +++ b/src/lib/server/endpoints/langserve/endpointLangserve.ts @@ -2,6 +2,7 @@ import { buildPrompt } from "$lib/buildPrompt"; import { z } from "zod"; import type { Endpoint } from "../endpoints"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; +import { logger } from "$lib/server/logger"; export const endpointLangserveParametersSchema = z.object({ weight: z.number().int().positive().default(1), @@ -99,8 +100,8 @@ export function endpointLangserve( try { data = JSON.parse(jsonString); } catch (e) { - console.error("Failed to parse JSON", e); - console.error("Problematic JSON string:", jsonString); + logger.error("Failed to parse JSON", e); + logger.error("Problematic JSON string:", jsonString); continue; // Skip this iteration and try the next chunk } // Assuming content within data is a plain string diff --git a/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts b/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts index ffd9fa2c495..67b5ea954d0 100644 --- a/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts +++ b/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts @@ -3,6 +3,7 @@ import { buildPrompt } from "$lib/buildPrompt"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; import type { Endpoint } from "../endpoints"; import { z } from "zod"; +import { logger } from "$lib/server/logger"; export const endpointLlamacppParametersSchema = z.object({ weight: z.number().int().positive().default(1), @@ -93,8 +94,8 @@ export function endpointLlamacpp( try { data = JSON.parse(jsonString); } catch (e) { - console.error("Failed to parse JSON", e); - console.error("Problematic JSON string:", jsonString); + logger.error("Failed to parse JSON", e); + logger.error("Problematic JSON string:", jsonString); continue; // Skip this iteration and try the next chunk } diff --git a/src/lib/server/logger.ts b/src/lib/server/logger.ts new file mode 100644 index 00000000000..76d9a2d77ab --- /dev/null +++ b/src/lib/server/logger.ts @@ -0,0 +1,18 @@ +import pino from "pino"; +import { dev } from "$app/environment"; + +let options: pino.LoggerOptions = {}; + +if (dev) { + options = { + level: "debug", + transport: { + target: "pino-pretty", + options: { + colorize: true, + }, + }, + }; +} + +export const logger = pino(options); diff --git a/src/lib/server/models.ts b/src/lib/server/models.ts index 20bd58645d0..0ba2fa7c830 100644 --- a/src/lib/server/models.ts +++ b/src/lib/server/models.ts @@ -18,6 +18,7 @@ import type { PreTrainedTokenizer } from "@xenova/transformers"; import JSON5 from "json5"; import { getTokenizer } from "$lib/utils/getTokenizer"; +import { logger } from "$lib/server/logger"; type Optional = Pick, K> & Omit; @@ -91,7 +92,7 @@ async function getChatPromptRender( try { tokenizer = await getTokenizer(m.tokenizer); } catch (e) { - console.error( + logger.error( "Failed to load tokenizer for model " + m.name + " consider setting chatPromptTemplate manually or making sure the model is available on the hub. Error: " + diff --git a/src/lib/server/preprocessMessages.ts b/src/lib/server/preprocessMessages.ts index 53768fa6f62..9cdcaae0487 100644 --- a/src/lib/server/preprocessMessages.ts +++ b/src/lib/server/preprocessMessages.ts @@ -2,6 +2,7 @@ import type { Conversation } from "$lib/types/Conversation"; import type { Message } from "$lib/types/Message"; import { format } from "date-fns"; import { downloadFile } from "./files/downloadFile"; +import { logger } from "$lib/server/logger"; export async function preprocessMessages( messages: Message[], @@ -44,7 +45,7 @@ Answer the question: ${lastQuestion}`; const b64 = image.toString("base64"); return `![](data:${mime};base64,${b64})})`; } catch (e) { - console.error(e); + logger.error(e); } }) ); diff --git a/src/lib/server/summarize.ts b/src/lib/server/summarize.ts index 264963614f1..f18639637bc 100644 --- a/src/lib/server/summarize.ts +++ b/src/lib/server/summarize.ts @@ -1,6 +1,7 @@ import { LLM_SUMMERIZATION } from "$env/static/private"; import { generateFromDefaultEndpoint } from "$lib/server/generateFromDefaultEndpoint"; import type { Message } from "$lib/types/Message"; +import { logger } from "$lib/server/logger"; export async function summarize(prompt: string) { if (!LLM_SUMMERIZATION) { @@ -41,7 +42,7 @@ export async function summarize(prompt: string) { return summary; }) .catch((e) => { - console.error(e); + logger.error(e); return null; }); } diff --git a/src/lib/server/websearch/searchSearxng.ts b/src/lib/server/websearch/searchSearxng.ts index a432003cb16..5378f953b47 100644 --- a/src/lib/server/websearch/searchSearxng.ts +++ b/src/lib/server/websearch/searchSearxng.ts @@ -1,4 +1,5 @@ import { SEARXNG_QUERY_URL } from "$env/static/private"; +import { logger } from "$lib/server/logger"; export async function searchSearxng(query: string) { const abortController = new AbortController(); @@ -18,7 +19,7 @@ export async function searchSearxng(query: string) { }) .then((response) => response.json() as Promise<{ results: { url: string }[] }>) .catch((error) => { - console.error("Failed to fetch or parse JSON", error); + logger.error("Failed to fetch or parse JSON", error); throw new Error("Failed to fetch or parse JSON"); }); diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index 6836376aa41..376412a7ffd 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -11,5 +11,10 @@

{$page.status}

{$page.error?.message}

+ {#if $page.error?.errorId} +
+
{$page.error
+					.errorId}
+ {/if}
diff --git a/src/routes/admin/export/+server.ts b/src/routes/admin/export/+server.ts index 94a3edcdddb..5c9e791e68d 100644 --- a/src/routes/admin/export/+server.ts +++ b/src/routes/admin/export/+server.ts @@ -7,6 +7,7 @@ import { unlink } from "node:fs/promises"; import { uploadFile } from "@huggingface/hub"; import parquet from "parquetjs"; import { z } from "zod"; +import { logger } from "$lib/server/logger.js"; // Triger like this: // curl -X POST "http://localhost:5173/chat/admin/export" -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"model": "OpenAssistant/oasst-sft-6-llama-30b-xor"}' @@ -41,7 +42,7 @@ export async function POST({ request }) { const writer = await parquet.ParquetWriter.openFile(schema, fileName); let count = 0; - console.log("Exporting conversations for model", model); + logger.info("Exporting conversations for model", model); for await (const conversation of collections.settings.aggregate<{ title: string; @@ -88,11 +89,11 @@ export async function POST({ request }) { ++count; if (count % 1_000 === 0) { - console.log("Exported", count, "conversations"); + logger.info("Exported", count, "conversations"); } } - console.log("exporting convos with userId"); + logger.info("exporting convos with userId"); for await (const conversation of collections.settings.aggregate<{ title: string; @@ -133,13 +134,13 @@ export async function POST({ request }) { ++count; if (count % 1_000 === 0) { - console.log("Exported", count, "conversations"); + logger.info("Exported", count, "conversations"); } } await writer.close(); - console.log("Uploading", fileName, "to Hugging Face Hub"); + logger.info("Uploading", fileName, "to Hugging Face Hub"); await uploadFile({ file: pathToFileURL(fileName) as URL, @@ -150,7 +151,7 @@ export async function POST({ request }) { }, }); - console.log("Upload done"); + logger.info("Upload done"); await unlink(fileName); diff --git a/src/routes/admin/stats/compute/+server.ts b/src/routes/admin/stats/compute/+server.ts index 5e73c36c26a..f814976f505 100644 --- a/src/routes/admin/stats/compute/+server.ts +++ b/src/routes/admin/stats/compute/+server.ts @@ -1,15 +1,16 @@ import { json } from "@sveltejs/kit"; import type { ConversationStats } from "$lib/types/ConversationStats"; import { CONVERSATION_STATS_COLLECTION, collections } from "$lib/server/database.js"; +import { logger } from "$lib/server/logger"; // Triger like this: // curl -X POST "http://localhost:5173/chat/admin/stats/compute" -H "Authorization: Bearer " export async function POST() { for (const span of ["day", "week", "month"] as const) { - computeStats({ dateField: "updatedAt", type: "conversation", span }).catch(console.error); - computeStats({ dateField: "createdAt", type: "conversation", span }).catch(console.error); - computeStats({ dateField: "createdAt", type: "message", span }).catch(console.error); + computeStats({ dateField: "updatedAt", type: "conversation", span }).catch(logger.error); + computeStats({ dateField: "createdAt", type: "conversation", span }).catch(logger.error); + computeStats({ dateField: "createdAt", type: "message", span }).catch(logger.error); } return json({}, { status: 202 }); @@ -29,7 +30,7 @@ async function computeStats(params: { // In those cases we need to compute the stats from before the last month as everything is one aggregation const minDate = lastComputed ? lastComputed.date.at : new Date(0); - console.log("Computing stats for", params.type, params.span, params.dateField, "from", minDate); + logger.info("Computing stats for", params.type, params.span, params.dateField, "from", minDate); const dateField = params.type === "message" ? "messages." + params.dateField : params.dateField; @@ -213,5 +214,5 @@ async function computeStats(params: { await collections.conversations.aggregate(pipeline, { allowDiskUse: true }).next(); - console.log("Computed stats for", params.type, params.span, params.dateField); + logger.info("Computed stats for", params.type, params.span, params.dateField); } diff --git a/src/routes/conversation/[id]/+server.ts b/src/routes/conversation/[id]/+server.ts index 02e6d84de90..a057e6e3a43 100644 --- a/src/routes/conversation/[id]/+server.ts +++ b/src/routes/conversation/[id]/+server.ts @@ -23,6 +23,7 @@ import { addSibling } from "$lib/utils/tree/addSibling.js"; import { preprocessMessages } from "$lib/server/preprocessMessages.js"; import { usageLimits } from "$lib/server/usageLimits"; import { isURLLocal } from "$lib/server/isURLLocal.js"; +import { logger } from "$lib/server/logger.js"; export async function POST({ request, locals, params, getClientAddress }) { const id = z.string().parse(params.id); @@ -334,7 +335,7 @@ export async function POST({ request, locals, params, getClientAddress }) { } ); } catch (e) { - console.error(e); + logger.error(e); } } })(); diff --git a/src/routes/settings/(nav)/assistants/[assistantId]/+page.server.ts b/src/routes/settings/(nav)/assistants/[assistantId]/+page.server.ts index 0734d964c39..26f09d176f7 100644 --- a/src/routes/settings/(nav)/assistants/[assistantId]/+page.server.ts +++ b/src/routes/settings/(nav)/assistants/[assistantId]/+page.server.ts @@ -7,6 +7,7 @@ import { PUBLIC_ORIGIN, PUBLIC_SHARE_PREFIX } from "$env/static/public"; import { WEBHOOK_URL_REPORT_ASSISTANT } from "$env/static/private"; import { z } from "zod"; import type { Assistant } from "$lib/types/Assistant"; +import { logger } from "$lib/server/logger"; async function assistantOnlyIfAuthor(locals: App.Locals, assistantId?: string) { const assistant = await collections.assistants.findOne({ _id: new ObjectId(assistantId) }); @@ -109,7 +110,7 @@ export const actions: Actions = { }); if (!res.ok) { - console.error(`Webhook assistant report failed. ${res.statusText} ${res.text}`); + logger.error(`Webhook assistant report failed. ${res.statusText} ${res.text}`); } } From 6d910182d0c8c4379f797f16f407f2c0b1255b23 Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Tue, 30 Apr 2024 10:49:24 +0200 Subject: [PATCH 09/20] Add the simplest healthcheck route (#1089) --- src/routes/healthcheck/+server.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/routes/healthcheck/+server.ts diff --git a/src/routes/healthcheck/+server.ts b/src/routes/healthcheck/+server.ts new file mode 100644 index 00000000000..edb40a0dd81 --- /dev/null +++ b/src/routes/healthcheck/+server.ts @@ -0,0 +1,3 @@ +export async function GET() { + return new Response("OK", { status: 200 }); +} From 3d2e6215bdd9be8ff4540cabd21d51a6b41eec7e Mon Sep 17 00:00:00 2001 From: Nathan Sarrazin Date: Tue, 30 Apr 2024 11:37:44 +0200 Subject: [PATCH 10/20] Add truncate to task model (#1090) --- .env.template | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.template b/.env.template index a4547e53199..bcb0cb20ca6 100644 --- a/.env.template +++ b/.env.template @@ -231,6 +231,7 @@ MODELS=`[ "parameters": { "temperature": 0.1, "stop": ["<|eot_id|>"], + "truncate": 1024, }, "unlisted": true } From 1b2418908cf02b8c4c3bd9733b10f2183603c821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Mu=C5=A1tar?= Date: Tue, 30 Apr 2024 16:02:28 +0200 Subject: [PATCH 11/20] retry text area height (#1091) --- src/lib/components/chat/ChatMessage.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/ChatMessage.svelte b/src/lib/components/chat/ChatMessage.svelte index a89d4795386..14fbf46c7e5 100644 --- a/src/lib/components/chat/ChatMessage.svelte +++ b/src/lib/components/chat/ChatMessage.svelte @@ -344,7 +344,8 @@ }} >