diff --git a/.dockerignore b/.dockerignore index cd4afbe92f293bdc25787af1b7ba21c86cb2f8cb..b501ad1a9595541c90e0f9c78d98b20406594121 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,4 +8,4 @@ node_modules/ .svelte-kit/ .env* !.env -.env.local \ No newline at end of file +!.env.local \ No newline at end of file diff --git a/.env b/.env index 67993894d94d47fde852a675f860eed3ff092d9e..a3cb298e22c2ef8f4abc5f3ac896605f2aedab34 100644 --- a/.env +++ b/.env @@ -145,7 +145,6 @@ EXPOSE_API=true ENABLE_ASSISTANTS=false #set to true to enable assistants feature ENABLE_ASSISTANTS_RAG=false # /!\ This will let users specify arbitrary URLs that the server will then request. Make sure you have the proper firewall rules in place. -REQUIRE_FEATURED_ASSISTANTS=false ENABLE_LOCAL_FETCH=false #set to true to disable the blocklist for local fetches. Only enable this if you have the proper firewall rules to prevent SSRF attacks and understand the implications. ALTERNATIVE_REDIRECT_URLS=`[]` #valide alternative redirect URL for OAuth WEBHOOK_URL_REPORT_ASSISTANT=#provide webhook url to get notified when an assistant gets reported @@ -153,6 +152,3 @@ 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 -METRICS_PORT= -LOG_LEVEL=info \ No newline at end of file diff --git a/.env.template b/.env.template index bcb0cb20ca64f81fffd4ec66ca0fe3a0616314e3..205c5ea66c01ee4b1e37b247d9a25d406477103e 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" : 28672, - "max_new_tokens" : 4096, + "truncate" : 24576, + "max_new_tokens" : 8192, "temperature" : 0.3 }, "promptExamples" : [ @@ -27,31 +27,6 @@ MODELS=`[ } ] }, - { - "name" : "meta-llama/Meta-Llama-3-70B-Instruct", - "description": "Generation over generation, Meta Llama 3 demonstrates state-of-the-art performance on a wide range of industry benchmarks and offers new capabilities, including improved reasoning.", - "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/meta-logo.png", - "modelUrl": "https://huggingface.co/meta-llama/Meta-Llama-3-70B-Instruct", - "websiteUrl": "https://llama.meta.com/llama3/", - "tokenizer" : "philschmid/meta-llama-3-tokenizer", - "promptExamples" : [ - { - "title": "Write an email from bullet list", - "prompt": "As a restaurant owner, write a professional email to the supplier to get these products every week: \n\n- Wine (x10)\n- Eggs (x24)\n- Bread (x12)" - }, { - "title": "Code a snake game", - "prompt": "Code a basic snake game in python, give explanations for each step." - }, { - "title": "Assist in a task", - "prompt": "How do I make a delicious lemon cheesecake?" - } - ], - "parameters": { - "stop": ["<|eot_id|>"], - "truncate": 6144, - "max_new_tokens": 2047 - } - }, { "name" : "HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1", "tokenizer": "HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1", @@ -76,7 +51,7 @@ MODELS=`[ "prompt": "How do I make a delicious lemon cheesecake?" } ] - }, + }, { "name" : "mistralai/Mixtral-8x7B-Instruct-v0.1", "description" : "The latest MoE model from Mistral AI! 8x7B and outperforms Llama 2 70B in most benchmarks.", @@ -108,6 +83,33 @@ MODELS=`[ } ] }, + { + "name" : "google/gemma-1.1-7b-it", + "description": "Gemma 7B 1.1 is the latest release in the Gemma family of lightweight models built by Google, trained using a novel RLHF method.", + "websiteUrl" : "https://blog.google/technology/developers/gemma-open-models/", + "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/google-logo.png", + "modelUrl": "https://huggingface.co/google/gemma-1.1-7b-it", + "preprompt": "", + "chatPromptTemplate" : "{{#each messages}}{{#ifUser}}user\n{{#if @first}}{{#if @root.preprompt}}{{@root.preprompt}}\n{{/if}}{{/if}}{{content}}\nmodel\n{{/ifUser}}{{#ifAssistant}}{{content}}\n{{/ifAssistant}}{{/each}}", + "promptExamples": [ + { + "title": "Write an email from bullet list", + "prompt": "As a restaurant owner, write a professional email to the supplier to get these products every week: \n\n- Wine (x10)\n- Eggs (x24)\n- Bread (x12)" + }, { + "title": "Code a snake game", + "prompt": "Code a basic snake game in python, give explanations for each step." + }, { + "title": "Assist in a task", + "prompt": "How do I make a delicious lemon cheesecake?" + } + ], + "parameters": { + "do_sample": true, + "truncate": 7168, + "max_new_tokens": 1024, + "stop" : [""] + } + }, { "name" : "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO", "description" : "Nous Hermes 2 Mixtral 8x7B DPO is the new flagship Nous Research model trained over the Mixtral 8x7B MoE LLM.", @@ -138,46 +140,18 @@ MODELS=`[ "stop": ["<|im_end|>"] } }, - { - "name" : "google/gemma-1.1-7b-it", - "description": "Gemma 7B 1.1 is the latest release in the Gemma family of lightweight models built by Google, trained using a novel RLHF method.", - "websiteUrl" : "https://blog.google/technology/developers/gemma-open-models/", - "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/google-logo.png", - "modelUrl": "https://huggingface.co/google/gemma-1.1-7b-it", - "preprompt": "", - "chatPromptTemplate" : "{{#each messages}}{{#ifUser}}user\n{{#if @first}}{{#if @root.preprompt}}{{@root.preprompt}}\n{{/if}}{{/if}}{{content}}\nmodel\n{{/ifUser}}{{#ifAssistant}}{{content}}\n{{/ifAssistant}}{{/each}}", - "promptExamples": [ - { - "title": "Write an email from bullet list", - "prompt": "As a restaurant owner, write a professional email to the supplier to get these products every week: \n\n- Wine (x10)\n- Eggs (x24)\n- Bread (x12)" - }, { - "title": "Code a snake game", - "prompt": "Code a basic snake game in python, give explanations for each step." - }, { - "title": "Assist in a task", - "prompt": "How do I make a delicious lemon cheesecake?" - } - ], - "parameters": { - "do_sample": true, - "truncate": 7168, - "max_new_tokens": 1024, - "stop" : [""] - } - }, - - { - "name": "mistralai/Mistral-7B-Instruct-v0.2", - "displayName": "mistralai/Mistral-7B-Instruct-v0.2", + { + "name": "mistralai/Mistral-7B-Instruct-v0.1", + "displayName": "mistralai/Mistral-7B-Instruct-v0.1", "description": "Mistral 7B is a new Apache 2.0 model, released by Mistral AI that outperforms Llama2 13B in benchmarks.", "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/mistral-logo.png", "websiteUrl": "https://mistral.ai/news/announcing-mistral-7b/", - "modelUrl": "https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2", - "tokenizer": "mistralai/Mistral-7B-Instruct-v0.2", + "modelUrl": "https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.1", + "tokenizer": "mistralai/Mistral-7B-Instruct-v0.1", "preprompt": "", "chatPromptTemplate" : "{{#each messages}}{{#ifUser}}[INST] {{#if @first}}{{#if @root.preprompt}}{{@root.preprompt}}\n{{/if}}{{/if}}{{content}} [/INST]{{/ifUser}}{{#ifAssistant}}{{content}}{{/ifAssistant}}{{/each}}", "parameters": { - "temperature": 0.3, + "temperature": 0.1, "top_p": 0.95, "repetition_penalty": 1.2, "top_k": 50, @@ -196,21 +170,27 @@ MODELS=`[ "title": "Assist in a task", "prompt": "How do I make a delicious lemon cheesecake?" } - ] + ], + "unlisted": true }, - { - "name": "microsoft/Phi-3-mini-4k-instruct", - "tokenizer": "microsoft/Phi-3-mini-4k-instruct", - "description" : "Phi-3 Mini-4K-Instruct is a 3.8B parameters, lightweight, state-of-the-art open model built upon datasets used for Phi-2.", - "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/microsoft-logo.png", - "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/", + { + "name": "mistralai/Mistral-7B-Instruct-v0.2", + "displayName": "mistralai/Mistral-7B-Instruct-v0.2", + "description": "Mistral 7B is a new Apache 2.0 model, released by Mistral AI that outperforms Llama2 13B in benchmarks.", + "logoUrl": "https://huggingface.co/datasets/huggingchat/models-logo/resolve/main/mistral-logo.png", + "websiteUrl": "https://mistral.ai/news/announcing-mistral-7b/", + "modelUrl": "https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2", + "tokenizer": "mistralai/Mistral-7B-Instruct-v0.2", "preprompt": "", - "chatPromptTemplate": "{{preprompt}}{{#each messages}}{{#ifUser}}<|user|>\n{{content}}<|end|>\n<|assistant|>\n{{/ifUser}}{{#ifAssistant}}{{content}}<|end|>\n{{/ifAssistant}}{{/each}}", + "chatPromptTemplate" : "{{#each messages}}{{#ifUser}}[INST] {{#if @first}}{{#if @root.preprompt}}{{@root.preprompt}}\n{{/if}}{{/if}}{{content}} [/INST]{{/ifUser}}{{#ifAssistant}}{{content}}{{/ifAssistant}}{{/each}}", "parameters": { - "stop": ["<|end|>", "<|endoftext|>", "<|assistant|>"], + "temperature": 0.3, + "top_p": 0.95, + "repetition_penalty": 1.2, + "top_k": 50, + "truncate": 3072, "max_new_tokens": 1024, - "truncate": 3071 + "stop": [""] }, "promptExamples": [ { @@ -224,16 +204,6 @@ MODELS=`[ "prompt": "How do I make a delicious lemon cheesecake?" } ] - }, - { - "name": "meta-llama/Meta-Llama-3-8B-Instruct", - "tokenizer" : "philschmid/meta-llama-3-tokenizer", - "parameters": { - "temperature": 0.1, - "stop": ["<|eot_id|>"], - "truncate": 1024, - }, - "unlisted": true } ]` @@ -251,21 +221,7 @@ OLD_MODELS=`[ {"name": "openchat/openchat-3.5-0106"} ]` -TASK_MODEL='meta-llama/Meta-Llama-3-8B-Instruct' - -TEXT_EMBEDDING_MODELS = `[ - { - "name": "bge-base-en-v1-5-sxa", - "displayName": "bge-base-en-v1-5-sxa", - "chunkCharLength": 512, - "endpoints": [ - { "type": "tei", - "url" : "https://huggingchat-tei.hf.space/" - } - ] - } -]` - +TASK_MODEL='mistralai/Mistral-7B-Instruct-v0.1' APP_BASE="/chat" PUBLIC_ORIGIN=https://huggingface.co @@ -288,7 +244,6 @@ PUBLIC_APPLE_APP_ID=6476778843 ENABLE_ASSISTANTS=true ENABLE_ASSISTANTS_RAG=true -REQUIRE_FEATURED_ASSISTANTS=true EXPOSE_API=true ALTERNATIVE_REDIRECT_URLS=`[ diff --git a/.github/release.yml b/.github/release.yml deleted file mode 100644 index 3a183679f1aa435bf266e800f343d91ef355eabd..0000000000000000000000000000000000000000 --- a/.github/release.yml +++ /dev/null @@ -1,16 +0,0 @@ -changelog: - exclude: - labels: - - huggingchat - - CI/CD - - documentation - categories: - - title: Features - labels: - - enhancement - - title: Bugfixes - labels: - - bug - - title: Other changes - labels: - - "*" diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 0238051484f7bf7240aa69bb31fc1e828d2e3c6a..b284c9f6ec3c4bf8e1e29f323756bdf7fa6ac908 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -8,7 +8,7 @@ on: branches: - "*" paths: - - "Dockerfile" + - "Dockerfile.local" - "entrypoint.sh" workflow_dispatch: release: @@ -62,7 +62,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: Dockerfile + file: Dockerfile.local push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -116,7 +116,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: Dockerfile + file: Dockerfile.local push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 5621728539ac6a3a14a8e9a34935e44b551c79d4..7e8c5e8f85a177c66518a4c2995c02ef85a1b449 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,4 @@ SECRET_CONFIG .idea !.env.ci !.env -!.env.template -gcp-*.json \ No newline at end of file +!.env.template \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ca205fbbfb4bad59f38d24611b40d98f30f63cf1..a88846db80e84c0ad4836a444cd18a0370edddb0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,6 @@ # syntax=docker/dockerfile:1 # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker # you will also find guides on how best to write your Dockerfile -ARG INCLUDE_DB=false - -# stage that install the dependencies FROM node:20 as builder-production WORKDIR /app @@ -15,74 +12,21 @@ RUN --mount=type=cache,target=/app/.npm \ FROM builder-production as builder -ARG APP_BASE= -ARG PUBLIC_APP_COLOR=blue - RUN --mount=type=cache,target=/app/.npm \ npm set cache /app/.npm && \ npm ci COPY --link --chown=1000 . . -RUN npm run build - -# mongo image -FROM mongo:latest as mongo - -# image to be used if INCLUDE_DB is false -FROM node:20-slim as local_db_false - -# image to be used if INCLUDE_DB is true -FROM node:20-slim as local_db_true - -RUN apt-get update -RUN apt-get install gnupg curl -y -# copy mongo from the other stage -COPY --from=mongo /usr/bin/mongo* /usr/bin/ - -ENV MONGODB_URL=mongodb://localhost:27017 -RUN mkdir -p /data/db -RUN chown -R 1000:1000 /data/db - -# final image -FROM local_db_${INCLUDE_DB} as final - -# build arg to determine if the database should be included -ARG INCLUDE_DB=false -ENV INCLUDE_DB=${INCLUDE_DB} - -# svelte requires APP_BASE at build time so it must be passed as a build arg -ARG APP_BASE= -# tailwind requires the primary theme to be known at build time so it must be passed as a build arg -ARG PUBLIC_APP_COLOR=blue - - -# install dotenv-cli -RUN npm install -g dotenv-cli - -# switch to a user that works for spaces -RUN userdel -r node -RUN useradd -m -u 1000 user -USER user - -ENV HOME=/home/user \ - PATH=/home/user/.local/bin:$PATH - -WORKDIR /app - -# add a .env.local if the user doesn't bind a volume to it -RUN touch /app/.env.local +RUN --mount=type=secret,id=DOTENV_LOCAL,dst=.env.local \ + npm run build -# get the default config, the entrypoint script and the server script -COPY --chown=1000 package.json /app/package.json -COPY --chown=1000 .env /app/.env -COPY --chown=1000 entrypoint.sh /app/entrypoint.sh -COPY --chown=1000 gcp-*.json /app/ +FROM node:20-slim -#import the build & dependencies -COPY --from=builder --chown=1000 /app/build /app/build -COPY --from=builder --chown=1000 /app/node_modules /app/node_modules +RUN npm install -g pm2 -RUN chmod +x /app/entrypoint.sh +COPY --from=builder-production /app/node_modules /app/node_modules +COPY --link --chown=1000 package.json /app/package.json +COPY --from=builder /app/build /app/build -CMD ["/bin/bash", "-c", "/app/entrypoint.sh"] +CMD pm2 start /app/build/index.js -i $CPU_CORES --no-daemon diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 0000000000000000000000000000000000000000..c6046c3eb92038ba18f5f3d1d6a6fae51e4ae7bc --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,28 @@ +ARG INCLUDE_DB=false +FROM mongo:latest as mongo + +FROM node:20-slim as local_db_false + +FROM node:20-slim as local_db_true + +RUN apt-get update +RUN apt-get install gnupg curl -y + +COPY --from=mongo /usr/bin/mongo* /usr/bin/ + +FROM local_db_${INCLUDE_DB} as final +ARG INCLUDE_DB=false +ENV INCLUDE_DB=${INCLUDE_DB} + +WORKDIR /app + +COPY --link --chown=1000 package-lock.json package.json ./ +RUN --mount=type=cache,target=/app/.npm \ + npm set cache /app/.npm && \ + npm ci + +# copy the rest of the files, run regardless of +COPY --chown=1000 --link . . +RUN chmod +x /app/entrypoint.sh + +CMD ["/bin/bash", "-c", "/app/entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 9676dd8014c73ef6861ebca68d75becfe0698459..636e40a805fffec3316611c52c952be08d07052c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ license: apache-2.0 base_path: /chat app_port: 3000 failure_strategy: rollback -load_balancing_strategy: random --- # Chat UI @@ -24,9 +23,8 @@ 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. [Common issues](#common-issues) -7. [Deploying to a HF Space](#deploying-to-a-hf-space) -8. [Building](#building) +6. [Deploying to a HF Space](#deploying-to-a-hf-space) +7. [Building](#building) ## No Setup Deploy @@ -590,11 +588,11 @@ Chat UI can connect to the google Vertex API endpoints ([List of supported model To enable: -1. [Select](https://console.cloud.google.com/project) or [create](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project) a Google Cloud project. -1. [Enable billing for your project](https://cloud.google.com/billing/docs/how-to/modify-project). -1. [Enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com). -1. [Set up authentication with a service account](https://cloud.google.com/docs/authentication/getting-started) - so you can access the API from your local workstation. +1. [Select](https://console.cloud.google.com/project) or [create](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project) a Google Cloud project. +1. [Enable billing for your project](https://cloud.google.com/billing/docs/how-to/modify-project). +1. [Enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com). +1. [Set up authentication with a service account](https://cloud.google.com/docs/authentication/getting-started) + so you can access the API from your local workstation. The service account credentials file can be imported as an environmental variable: @@ -602,24 +600,18 @@ The service account credentials file can be imported as an environmental variabl GOOGLE_APPLICATION_CREDENTIALS = clientid.json ``` -Make sure your docker container has access to the file and the variable is correctly set. -Afterwards Google Vertex endpoints can be configured as following: +Make sure docker has access to the file. Afterwards Google Vertex endpoints can be configured as following: ``` MODELS=`[ //... { - "name": "gemini-1.5-pro", - "displayName": "Vertex Gemini Pro 1.5", + "name": "gemini-1.0-pro", //model-name + "displayName": "Vertex Gemini Pro 1.0", + "location": "europe-west3", + "apiEndpoint": "", //alternative api endpoint url "endpoints" : [{ - "type": "vertex", - "project": "abc-xyz", - "location": "europe-west3", - "model": "gemini-1.5-pro-preview-0409", // model-name - - // Optional - "safetyThreshold": "BLOCK_MEDIUM_AND_ABOVE", - "apiEndpoint": "", // alternative api endpoint url + "type": "vertex" }] }, ]` @@ -736,14 +728,6 @@ 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. @@ -765,7 +749,7 @@ You can preview the production build with `npm run preview`. The config file for HuggingChat is stored in the `.env.template` file at the root of the repository. It is the single source of truth that is used to generate the actual `.env.local` file using our CI/CD pipeline. See [updateProdEnv](https://github.com/huggingface/chat-ui/blob/cdb33a9583f5339ade724db615347393ef48f5cd/scripts/updateProdEnv.ts) for more details. > [!TIP] -> If you want to make changes to the model config used in production for HuggingChat, you should do so against `.env.template`. +> If you want to make changes to model config for HuggingChat, you should do so against `.env.template`. We currently use the following secrets for deploying HuggingChat in addition to the `.env.template` above: @@ -774,34 +758,21 @@ We currently use the following secrets for deploying HuggingChat in addition to - `OPENID_CONFIG` - `SERPER_API_KEY` -### Running a copy of HuggingChat locally - -If you want to run an exact copy of HuggingChat locally, you will need to do the following first: +They are defined as secrets in the repository. -1. Create an [OAuth App on the hub](https://huggingface.co/settings/applications/new) with `openid profile email` permissions. Make sure to set the callback URL to something like `http://localhost:5173/chat/login/callback` which matches the right path for your local instance. -2. Create a [HF Token](https://huggingface.co/settings/tokens) with your Hugging Face account. You will need a Pro account to be able to access some of the larger models available through HuggingChat. -3. Create a free account with [serper.dev](https://serper.dev/) (you will get 2500 free search queries) -4. Run an instance of mongoDB, however you want. (Local or remote) +### Testing config changes locally -You can then create a new `.env.SECRET_CONFIG` file with the following content +You can test the config changes locally by first creating an `.env.SECRET_CONFIG` file with the secrets defined above. Then you can run the following command to generate the `.env.local` file: -```env -MONGODB_URL= -HF_TOKEN= -OPENID_CONFIG=`{ - PROVIDER_URL: "https://huggingface.co", - CLIENT_ID: "", - CLIENT_SECRET: "", -}` -SERPER_API_KEY= -MESSAGES_BEFORE_LOGIN= +```bash +npm run updateLocalEnv ``` -You can then run `npm run updateLocalEnv` in the root of chat-ui. This will create a `.env.local` file which combines the `.env.template` and the `.env.SECRET_CONFIG` file. You can then run `npm run dev` to start your local instance of HuggingChat. +This will replace your `.env.local` file with the one that will be used in prod (simply taking `.env.template + .env.SECRET_CONFIG`). ### Populate database -> [!WARNING] +> [!WARNING] > The `MONGODB_URL` used for this script will be fetched from `.env.local`. Make sure it's correct! The command runs directly on the database. You can populate the database using faker data using the `populate` script: diff --git a/entrypoint.sh b/entrypoint.sh index 3fc46ee53aa072971739b5a251011b6327f53c32..c9a9f41fe49cf23a956fb1ee606b0abf3e4c14e3 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,7 +2,7 @@ ENV_LOCAL_PATH=/app/.env.local if test -z "${DOTENV_LOCAL}" ; then if ! test -f "${ENV_LOCAL_PATH}" ; then - echo "DOTENV_LOCAL was not found in the ENV variables and .env.local is not set using a bind volume. Make sure to set environment variables properly. " + echo "DOTENV_LOCAL was not found in the ENV variables and .env.local is not set using a bind volume. We are using the default .env config." fi; else echo "DOTENV_LOCAL was found in the ENV variables. Creating .env.local file." @@ -10,10 +10,20 @@ else fi; if [ "$INCLUDE_DB" = "true" ] ; then + echo "INCLUDE_DB is set to true." + + MONGODB_CONFIG="MONGODB_URL=mongodb://localhost:27017" + if ! grep -q "^${MONGODB_CONFIG}$" ${ENV_LOCAL_PATH}; then + echo "Appending MONGODB_URL" + touch /app/.env.local + echo -e "\n${MONGODB_CONFIG}" >> ${ENV_LOCAL_PATH} + fi + + mkdir -p /data/db + mongod & echo "Starting local MongoDB instance" - nohup mongod & -fi; -export PUBLIC_VERSION=$(node -p "require('./package.json').version") +fi; -dotenv -e /app/.env -c -- node /app/build/index.js -- --host 0.0.0.0 --port 3000 \ No newline at end of file +npm run build +npm run preview -- --host 0.0.0.0 --port 3000 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8a683fc33b3f54b9222ba04a5db987eb79f3f768..b37465117d5a740b3fe3b482e63249af25f9653e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chat-ui", - "version": "0.8.3", + "version": "0.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chat-ui", - "version": "0.8.3", + "version": "0.8.2", "dependencies": { "@huggingface/hub": "^0.5.1", "@huggingface/inference": "^2.6.3", @@ -17,7 +17,6 @@ "browser-image-resizer": "^2.4.1", "date-fns": "^2.29.3", "dotenv": "^16.0.3", - "express": "^4.19.2", "handlebars": "^4.7.8", "highlight.js": "^11.7.0", "image-size": "^1.0.2", @@ -30,8 +29,6 @@ "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", @@ -50,7 +47,6 @@ "@sveltejs/adapter-node": "^1.3.1", "@sveltejs/kit": "^1.30.4", "@tailwindcss/typography": "^0.5.9", - "@types/express": "^4.17.21", "@types/jsdom": "^21.1.1", "@types/minimist": "^1.2.5", "@types/parquetjs": "^0.10.3", @@ -64,7 +60,6 @@ "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.10.1", "prettier-plugin-tailwindcss": "^0.2.7", - "prom-client": "^15.1.2", "svelte": "^4.2.8", "svelte-check": "^3.6.2", "ts-node": "^10.9.1", @@ -77,7 +72,7 @@ }, "optionalDependencies": { "@anthropic-ai/sdk": "^0.17.1", - "@google-cloud/vertexai": "^1.1.0", + "@google-cloud/vertexai": "^0.5.0", "aws4fetch": "^1.0.17", "cohere-ai": "^7.9.0", "openai": "^4.14.2" @@ -635,9 +630,9 @@ } }, "node_modules/@google-cloud/vertexai": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.1.0.tgz", - "integrity": "sha512-hfwfdlVpJ+kM6o2b5UFfPnweBcz8tgHAFRswnqUKYqLJsvKU0DDD0Z2/YKoHyAUoPJAv20qg6KlC3msNeUKUiw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-0.5.0.tgz", + "integrity": "sha512-qIFHYTXA5UCLdm9JG+Xf1suomCXxRqa1PKdYjqXuhZsCm8mn37Rb0Tf8djlhDzuRVWyWoQTmsWpsk28ZTmbqJg==", "optional": true, "dependencies": { "google-auth-library": "^9.1.0" @@ -1266,15 +1261,6 @@ "node": ">= 8" } }, - "node_modules/@opentelemetry/api": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", - "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -1999,16 +1985,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, "node_modules/@types/chai": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", @@ -2024,15 +2000,6 @@ "@types/chai": "*" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/cookie": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz", @@ -2045,36 +2012,6 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", - "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, "node_modules/@types/jsdom": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.1.tgz", @@ -2102,12 +2039,6 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, "node_modules/@types/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", @@ -2153,18 +2084,6 @@ "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", "dev": true }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -2177,27 +2096,6 @@ "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "dev": true }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -2553,6 +2451,7 @@ "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" }, @@ -2560,18 +2459,6 @@ "node": ">=6.5" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -2702,11 +2589,6 @@ "dequal": "^2.0.3" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -2730,14 +2612,6 @@ "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", @@ -2843,12 +2717,6 @@ "integrity": "sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g==", "optional": true }, - "node_modules/bintrees": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", - "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", - "dev": true - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2865,67 +2733,6 @@ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", "dev": true }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3058,14 +2865,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -3079,6 +2878,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "optional": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3303,11 +3103,6 @@ "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", @@ -3357,25 +3152,6 @@ "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -3385,11 +3161,6 @@ "node": ">= 0.6" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -3542,14 +3313,6 @@ "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", @@ -3624,6 +3387,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "optional": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3644,14 +3408,6 @@ "node": ">=0.4.0" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3661,15 +3417,6 @@ "node": ">=6" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -3774,11 +3521,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, "node_modules/electron-to-chromium": { "version": "1.4.359", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz", @@ -3789,14 +3531,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3820,6 +3554,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "optional": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -3831,6 +3566,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "optional": true, "engines": { "node": ">= 0.4" } @@ -4120,30 +3856,15 @@ "node": ">=0.10.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/event-target-shim": { "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", @@ -4175,93 +3896,12 @@ "node": ">=6" } }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "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", @@ -4317,19 +3957,6 @@ "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", @@ -4366,36 +3993,6 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4468,14 +4065,6 @@ "node": ">= 12.20" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4488,14 +4077,6 @@ "url": "https://www.patreon.com/infusion" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -4593,6 +4174,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "optional": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -4723,6 +4305,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "optional": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -4799,6 +4382,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "optional": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -4810,6 +4394,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "optional": true, "engines": { "node": ">= 0.4" }, @@ -4821,6 +4406,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "optional": true, "engines": { "node": ">= 0.4" }, @@ -4844,11 +4430,6 @@ "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", @@ -4879,21 +4460,6 @@ "node": ">=12" } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -5051,14 +4617,6 @@ "node": ">= 12" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -5197,14 +4755,6 @@ "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", @@ -5621,24 +5171,11 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5653,14 +5190,6 @@ "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -5673,17 +5202,6 @@ "node": ">=8.6" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -5890,14 +5408,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -6055,6 +5565,7 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6075,25 +5586,6 @@ "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/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6320,14 +5812,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6359,11 +5843,6 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6441,141 +5920,6 @@ "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", @@ -6969,32 +6313,6 @@ "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/prom-client": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.2.tgz", - "integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.4.0", - "tdigest": "^0.1.1" - }, - "engines": { - "node": "^16 || ^18 || >=20" - } - }, "node_modules/protobufjs": { "version": "6.11.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", @@ -7020,18 +6338,6 @@ "pbts": "bin/pbts" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -7115,44 +6421,6 @@ "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/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -7213,14 +6481,6 @@ "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", @@ -7349,14 +6609,6 @@ } ] }, - "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", @@ -7436,11 +6688,6 @@ "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", @@ -7455,47 +6702,6 @@ "node": ">=10" } }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serpapi": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/serpapi/-/serpapi-1.1.1.tgz", @@ -7504,20 +6710,6 @@ "undici": "^5.12.0" } }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/set-cookie-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", @@ -7528,6 +6720,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "optional": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7540,11 +6733,6 @@ "node": ">= 0.4" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "node_modules/sharp": { "version": "0.33.2", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz", @@ -7609,6 +6797,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "optional": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -7735,14 +6924,6 @@ "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", @@ -7782,14 +6963,6 @@ "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", @@ -7801,14 +6974,6 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/std-env": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", @@ -7874,6 +7039,7 @@ "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" }, @@ -8280,15 +7446,6 @@ "streamx": "^2.15.0" } }, - "node_modules/tdigest": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", - "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", - "dev": true, - "dependencies": { - "bintrees": "1.0.2" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8314,14 +7471,6 @@ "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", @@ -8394,14 +7543,6 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/totalist": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz", @@ -8552,18 +7693,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -8628,14 +7757,6 @@ "node": ">= 4.0.0" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/unplugin": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.3.1.tgz", @@ -8740,14 +7861,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -8771,14 +7884,6 @@ "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/vite": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", diff --git a/package.json b/package.json index ef1d419c0c8766e0377cc25f14f27bf31fb85fc7..4906292943a7b38eb7d95ec916f245115a82a34f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chat-ui", - "version": "0.8.3", + "version": "0.8.2", "private": true, "packageManager": "npm@9.5.0", "scripts": { @@ -23,7 +23,6 @@ "@sveltejs/adapter-node": "^1.3.1", "@sveltejs/kit": "^1.30.4", "@tailwindcss/typography": "^0.5.9", - "@types/express": "^4.17.21", "@types/jsdom": "^21.1.1", "@types/minimist": "^1.2.5", "@types/parquetjs": "^0.10.3", @@ -37,7 +36,6 @@ "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.10.1", "prettier-plugin-tailwindcss": "^0.2.7", - "prom-client": "^15.1.2", "svelte": "^4.2.8", "svelte-check": "^3.6.2", "ts-node": "^10.9.1", @@ -59,7 +57,6 @@ "browser-image-resizer": "^2.4.1", "date-fns": "^2.29.3", "dotenv": "^16.0.3", - "express": "^4.19.2", "handlebars": "^4.7.8", "highlight.js": "^11.7.0", "image-size": "^1.0.2", @@ -72,8 +69,6 @@ "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", @@ -87,7 +82,7 @@ }, "optionalDependencies": { "@anthropic-ai/sdk": "^0.17.1", - "@google-cloud/vertexai": "^1.1.0", + "@google-cloud/vertexai": "^0.5.0", "aws4fetch": "^1.0.17", "cohere-ai": "^7.9.0", "openai": "^4.14.2" diff --git a/scripts/populate.ts b/scripts/populate.ts index 8e06879a634d7f40010606ff49e3010967efb4b9..2a3e12a541ec82585cd17335b21e0b0ec5924a16 100644 --- a/scripts/populate.ts +++ b/scripts/populate.ts @@ -2,13 +2,12 @@ import readline from "readline"; import minimist from "minimist"; // @ts-expect-error: vite-node makes the var available but the typescript compiler doesn't see them -import { env } from "$env/dynamic/private"; +import { MONGODB_URL } from "$env/static/private"; import { faker } from "@faker-js/faker"; import { ObjectId } from "mongodb"; -// @ts-expect-error: vite-node makes the var available but the typescript compiler doesn't see them -import { collections } from "$lib/server/database"; +import { collections } from "../src/lib/server/database.ts"; import { models } from "../src/lib/server/models.ts"; import type { User } from "../src/lib/types/User"; import type { Assistant } from "../src/lib/types/Assistant"; @@ -18,7 +17,6 @@ import { defaultEmbeddingModel } from "../src/lib/server/embeddingModels.ts"; import { Message } from "../src/lib/types/Message.ts"; import { addChildren } from "../src/lib/utils/tree/addChildren.ts"; -import { generateSearchTokens } from "../src/lib/utils/searchTokens.ts"; const rl = readline.createInterface({ input: process.stdin, @@ -160,11 +158,10 @@ async function seed() { console.log("Creating assistants for all users"); await Promise.all( users.map(async (user) => { - const name = faker.animal.insect(); const assistants = faker.helpers.multiple( () => ({ _id: new ObjectId(), - name, + name: faker.animal.insect(), createdById: user._id, createdByName: user.username, createdAt: faker.date.recent({ days: 30 }), @@ -177,8 +174,6 @@ async function seed() { exampleInputs: faker.helpers.multiple(() => faker.lorem.sentence(), { count: faker.number.int({ min: 0, max: 4 }), }), - searchTokens: generateSearchTokens(name), - last24HoursCount: faker.number.int({ min: 0, max: 1000 }), }), { count: faker.number.int({ min: 3, max: 10 }) } ); @@ -246,7 +241,7 @@ async function seed() { try { rl.question( "You're about to run a seeding script on the following MONGODB_URL: \x1b[31m" + - env.MONGODB_URL + + MONGODB_URL + "\x1b[0m\n\n With the following flags: \x1b[31m" + flags.join("\x1b[0m , \x1b[31m") + "\x1b[0m\n \n\n Are you sure you want to continue? (yes/no): ", diff --git a/src/app.d.ts b/src/app.d.ts index 681fd3654336a2192095c33677be26f4d95436e3..dfd942be68dce05eda0ca563062e867c1230d866 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -12,11 +12,6 @@ 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 ff4f044eca4655e1bbeee64916464b437cadbdec..d749c38e531ea0f2419a9a297f28cf9fb136072f 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,6 +1,17 @@ -import { env } from "$env/dynamic/private"; -import { env as envPublic } from "$env/dynamic/public"; -import type { Handle, HandleServerError } from "@sveltejs/kit"; +import { + ADMIN_API_SECRET, + COOKIE_NAME, + ENABLE_ASSISTANTS, + EXPOSE_API, + MESSAGES_BEFORE_LOGIN, + PARQUET_EXPORT_SECRET, +} from "$env/static/private"; +import type { Handle } from "@sveltejs/kit"; +import { + PUBLIC_GOOGLE_ANALYTICS_ID, + PUBLIC_ORIGIN, + PUBLIC_APP_DISCLAIMER, +} from "$env/static/public"; import { collections } from "$lib/server/database"; import { base } from "$app/paths"; import { findUser, refreshSessionCookie, requiresUser } from "$lib/server/auth"; @@ -10,63 +21,16 @@ 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"; -import { AbortedGenerations } from "$lib/server/abortedGenerations"; -import { MetricsServer } from "$lib/server/metrics"; -// TODO: move this code on a started server hook, instead of using a "building" flag if (!building) { await checkAndRunMigrations(); - if (env.ENABLE_ASSISTANTS) { + if (ENABLE_ASSISTANTS) { refreshAssistantsCounts(); } - - // Init metrics server - MetricsServer.getInstance(); - - // Init AbortedGenerations refresh process - AbortedGenerations.getInstance(); } -export const handleError: HandleServerError = async ({ error, event }) => { - // handle 404 - - if (building) { - throw error; - } - - 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 }) => { - logger.debug({ - locals: event.locals, - url: event.url.pathname, - params: event.params, - request: event.request, - }); - - if (event.url.pathname.startsWith(`${base}/api/`) && env.EXPOSE_API !== "true") { + if (event.url.pathname.startsWith(`${base}/api/`) && EXPOSE_API !== "true") { return new Response("API is disabled", { status: 403 }); } @@ -83,7 +47,7 @@ export const handle: Handle = async ({ event, resolve }) => { } if (event.url.pathname.startsWith(`${base}/admin/`) || event.url.pathname === `${base}/admin`) { - const ADMIN_SECRET = env.ADMIN_API_SECRET || env.PARQUET_EXPORT_SECRET; + const ADMIN_SECRET = ADMIN_API_SECRET || PARQUET_EXPORT_SECRET; if (!ADMIN_SECRET) { return errorResponse(500, "Admin API is not configured"); @@ -94,7 +58,7 @@ export const handle: Handle = async ({ event, resolve }) => { } } - const token = event.cookies.get(env.COOKIE_NAME); + const token = event.cookies.get(COOKIE_NAME); let secretSessionId: string; let sessionId: string; @@ -133,18 +97,18 @@ export const handle: Handle = async ({ event, resolve }) => { refreshSessionCookie(event.cookies, event.locals.sessionId); if (nativeFormContentTypes.includes(requestContentType)) { - const origin = event.request.headers.get("origin"); + const referer = event.request.headers.get("referer"); - if (!origin) { - return errorResponse(403, "Non-JSON form requests need to have an origin"); + if (!referer) { + return errorResponse(403, "Non-JSON form requests need to have a referer"); } const validOrigins = [ - new URL(event.request.url).host, - ...(envPublic.PUBLIC_ORIGIN ? [new URL(envPublic.PUBLIC_ORIGIN).host] : []), + new URL(event.request.url).origin, + ...(PUBLIC_ORIGIN ? [new URL(PUBLIC_ORIGIN).origin] : []), ]; - if (!validOrigins.includes(new URL(origin).host)) { + if (!validOrigins.includes(new URL(referer).origin)) { return errorResponse(403, "Invalid referer for POST request"); } } @@ -168,7 +132,7 @@ export const handle: Handle = async ({ event, resolve }) => { if ( !event.locals.user && requiresUser && - !((env.MESSAGES_BEFORE_LOGIN ? parseInt(env.MESSAGES_BEFORE_LOGIN) : 0) > 0) + !((MESSAGES_BEFORE_LOGIN ? parseInt(MESSAGES_BEFORE_LOGIN) : 0) > 0) ) { return errorResponse(401, ERROR_MESSAGES.authOnly); } @@ -179,7 +143,7 @@ export const handle: Handle = async ({ event, resolve }) => { if ( !requiresUser && !event.url.pathname.startsWith(`${base}/settings`) && - !!envPublic.PUBLIC_APP_DISCLAIMER + !!PUBLIC_APP_DISCLAIMER ) { const hasAcceptedEthicsModal = await collections.settings.countDocuments({ sessionId: event.locals.sessionId, @@ -202,7 +166,7 @@ export const handle: Handle = async ({ event, resolve }) => { } replaced = true; - return chunk.html.replace("%gaId%", envPublic.PUBLIC_GOOGLE_ANALYTICS_ID); + return chunk.html.replace("%gaId%", PUBLIC_GOOGLE_ANALYTICS_ID); }, }); diff --git a/src/lib/assistantStats/refresh-assistants-counts.ts b/src/lib/assistantStats/refresh-assistants-counts.ts index a5bb5f525964b5b0c0779e3f4ef3486adc529d9d..4376202638ca52a37778f7510c18a4dd866f7248 100644 --- a/src/lib/assistantStats/refresh-assistants-counts.ts +++ b/src/lib/assistantStats/refresh-assistants-counts.ts @@ -1,8 +1,7 @@ -import { Database } from "$lib/server/database"; +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"; @@ -15,52 +14,47 @@ async function refreshAssistantsCountsHelper() { } try { - await Database.getInstance() - .getClient() - .withSession((session) => - session.withTransaction(async () => { - await Database.getInstance() - .getCollections() - .assistants.aggregate([ - { $project: { _id: 1 } }, - { $set: { last24HoursCount: 0 } }, - { - $unionWith: { - coll: "assistants.stats", - pipeline: [ - { - $match: { "date.at": { $gte: subDays(new Date(), 1) }, "date.span": "hour" }, + await client.withSession((session) => + session.withTransaction(async () => { + await collections.assistants + .aggregate([ + { $project: { _id: 1 } }, + { $set: { last24HoursCount: 0 } }, + { + $unionWith: { + coll: "assistants.stats", + pipeline: [ + { $match: { "date.at": { $gte: subDays(new Date(), 1) }, "date.span": "hour" } }, + { + $group: { + _id: "$assistantId", + last24HoursCount: { $sum: "$count" }, }, - { - $group: { - _id: "$assistantId", - last24HoursCount: { $sum: "$count" }, - }, - }, - ], - }, + }, + ], }, - { - $group: { - _id: "$_id", - last24HoursCount: { $sum: "$last24HoursCount" }, - }, + }, + { + $group: { + _id: "$_id", + last24HoursCount: { $sum: "$last24HoursCount" }, }, - { - $merge: { - into: "assistants", - on: "_id", - whenMatched: "merge", - whenNotMatched: "discard", - }, + }, + { + $merge: { + into: "assistants", + on: "_id", + whenMatched: "merge", + whenNotMatched: "discard", }, - ]) - .next(); - }) - ); + }, + ]) + .next(); + }) + ); } catch (e) { - logger.error("Refresh assistants counter failed!"); - logger.error(e); + console.log("Refresh assistants counter failed!"); + console.error(e); } } diff --git a/src/lib/components/AnnouncementBanner.svelte b/src/lib/components/AnnouncementBanner.svelte index 47ec7b02ef248faab47d38c37dd6260c625886b0..7d6948a6b387ef45104b9b08294ac1e5920e607b 100644 --- a/src/lib/components/AnnouncementBanner.svelte +++ b/src/lib/components/AnnouncementBanner.svelte @@ -5,7 +5,7 @@
New {title} diff --git a/src/lib/components/CopyToClipBoardBtn.svelte b/src/lib/components/CopyToClipBoardBtn.svelte index bafb71c9a580859878c53d06eab5c76eb3fe3e65..53f4c122c66928e2e22c17b3cc03f33433d4363f 100644 --- a/src/lib/components/CopyToClipBoardBtn.svelte +++ b/src/lib/components/CopyToClipBoardBtn.svelte @@ -43,7 +43,7 @@ >
- + diff --git a/src/lib/components/DisclaimerModal.svelte b/src/lib/components/DisclaimerModal.svelte index f0a18abd1dd2fd536a05ddc9eafa9e8c5d3cd435..590bb088b3cb3e0f2bf40a18279cd7d3949319b8 100644 --- a/src/lib/components/DisclaimerModal.svelte +++ b/src/lib/components/DisclaimerModal.svelte @@ -1,7 +1,11 @@
- + - {envPublic.PUBLIC_APP_NAME} + {PUBLIC_APP_NAME} Settings - {#if envPublic.PUBLIC_APP_NAME === "HuggingChat"} + {#if PUBLIC_APP_NAME === "HuggingChat"} 0 || assistant?.dynamicPrompt; - const prefix = - envPublic.PUBLIC_SHARE_PREFIX || `${envPublic.PUBLIC_ORIGIN || $page.url.origin}${base}`; + const prefix = PUBLIC_SHARE_PREFIX || `${PUBLIC_ORIGIN || $page.url.origin}${base}`; $: shareUrl = `${prefix}/assistant/${assistant?._id}`; diff --git a/src/lib/components/chat/ChatIntroduction.svelte b/src/lib/components/chat/ChatIntroduction.svelte index 81fb51b09faf012417377ac77f379e6db72e701d..eb0a11b0537d947d411618437e64c900e99b981b 100644 --- a/src/lib/components/chat/ChatIntroduction.svelte +++ b/src/lib/components/chat/ChatIntroduction.svelte @@ -1,5 +1,7 @@ -{#if envPublic.PUBLIC_APP_ASSETS === "chatui"} +{#if PUBLIC_APP_ASSETS === "chatui"} {/if} diff --git a/src/lib/migrations/migrations.ts b/src/lib/migrations/migrations.ts index 2331644d8b8bd9cdd14c6f7c2827071274941374..2615f170b5f201e78ea3ec32b0cf41048eb258a5 100644 --- a/src/lib/migrations/migrations.ts +++ b/src/lib/migrations/migrations.ts @@ -1,8 +1,7 @@ -import { Database } from "$lib/server/database"; +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"; @@ -13,21 +12,18 @@ export async function checkAndRunMigrations() { } // check if all migrations have already been run - const migrationResults = await Database.getInstance() - .getCollections() - .migrationResults.find() - .toArray(); + const migrationResults = await collections.migrationResults.find().toArray(); - logger.info("[MIGRATIONS] Begin check..."); + console.log("[MIGRATIONS] Begin check..."); // connect to the database - const connectedClient = await Database.getInstance().getClient().connect(); + const connectedClient = await client.connect(); const lockId = await acquireLock(LOCK_KEY); if (!lockId) { // another instance already has the lock, so we exit early - logger.info( + console.log( "[MIGRATIONS] Another instance already has the lock. Waiting for DB to be unlocked." ); @@ -54,69 +50,65 @@ export async function checkAndRunMigrations() { // check if the migration has already been applied if (!shouldRun) { - logger.info(`[MIGRATIONS] "${migration.name}" already applied. Skipping...`); + console.log(`[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) ) { - logger.info( + console.log( `[MIGRATIONS] "${migration.name}" should not be applied for this run. Skipping...` ); continue; } // otherwise all is good and we can run the migration - logger.info( + console.log( `[MIGRATIONS] "${migration.name}" ${ migration.runEveryTime ? "should run every time" : "not applied yet" }. Applying...` ); - await Database.getInstance() - .getCollections() - .migrationResults.updateOne( - { _id: migration._id }, - { - $set: { - name: migration.name, - status: "ongoing", - }, + await collections.migrationResults.updateOne( + { _id: migration._id }, + { + $set: { + name: migration.name, + status: "ongoing", }, - { upsert: true } - ); + }, + { upsert: true } + ); const session = connectedClient.startSession(); let result = false; try { await session.withTransaction(async () => { - result = await migration.up(Database.getInstance()); + result = await migration.up(connectedClient); }); } catch (e) { - logger.info(`[MIGRATIONS] "${migration.name}" failed!`); - logger.error(e); + console.log(`[MIGRATION[] "${migration.name}" failed!`); + console.error(e); } finally { await session.endSession(); } - await Database.getInstance() - .getCollections() - .migrationResults.updateOne( - { _id: migration._id }, - { - $set: { - name: migration.name, - status: result ? "success" : "failure", - }, + await collections.migrationResults.updateOne( + { _id: migration._id }, + { + $set: { + name: migration.name, + status: result ? "success" : "failure", }, - { upsert: true } - ); + }, + { upsert: true } + ); } } - logger.info("[MIGRATIONS] All migrations applied. Releasing lock"); + console.log("[MIGRATIONS] All migrations applied. Releasing lock"); clearInterval(refreshInterval); await releaseLock(LOCK_KEY, lockId); diff --git a/src/lib/migrations/routines/01-update-search-assistants.ts b/src/lib/migrations/routines/01-update-search-assistants.ts index 52c8b2f6c99a5e9d349690271c4e28761e351b53..9f12b27d3fbf7060afcf76b094211773fbde55c2 100644 --- a/src/lib/migrations/routines/01-update-search-assistants.ts +++ b/src/lib/migrations/routines/01-update-search-assistants.ts @@ -1,5 +1,5 @@ import type { Migration } from "."; -import { collections } from "$lib/server/database"; +import { getCollections } from "$lib/server/database"; import { ObjectId, type AnyBulkWriteOperation } from "mongodb"; import type { Assistant } from "$lib/types/Assistant"; import { generateSearchTokens } from "$lib/utils/searchTokens"; @@ -7,8 +7,8 @@ import { generateSearchTokens } from "$lib/utils/searchTokens"; const migration: Migration = { _id: new ObjectId("5f9f3e3e3e3e3e3e3e3e3e3e"), name: "Update search assistants", - up: async () => { - const { assistants } = collections; + up: async (client) => { + const { assistants } = getCollections(client); let ops: AnyBulkWriteOperation[] = []; for await (const assistant of assistants @@ -40,8 +40,8 @@ const migration: Migration = { return true; }, - down: async () => { - const { assistants } = collections; + down: async (client) => { + const { assistants } = getCollections(client); await assistants.updateMany({}, { $unset: { searchTokens: "" } }); return true; }, diff --git a/src/lib/migrations/routines/02-update-assistants-models.ts b/src/lib/migrations/routines/02-update-assistants-models.ts index f7f0c9dd4543761e69efc55de923e2a284306c72..73655a88f84c711175f07bd5f749d7a3e375375a 100644 --- a/src/lib/migrations/routines/02-update-assistants-models.ts +++ b/src/lib/migrations/routines/02-update-assistants-models.ts @@ -1,14 +1,14 @@ import type { Migration } from "."; -import { collections } from "$lib/server/database"; +import { getCollections } from "$lib/server/database"; import { ObjectId } from "mongodb"; const updateAssistantsModels: Migration = { _id: new ObjectId("5f9f3f3f3f3f3f3f3f3f3f3f"), name: "Update deprecated models in assistants with the default model", - up: async () => { + up: async (client) => { const models = (await import("$lib/server/models")).models; - const { assistants } = collections; + const { assistants } = getCollections(client); const modelIds = models.map((el) => el.id); // string[] const defaultModelId = models[0].id; diff --git a/src/lib/migrations/routines/index.ts b/src/lib/migrations/routines/index.ts index 0d6eafa8f04e86e7bba3ba9462774d2c25589600..96a6a07ab3c70a6d1211f70d501f5667a655fc4e 100644 --- a/src/lib/migrations/routines/index.ts +++ b/src/lib/migrations/routines/index.ts @@ -1,14 +1,13 @@ -import type { ObjectId } from "mongodb"; +import type { MongoClient, ObjectId } from "mongodb"; import updateSearchAssistant from "./01-update-search-assistants"; import updateAssistantsModels from "./02-update-assistants-models"; -import type { Database } from "$lib/server/database"; export interface Migration { _id: ObjectId; name: string; - up: (client: Database) => Promise; - down?: (client: Database) => Promise; + up: (client: MongoClient) => Promise; + down?: (client: MongoClient) => Promise; runForFreshInstall?: "only" | "never"; // leave unspecified to run for both runForHuggingChat?: "only" | "never"; // leave unspecified to run for both runEveryTime?: boolean; diff --git a/src/lib/server/abortedGenerations.ts b/src/lib/server/abortedGenerations.ts index 548809d6b6c839524ff08e5fbcceb6d2adb2ae01..575cf637bfef812c40905e35570ba3ca1a31b241 100644 --- a/src/lib/server/abortedGenerations.ts +++ b/src/lib/server/abortedGenerations.ts @@ -1,42 +1,29 @@ // Shouldn't be needed if we dove into sveltekit internals, see https://github.com/huggingface/chat-ui/pull/88#issuecomment-1523173850 -import { logger } from "$lib/server/logger"; -import { collections } from "$lib/server/database"; +import { setTimeout } from "node:timers/promises"; +import { collections } from "./database"; -export class AbortedGenerations { - private static instance: AbortedGenerations; +let closed = false; +process.on("SIGINT", () => { + closed = true; +}); - private abortedGenerations: Map = new Map(); +export let abortedGenerations: Map = new Map(); - private constructor() { - const interval = setInterval(this.updateList, 1000); +async function maintainAbortedGenerations() { + while (!closed) { + await setTimeout(1000); - process.on("SIGINT", () => { - clearInterval(interval); - }); - } - - public static getInstance(): AbortedGenerations { - if (!AbortedGenerations.instance) { - AbortedGenerations.instance = new AbortedGenerations(); - } - - return AbortedGenerations.instance; - } - - public getList(): Map { - return this.abortedGenerations; - } - - private async updateList() { try { const aborts = await collections.abortedGenerations.find({}).sort({ createdAt: 1 }).toArray(); - this.abortedGenerations = new Map( + abortedGenerations = new Map( aborts.map(({ conversationId, createdAt }) => [conversationId.toString(), createdAt]) ); } catch (err) { - logger.error(err); + console.error(err); } } } + +maintainAbortedGenerations(); diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 94eacdb476b5f2c21cd667d41f4ab92b9332e680..96e6ec8f345c164526c7d64444266b7989e834f0 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -1,13 +1,22 @@ import { Issuer, BaseClient, type UserinfoResponse, TokenSet, custom } from "openid-client"; import { addHours, addWeeks } from "date-fns"; -import { env } from "$env/dynamic/private"; +import { + COOKIE_NAME, + OPENID_CLIENT_ID, + OPENID_CLIENT_SECRET, + OPENID_PROVIDER_URL, + OPENID_SCOPES, + OPENID_NAME_CLAIM, + OPENID_TOLERANCE, + OPENID_RESOURCE, + OPENID_CONFIG, +} from "$env/static/private"; import { sha256 } from "$lib/utils/sha256"; import { z } from "zod"; import { dev } from "$app/environment"; import type { Cookies } from "@sveltejs/kit"; -import { collections } from "$lib/server/database"; +import { collections } from "./database"; import JSON5 from "json5"; -import { logger } from "$lib/server/logger"; export interface OIDCSettings { redirectURI: string; @@ -26,27 +35,27 @@ const stringWithDefault = (value: string) => export const OIDConfig = z .object({ - CLIENT_ID: stringWithDefault(env.OPENID_CLIENT_ID), - CLIENT_SECRET: stringWithDefault(env.OPENID_CLIENT_SECRET), - PROVIDER_URL: stringWithDefault(env.OPENID_PROVIDER_URL), - SCOPES: stringWithDefault(env.OPENID_SCOPES), - NAME_CLAIM: stringWithDefault(env.OPENID_NAME_CLAIM).refine( + CLIENT_ID: stringWithDefault(OPENID_CLIENT_ID), + CLIENT_SECRET: stringWithDefault(OPENID_CLIENT_SECRET), + PROVIDER_URL: stringWithDefault(OPENID_PROVIDER_URL), + SCOPES: stringWithDefault(OPENID_SCOPES), + NAME_CLAIM: stringWithDefault(OPENID_NAME_CLAIM).refine( (el) => !["preferred_username", "email", "picture", "sub"].includes(el), { message: "nameClaim cannot be one of the restricted keys." } ), - TOLERANCE: stringWithDefault(env.OPENID_TOLERANCE), - RESOURCE: stringWithDefault(env.OPENID_RESOURCE), + TOLERANCE: stringWithDefault(OPENID_TOLERANCE), + RESOURCE: stringWithDefault(OPENID_RESOURCE), }) - .parse(JSON5.parse(env.OPENID_CONFIG)); + .parse(JSON5.parse(OPENID_CONFIG)); export const requiresUser = !!OIDConfig.CLIENT_ID && !!OIDConfig.CLIENT_SECRET; export function refreshSessionCookie(cookies: Cookies, sessionId: string) { - cookies.set(env.COOKIE_NAME, sessionId, { + cookies.set(COOKIE_NAME, sessionId, { path: "/", // So that it works inside the space's iframe - sameSite: dev || env.ALLOW_INSECURE_COOKIES === "true" ? "lax" : "none", - secure: !dev && !(env.ALLOW_INSECURE_COOKIES === "true"), + sameSite: dev ? "lax" : "none", + secure: !dev, httpOnly: true, expires: addWeeks(new Date(), 2), }); @@ -141,7 +150,7 @@ export async function validateAndParseCsrfToken( return { redirectUrl: data.redirectUrl }; } } catch (e) { - logger.error(e); + console.error(e); } return null; } diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index 4a8302ce9ca119fcb8217f798896286cf65079bc..fa6d2b8f0ff099e8ecdb814737d6191068933cb5 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -1,4 +1,4 @@ -import { env } from "$env/dynamic/private"; +import { MONGODB_URL, MONGODB_DB_NAME, MONGODB_DIRECT_CONNECTION } from "$env/static/private"; import { GridFSBucket, MongoClient } from "mongodb"; import type { Conversation } from "$lib/types/Conversation"; import type { SharedConversation } from "$lib/types/SharedConversation"; @@ -13,197 +13,149 @@ 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"; -import { building } from "$app/environment"; +if (!MONGODB_URL) { + throw new Error( + "Please specify the MONGODB_URL environment variable inside .env.local. Set it to mongodb://localhost:27017 if you are running MongoDB locally, or to a MongoDB Atlas free instance for example." + ); +} export const CONVERSATION_STATS_COLLECTION = "conversations.stats"; -export class Database { - private client: MongoClient; - - private static instance: Database; - - private constructor() { - if (!env.MONGODB_URL) { - throw new Error( - "Please specify the MONGODB_URL environment variable inside .env.local. Set it to mongodb://localhost:27017 if you are running MongoDB locally, or to a MongoDB Atlas free instance for example." - ); - } - - this.client = new MongoClient(env.MONGODB_URL, { - directConnection: env.MONGODB_DIRECT_CONNECTION === "true", - }); - - this.client.connect().catch((err) => { - logger.error("Connection error", err); - process.exit(1); - }); - this.client.db(env.MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "")); - this.client.on("open", () => this.initDatabase()); - - // Disconnect DB on process kill - process.on("SIGINT", async () => { - await this.client.close(true); - - // https://github.com/sveltejs/kit/issues/9540 - setTimeout(() => { - process.exit(0); - }, 100); - }); - } - - public static getInstance(): Database { - if (!Database.instance) { - Database.instance = new Database(); - } - - return Database.instance; - } - - /** - * Return mongoClient - */ - public getClient(): MongoClient { - return this.client; - } - - /** - * Return map of database's collections - */ - public getCollections() { - const db = this.client.db( - env.MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "") - ); - - const conversations = db.collection("conversations"); - const conversationStats = db.collection(CONVERSATION_STATS_COLLECTION); - const assistants = db.collection("assistants"); - const assistantStats = db.collection("assistants.stats"); - const reports = db.collection("reports"); - const sharedConversations = db.collection("sharedConversations"); - const abortedGenerations = db.collection("abortedGenerations"); - const settings = db.collection("settings"); - const users = db.collection("users"); - const sessions = db.collection("sessions"); - const messageEvents = db.collection("messageEvents"); - const bucket = new GridFSBucket(db, { bucketName: "files" }); - const migrationResults = db.collection("migrationResults"); - const semaphores = db.collection("semaphores"); - - return { - conversations, - conversationStats, - assistants, - assistantStats, - reports, - sharedConversations, - abortedGenerations, - settings, - users, - sessions, - messageEvents, - bucket, - migrationResults, - semaphores, - }; - } - - /** - * Init database once connected: Index creation - * @private - */ - private initDatabase() { - const { - conversations, - conversationStats, - assistants, - assistantStats, - reports, - sharedConversations, - abortedGenerations, - settings, - users, - sessions, - messageEvents, - semaphores, - } = this.getCollections(); - - conversations - .createIndex( - { sessionId: 1, updatedAt: -1 }, - { partialFilterExpression: { sessionId: { $exists: true } } } - ) - .catch(logger.error); - conversations - .createIndex( - { userId: 1, updatedAt: -1 }, - { partialFilterExpression: { userId: { $exists: true } } } - ) - .catch(logger.error); - conversations - .createIndex( - { "message.id": 1, "message.ancestors": 1 }, - { partialFilterExpression: { userId: { $exists: true } } } - ) - .catch(logger.error); - // Not strictly necessary, could use _id, but more convenient. Also for stats - // To do stats on conversation messages - conversations.createIndex({ "messages.createdAt": 1 }, { sparse: true }).catch(logger.error); - // Unique index for stats - conversationStats - .createIndex( - { - type: 1, - "date.field": 1, - "date.span": 1, - "date.at": 1, - distinct: 1, - }, - { unique: true } - ) - .catch(logger.error); - // Allow easy check of last computed stat for given type/dateField - conversationStats - .createIndex({ +const client = new MongoClient(MONGODB_URL, { + directConnection: MONGODB_DIRECT_CONNECTION === "true", +}); + +export const connectPromise = client.connect().catch(console.error); + +export function getCollections(mongoClient: MongoClient) { + const db = mongoClient.db(MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "")); + + const conversations = db.collection("conversations"); + const conversationStats = db.collection(CONVERSATION_STATS_COLLECTION); + const assistants = db.collection("assistants"); + const assistantStats = db.collection("assistants.stats"); + const reports = db.collection("reports"); + const sharedConversations = db.collection("sharedConversations"); + const abortedGenerations = db.collection("abortedGenerations"); + const settings = db.collection("settings"); + const users = db.collection("users"); + const sessions = db.collection("sessions"); + const messageEvents = db.collection("messageEvents"); + const bucket = new GridFSBucket(db, { bucketName: "files" }); + const migrationResults = db.collection("migrationResults"); + const semaphores = db.collection("semaphores"); + + return { + conversations, + conversationStats, + assistants, + assistantStats, + reports, + sharedConversations, + abortedGenerations, + settings, + users, + sessions, + messageEvents, + bucket, + migrationResults, + semaphores, + }; +} +const db = client.db(MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "")); + +const collections = getCollections(client); + +const { + conversations, + conversationStats, + assistants, + assistantStats, + reports, + sharedConversations, + abortedGenerations, + settings, + users, + sessions, + messageEvents, + semaphores, +} = collections; + +export { client, db, collections }; + +client.on("open", () => { + conversations + .createIndex( + { sessionId: 1, updatedAt: -1 }, + { partialFilterExpression: { sessionId: { $exists: true } } } + ) + .catch(console.error); + conversations + .createIndex( + { userId: 1, updatedAt: -1 }, + { partialFilterExpression: { userId: { $exists: true } } } + ) + .catch(console.error); + conversations + .createIndex( + { "message.id": 1, "message.ancestors": 1 }, + { partialFilterExpression: { userId: { $exists: true } } } + ) + .catch(console.error); + // To do stats on conversations + conversations.createIndex({ updatedAt: 1 }).catch(console.error); + // Not strictly necessary, could use _id, but more convenient. Also for stats + conversations.createIndex({ createdAt: 1 }).catch(console.error); + // To do stats on conversation messages + conversations.createIndex({ "messages.createdAt": 1 }, { sparse: true }).catch(console.error); + // Unique index for stats + conversationStats + .createIndex( + { type: 1, "date.field": 1, + "date.span": 1, "date.at": 1, - }) - .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(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(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(logger.error); - semaphores.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(logger.error); - } -} - -export const collections = building - ? ({} as unknown as ReturnType) - : Database.getInstance().getCollections(); + distinct: 1, + }, + { unique: true } + ) + .catch(console.error); + // Allow easy check of last computed stat for given type/dateField + conversationStats + .createIndex({ + type: 1, + "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); + // 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); + 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); + + // 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); +}); diff --git a/src/lib/server/embeddingEndpoints/embeddingEndpoints.ts b/src/lib/server/embeddingEndpoints/embeddingEndpoints.ts index 053e4316605e098cf3957bceff324a6284c76481..2644d20e5962ba867916b6b14ad19f7164047382 100644 --- a/src/lib/server/embeddingEndpoints/embeddingEndpoints.ts +++ b/src/lib/server/embeddingEndpoints/embeddingEndpoints.ts @@ -11,7 +11,6 @@ import { embeddingEndpointOpenAI, embeddingEndpointOpenAIParametersSchema, } from "./openai/embeddingEndpoints"; -import { embeddingEndpointHfApi, embeddingEndpointHfApiSchema } from "./hfApi/embeddingHfApi"; // parameters passed when generating text interface EmbeddingEndpointParameters { @@ -27,7 +26,6 @@ export const embeddingEndpointSchema = z.discriminatedUnion("type", [ embeddingEndpointTeiParametersSchema, embeddingEndpointTransformersJSParametersSchema, embeddingEndpointOpenAIParametersSchema, - embeddingEndpointHfApiSchema, ]); type EmbeddingEndpointTypeOptions = z.infer["type"]; @@ -44,7 +42,6 @@ export const embeddingEndpoints: { tei: embeddingEndpointTei, transformersjs: embeddingEndpointTransformersJS, openai: embeddingEndpointOpenAI, - hfapi: embeddingEndpointHfApi, }; export default embeddingEndpoints; diff --git a/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts b/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts deleted file mode 100644 index 86f84ac19b851333a9380fe08eda2e8d05653fe4..0000000000000000000000000000000000000000 --- a/src/lib/server/embeddingEndpoints/hfApi/embeddingHfApi.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { z } from "zod"; -import type { EmbeddingEndpoint, Embedding } from "../embeddingEndpoints"; -import { chunk } from "$lib/utils/chunk"; -import { env } from "$env/dynamic/private"; -import { logger } from "$lib/server/logger"; - -export const embeddingEndpointHfApiSchema = z.object({ - weight: z.number().int().positive().default(1), - model: z.any(), - type: z.literal("hfapi"), - authorization: z - .string() - .optional() - .transform((v) => (!v && env.HF_TOKEN ? "Bearer " + env.HF_TOKEN : v)), // if the header is not set but HF_TOKEN is, use it as the authorization header -}); - -export async function embeddingEndpointHfApi( - input: z.input -): Promise { - const { model, authorization } = embeddingEndpointHfApiSchema.parse(input); - const url = "https://api-inference.huggingface.co/models/" + model.id; - - return async ({ inputs }) => { - const batchesInputs = chunk(inputs, 128); - - const batchesResults = await Promise.all( - batchesInputs.map(async (batchInputs) => { - const response = await fetch(`${url}`, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - ...(authorization ? { Authorization: authorization } : {}), - }, - body: JSON.stringify({ inputs: batchInputs }), - }); - - if (!response.ok) { - logger.error(await response.text()); - logger.error("Failed to get embeddings from Hugging Face API", response); - return []; - } - - const embeddings: Embedding[] = await response.json(); - return embeddings; - }) - ); - - const flatAllEmbeddings = batchesResults.flat(); - - return flatAllEmbeddings; - }; -} diff --git a/src/lib/server/embeddingEndpoints/openai/embeddingEndpoints.ts b/src/lib/server/embeddingEndpoints/openai/embeddingEndpoints.ts index 527a97324984512fd836efde5e7e6e8aebb19345..89d7900bb28c09a8e87751b92159e3a499a4a4e7 100644 --- a/src/lib/server/embeddingEndpoints/openai/embeddingEndpoints.ts +++ b/src/lib/server/embeddingEndpoints/openai/embeddingEndpoints.ts @@ -1,14 +1,14 @@ import { z } from "zod"; import type { EmbeddingEndpoint, Embedding } from "../embeddingEndpoints"; import { chunk } from "$lib/utils/chunk"; -import { env } from "$env/dynamic/private"; +import { OPENAI_API_KEY } from "$env/static/private"; export const embeddingEndpointOpenAIParametersSchema = z.object({ weight: z.number().int().positive().default(1), model: z.any(), type: z.literal("openai"), url: z.string().url().default("https://api.openai.com/v1/embeddings"), - apiKey: z.string().default(env.OPENAI_API_KEY), + apiKey: z.string().default(OPENAI_API_KEY), }); export async function embeddingEndpointOpenAI( diff --git a/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts b/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts index c999ceba7da550ce5f7eb1ef6e263fb3142f4101..5af933362fbfd469e2baa80adbbc544f97d8124b 100644 --- a/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts +++ b/src/lib/server/embeddingEndpoints/tei/embeddingEndpoints.ts @@ -1,18 +1,13 @@ import { z } from "zod"; import type { EmbeddingEndpoint, Embedding } from "../embeddingEndpoints"; import { chunk } from "$lib/utils/chunk"; -import { env } from "$env/dynamic/private"; -import { logger } from "$lib/server/logger"; export const embeddingEndpointTeiParametersSchema = z.object({ weight: z.number().int().positive().default(1), model: z.any(), type: z.literal("tei"), url: z.string().url(), - authorization: z - .string() - .optional() - .transform((v) => (!v && env.HF_TOKEN ? "Bearer " + env.HF_TOKEN : v)), // if the header is not set but HF_TOKEN is, use it as the authorization header + authorization: z.string().optional(), }); const getModelInfoByUrl = async (url: string, authorization?: string) => { @@ -26,13 +21,8 @@ const getModelInfoByUrl = async (url: string, authorization?: string) => { }, }); - try { - const json = await response.json(); - return { max_client_batch_size: 32, max_batch_tokens: 16384, ...json }; - } catch { - logger.debug("Could not get info from TEI embedding endpoint. Using defaults."); - return { max_client_batch_size: 32, max_batch_tokens: 16384 }; - } + const json = await response.json(); + return json; }; export async function embeddingEndpointTei( diff --git a/src/lib/server/embeddingEndpoints/transformersjs/embeddingEndpoints.ts b/src/lib/server/embeddingEndpoints/transformersjs/embeddingEndpoints.ts index 6f24ce74715167b0d79bf154b18a9b386e6b5d13..6e6c47f50bb7c1e0e369786a1a497769b083bd31 100644 --- a/src/lib/server/embeddingEndpoints/transformersjs/embeddingEndpoints.ts +++ b/src/lib/server/embeddingEndpoints/transformersjs/embeddingEndpoints.ts @@ -18,10 +18,9 @@ class TransformersJSModelsSingleton { if (modelPipelineInstance) { const [, modelPipeline] = modelPipelineInstance; - // dispose of the previous pipeline to clear memory - await (await modelPipeline).dispose(); - this.instances = this.instances.filter(([name]) => name !== modelName); + return modelPipeline; } + const newModelPipeline = pipeline("feature-extraction", modelName); this.instances.push([modelName, newModelPipeline]); diff --git a/src/lib/server/embeddingModels.ts b/src/lib/server/embeddingModels.ts index 67ad8fe5b1edc61aa6cde738e9ee18ca477f304f..303c90c2e0fcc69c05e2f5689e97c8594ba96300 100644 --- a/src/lib/server/embeddingModels.ts +++ b/src/lib/server/embeddingModels.ts @@ -1,4 +1,4 @@ -import { env } from "$env/dynamic/private"; +import { TEXT_EMBEDDING_MODELS } from "$env/static/private"; import { z } from "zod"; import { sum } from "$lib/utils/sum"; @@ -29,7 +29,7 @@ const modelConfig = z.object({ // Default embedding model for backward compatibility const rawEmbeddingModelJSON = - env.TEXT_EMBEDDING_MODELS || + TEXT_EMBEDDING_MODELS || `[ { "name": "Xenova/gte-small", @@ -73,10 +73,6 @@ const addEndpoint = (m: Awaited>) => ({ return embeddingEndpoints.transformersjs(args); case "openai": return embeddingEndpoints.openai(args); - case "hfapi": - return embeddingEndpoints.hfapi(args); - default: - throw new Error(`Unknown endpoint type: ${args}`); } } diff --git a/src/lib/server/endpoints/anthropic/endpointAnthropic.ts b/src/lib/server/endpoints/anthropic/endpointAnthropic.ts index 4353c6b11a5fe0dc2b31608dc3ee0e821c09ed8d..e3ef2eb51fa445f6ddd0acf52356b19525e22665 100644 --- a/src/lib/server/endpoints/anthropic/endpointAnthropic.ts +++ b/src/lib/server/endpoints/anthropic/endpointAnthropic.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { env } from "$env/dynamic/private"; +import { ANTHROPIC_API_KEY } from "$env/static/private"; import type { Endpoint } from "../endpoints"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; @@ -8,7 +8,7 @@ export const endpointAnthropicParametersSchema = z.object({ model: z.any(), type: z.literal("anthropic"), baseURL: z.string().url().default("https://api.anthropic.com"), - apiKey: z.string().default(env.ANTHROPIC_API_KEY ?? "sk-"), + apiKey: z.string().default(ANTHROPIC_API_KEY ?? "sk-"), defaultHeaders: z.record(z.string()).optional(), defaultQuery: z.record(z.string()).optional(), }); diff --git a/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts b/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts index f09d2723adb6c2413b5ff8ad61491b63850b058a..3a1be4d5175a859ef8f9ebb2ccb608c3daa6437a 100644 --- a/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts +++ b/src/lib/server/endpoints/cloudflare/endpointCloudflare.ts @@ -1,15 +1,14 @@ import { z } from "zod"; import type { Endpoint } from "../endpoints"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; -import { env } from "$env/dynamic/private"; -import { logger } from "$lib/server/logger"; +import { CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN } from "$env/static/private"; export const endpointCloudflareParametersSchema = z.object({ weight: z.number().int().positive().default(1), model: z.any(), type: z.literal("cloudflare"), - accountId: z.string().default(env.CLOUDFLARE_ACCOUNT_ID), - apiToken: z.string().default(env.CLOUDFLARE_API_TOKEN), + accountId: z.string().default(CLOUDFLARE_ACCOUNT_ID), + apiToken: z.string().default(CLOUDFLARE_API_TOKEN), }); export async function endpointCloudflare( @@ -105,8 +104,8 @@ export async function endpointCloudflare( try { data = JSON.parse(jsonString); } catch (e) { - logger.error("Failed to parse JSON", e); - logger.error("Problematic JSON string:", jsonString); + console.error("Failed to parse JSON", e); + console.error("Problematic JSON string:", jsonString); continue; // Skip this iteration and try the next chunk } diff --git a/src/lib/server/endpoints/cohere/endpointCohere.ts b/src/lib/server/endpoints/cohere/endpointCohere.ts index f1c5562fa022d3e5e40f7d8f240a32185f61e9c3..524152fb9915fea2e1e48cf31bc4023ebe4b0c27 100644 --- a/src/lib/server/endpoints/cohere/endpointCohere.ts +++ b/src/lib/server/endpoints/cohere/endpointCohere.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { env } from "$env/dynamic/private"; +import { COHERE_API_TOKEN } from "$env/static/private"; import type { Endpoint } from "../endpoints"; import type { TextGenerationStreamOutput } from "@huggingface/inference"; import type { Cohere, CohereClient } from "cohere-ai"; @@ -9,7 +9,7 @@ export const endpointCohereParametersSchema = z.object({ weight: z.number().int().positive().default(1), model: z.any(), type: z.literal("cohere"), - apiKey: z.string().default(env.COHERE_API_TOKEN), + apiKey: z.string().default(COHERE_API_TOKEN), raw: z.boolean().default(false), }); diff --git a/src/lib/server/endpoints/google/endpointVertex.ts b/src/lib/server/endpoints/google/endpointVertex.ts index d8d3778ae8292f08745369dc482b4aae8eeea374..8c048e52d78b5f982e9749f43fac9c466e7f813f 100644 --- a/src/lib/server/endpoints/google/endpointVertex.ts +++ b/src/lib/server/endpoints/google/endpointVertex.ts @@ -1,14 +1,8 @@ -import { - VertexAI, - HarmCategory, - HarmBlockThreshold, - type Content, - type TextPart, -} from "@google-cloud/vertexai"; +import { VertexAI, HarmCategory, HarmBlockThreshold } from "@google-cloud/vertexai"; +import { buildPrompt } from "$lib/buildPrompt"; +import type { TextGenerationStreamOutput } from "@huggingface/inference"; import type { Endpoint } from "../endpoints"; import { z } from "zod"; -import type { Message } from "$lib/types/Message"; -import type { TextGenerationStreamOutput } from "@huggingface/inference"; export const endpointVertexParametersSchema = z.object({ weight: z.number().int().positive().default(1), @@ -17,20 +11,10 @@ export const endpointVertexParametersSchema = z.object({ location: z.string().default("europe-west1"), project: z.string(), apiEndpoint: z.string().optional(), - safetyThreshold: z - .enum([ - HarmBlockThreshold.HARM_BLOCK_THRESHOLD_UNSPECIFIED, - HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, - HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, - HarmBlockThreshold.BLOCK_NONE, - HarmBlockThreshold.BLOCK_ONLY_HIGH, - ]) - .optional(), }); export function endpointVertex(input: z.input): Endpoint { - const { project, location, model, apiEndpoint, safetyThreshold } = - endpointVertexParametersSchema.parse(input); + const { project, location, model, apiEndpoint } = endpointVertexParametersSchema.parse(input); const vertex_ai = new VertexAI({ project, @@ -38,104 +22,55 @@ export function endpointVertex(input: z.input { - const generativeModel = vertex_ai.getGenerativeModel({ - model: model.id ?? model.name, - safetySettings: safetyThreshold - ? [ - { - category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, - threshold: safetyThreshold, - }, - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: safetyThreshold, - }, - { - category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, - threshold: safetyThreshold, - }, - { - category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, - threshold: safetyThreshold, - }, - { - category: HarmCategory.HARM_CATEGORY_UNSPECIFIED, - threshold: safetyThreshold, - }, - ] - : undefined, - generationConfig: { - maxOutputTokens: generateSettings?.max_new_tokens ?? 4096, - stopSequences: generateSettings?.stop, - temperature: generateSettings?.temperature ?? 1, + const generativeModel = vertex_ai.getGenerativeModel({ + model: model.id ?? model.name, + safety_settings: [ + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, - }); - - // Preprompt is the same as the first system message. - let systemMessage = preprompt; - if (messages[0].from === "system") { - systemMessage = messages[0].content; - messages.shift(); - } - - const vertexMessages = messages.map(({ from, content }: Omit): Content => { - return { - role: from === "user" ? "user" : "model", - parts: [ - { - text: content, - }, - ], - }; - }); + ], + generation_config: {}, + }); - const result = await generativeModel.generateContentStream({ - contents: vertexMessages, - systemInstruction: systemMessage - ? { - role: "system", - parts: [ - { - text: systemMessage, - }, - ], - } - : undefined, + return async ({ messages, preprompt, continueMessage }) => { + const prompt = await buildPrompt({ + messages, + continueMessage, + preprompt, + model, }); + const chat = generativeModel.startChat(); + const result = await chat.sendMessageStream(prompt); let tokenId = 0; + return (async function* () { let generatedText = ""; for await (const data of result.stream) { - if (!data?.candidates?.length) break; // Handle case where no candidates are present - - const candidate = data.candidates[0]; - if (!candidate.content?.parts?.length) continue; // Skip if no parts are present - - const firstPart = candidate.content.parts.find((part) => "text" in part) as - | TextPart - | undefined; - if (!firstPart) continue; // Skip if no text part is found - - const isLastChunk = !!candidate.finishReason; - - const content = firstPart.text; - generatedText += content; - const output: TextGenerationStreamOutput = { - token: { - id: tokenId++, - text: content, - logprob: 0, - special: isLastChunk, - }, - generated_text: isLastChunk ? generatedText : null, - details: null, - }; - yield output; + if (Array.isArray(data?.candidates) && data.candidates.length > 0) { + const firstPart = data.candidates[0].content.parts[0]; + if ("text" in firstPart) { + const content = firstPart.text; + generatedText += content; + const output: TextGenerationStreamOutput = { + token: { + id: tokenId++, + text: content ?? "", + logprob: 0, + special: false, + }, + generated_text: generatedText, + details: null, + }; + yield output; + } - if (isLastChunk) break; + if (!data.candidates.slice(-1)[0].finishReason) break; + } else { + break; + } } })(); }; diff --git a/src/lib/server/endpoints/langserve/endpointLangserve.ts b/src/lib/server/endpoints/langserve/endpointLangserve.ts index 364765c478daddb7a39e41d2af39de705362b865..2c5a475c967309086a67d7b4f39dd5c1edd5ba6f 100644 --- a/src/lib/server/endpoints/langserve/endpointLangserve.ts +++ b/src/lib/server/endpoints/langserve/endpointLangserve.ts @@ -2,7 +2,6 @@ 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), @@ -100,8 +99,8 @@ export function endpointLangserve( try { data = JSON.parse(jsonString); } catch (e) { - logger.error("Failed to parse JSON", e); - logger.error("Problematic JSON string:", jsonString); + console.error("Failed to parse JSON", e); + console.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 b2b8d1478c2fb351869984f44df7feb04184fada..ffd9fa2c49518fa27966454d29a1cf3028b4c9a1 100644 --- a/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts +++ b/src/lib/server/endpoints/llamacpp/endpointLlamacpp.ts @@ -1,9 +1,8 @@ -import { env } from "$env/dynamic/private"; +import { HF_ACCESS_TOKEN, HF_TOKEN } from "$env/static/private"; 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), @@ -13,7 +12,7 @@ export const endpointLlamacppParametersSchema = z.object({ accessToken: z .string() .min(1) - .default(env.HF_TOKEN ?? env.HF_ACCESS_TOKEN), + .default(HF_TOKEN ?? HF_ACCESS_TOKEN), }); export function endpointLlamacpp( @@ -94,8 +93,8 @@ export function endpointLlamacpp( try { data = JSON.parse(jsonString); } catch (e) { - logger.error("Failed to parse JSON", e); - logger.error("Problematic JSON string:", jsonString); + console.error("Failed to parse JSON", e); + console.error("Problematic JSON string:", jsonString); continue; // Skip this iteration and try the next chunk } diff --git a/src/lib/server/endpoints/openai/endpointOai.ts b/src/lib/server/endpoints/openai/endpointOai.ts index 945921b1b9a4bd86e8fcf3374c0e120007a3336c..8bd28540dd4b82dd28ce7502fd95d25cd66320ce 100644 --- a/src/lib/server/endpoints/openai/endpointOai.ts +++ b/src/lib/server/endpoints/openai/endpointOai.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { openAICompletionToTextGenerationStream } from "./openAICompletionToTextGenerationStream"; import { openAIChatToTextGenerationStream } from "./openAIChatToTextGenerationStream"; import { buildPrompt } from "$lib/buildPrompt"; -import { env } from "$env/dynamic/private"; +import { OPENAI_API_KEY } from "$env/static/private"; import type { Endpoint } from "../endpoints"; export const endpointOAIParametersSchema = z.object({ @@ -10,7 +10,7 @@ export const endpointOAIParametersSchema = z.object({ model: z.any(), type: z.literal("openai"), baseURL: z.string().url().default("https://api.openai.com/v1"), - apiKey: z.string().default(env.OPENAI_API_KEY ?? "sk-"), + apiKey: z.string().default(OPENAI_API_KEY ?? "sk-"), completion: z .union([z.literal("completions"), z.literal("chat_completions")]) .default("chat_completions"), diff --git a/src/lib/server/endpoints/tgi/endpointTgi.ts b/src/lib/server/endpoints/tgi/endpointTgi.ts index aed067397222cfab3633c59b37b5212860b9c054..131d628ae2122e5ad3976a69458372574c3d299c 100644 --- a/src/lib/server/endpoints/tgi/endpointTgi.ts +++ b/src/lib/server/endpoints/tgi/endpointTgi.ts @@ -1,4 +1,4 @@ -import { env } from "$env/dynamic/private"; +import { HF_ACCESS_TOKEN, HF_TOKEN } from "$env/static/private"; import { buildPrompt } from "$lib/buildPrompt"; import { textGenerationStream } from "@huggingface/inference"; import type { Endpoint } from "../endpoints"; @@ -9,7 +9,7 @@ export const endpointTgiParametersSchema = z.object({ model: z.any(), type: z.literal("tgi"), url: z.string().url(), - accessToken: z.string().default(env.HF_TOKEN ?? env.HF_ACCESS_TOKEN), + accessToken: z.string().default(HF_TOKEN ?? HF_ACCESS_TOKEN), authorization: z.string().optional(), }); diff --git a/src/lib/server/files/downloadFile.ts b/src/lib/server/files/downloadFile.ts index 91b430fc5d835a66e95e2010c07f79b0acc57d58..4d2bddb1c30459c319a02200e4f33a9b38b957e2 100644 --- a/src/lib/server/files/downloadFile.ts +++ b/src/lib/server/files/downloadFile.ts @@ -1,5 +1,5 @@ import { error } from "@sveltejs/kit"; -import { collections } from "$lib/server/database"; +import { collections } from "../database"; import type { Conversation } from "$lib/types/Conversation"; import type { SharedConversation } from "$lib/types/SharedConversation"; diff --git a/src/lib/server/files/uploadFile.ts b/src/lib/server/files/uploadFile.ts index 34452245741f05b7819489a109133e8c8e125f9c..1c4a59b6f44f1914c9ae28b83a5c91c81e146af1 100644 --- a/src/lib/server/files/uploadFile.ts +++ b/src/lib/server/files/uploadFile.ts @@ -1,6 +1,6 @@ import type { Conversation } from "$lib/types/Conversation"; import { sha256 } from "$lib/utils/sha256"; -import { collections } from "$lib/server/database"; +import { collections } from "../database"; export async function uploadFile(file: Blob, conv: Conversation): Promise { const sha = await sha256(await file.text()); diff --git a/src/lib/server/generateFromDefaultEndpoint.ts b/src/lib/server/generateFromDefaultEndpoint.ts index 428e94a06f90fc712db51e2c816175035cfab2b6..30fd386dfe10d65ce10efe526390455f0a17f938 100644 --- a/src/lib/server/generateFromDefaultEndpoint.ts +++ b/src/lib/server/generateFromDefaultEndpoint.ts @@ -4,15 +4,13 @@ import type { Conversation } from "$lib/types/Conversation"; export async function generateFromDefaultEndpoint({ messages, preprompt, - generateSettings, }: { messages: Omit[]; preprompt?: string; - generateSettings?: Record; }): Promise { const endpoint = await smallModel.getEndpoint(); - const tokenStream = await endpoint({ messages, preprompt, generateSettings }); + const tokenStream = await endpoint({ messages, preprompt }); for await (const output of tokenStream) { // if not generated_text is here it means the generation is not done diff --git a/src/lib/server/logger.ts b/src/lib/server/logger.ts deleted file mode 100644 index b01b7692e3a33b20df5a86572cd8d748113ffda7..0000000000000000000000000000000000000000 --- a/src/lib/server/logger.ts +++ /dev/null @@ -1,18 +0,0 @@ -import pino from "pino"; -import { dev } from "$app/environment"; -import { env } from "$env/dynamic/private"; - -let options: pino.LoggerOptions = {}; - -if (dev) { - options = { - transport: { - target: "pino-pretty", - options: { - colorize: true, - }, - }, - }; -} - -export const logger = pino({ ...options, level: env.LOG_LEVEL ?? "info" }); diff --git a/src/lib/server/metrics.ts b/src/lib/server/metrics.ts deleted file mode 100644 index a4728cc78ba71d4ee7e37546f576b7fc29963ee8..0000000000000000000000000000000000000000 --- a/src/lib/server/metrics.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { collectDefaultMetrics, Registry } from "prom-client"; -import express from "express"; -import { logger } from "$lib/server/logger"; -import { env } from "$env/dynamic/private"; - -export class MetricsServer { - private static instance: MetricsServer; - - private constructor() { - const app = express(); - const port = env.METRICS_PORT || "5565"; - - const server = app.listen(port, () => { - logger.info(`Metrics server listening on port ${port}`); - }); - - const register = new Registry(); - collectDefaultMetrics({ register }); - - app.get("/metrics", (req, res) => { - register.metrics().then((metrics) => { - res.set("Content-Type", "text/plain"); - res.send(metrics); - }); - }); - - process.on("SIGINT", async () => { - logger.info("Sigint received, disconnect metrics server ..."); - server.close(() => { - logger.info("Server stopped ..."); - }); - process.exit(); - }); - } - - public static getInstance(): MetricsServer { - if (!MetricsServer.instance) { - MetricsServer.instance = new MetricsServer(); - } - - return MetricsServer.instance; - } -} diff --git a/src/lib/server/models.ts b/src/lib/server/models.ts index 7cdbaf5f5bb11e8a4d74bdc5a783e206ff19ed2b..f48d25288b37959c40eb292f93dd3b6a0ee7cb11 100644 --- a/src/lib/server/models.ts +++ b/src/lib/server/models.ts @@ -1,4 +1,11 @@ -import { env } from "$env/dynamic/private"; +import { + HF_TOKEN, + HF_API_ROOT, + MODELS, + OLD_MODELS, + TASK_MODEL, + HF_ACCESS_TOKEN, +} from "$env/static/private"; import type { ChatTemplateInput } from "$lib/types/Template"; import { compileTemplate } from "$lib/utils/template"; import { z } from "zod"; @@ -11,7 +18,6 @@ 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; @@ -65,7 +71,7 @@ const modelConfig = z.object({ embeddingModel: validateEmbeddingModelByName(embeddingModels).optional(), }); -const modelsRaw = z.array(modelConfig).parse(JSON5.parse(env.MODELS)); +const modelsRaw = z.array(modelConfig).parse(JSON5.parse(MODELS)); async function getChatPromptRender( m: z.infer @@ -85,13 +91,11 @@ async function getChatPromptRender( try { tokenizer = await getTokenizer(m.tokenizer); } catch (e) { - logger.error( + throw Error( "Failed to load tokenizer for model " + m.name + - " consider setting chatPromptTemplate manually or making sure the model is available on the hub. Error: " + - (e as Error).message + " consider setting chatPromptTemplate manually or making sure the model is available on the hub." ); - process.exit(); } const renderTemplate = ({ messages, preprompt }: ChatTemplateInput) => { @@ -140,8 +144,8 @@ const addEndpoint = (m: Awaited>) => ({ if (!m.endpoints) { return endpointTgi({ type: "tgi", - url: `${env.HF_API_ROOT}/${m.name}`, - accessToken: env.HF_TOKEN ?? env.HF_ACCESS_TOKEN, + url: `${HF_API_ROOT}/${m.name}`, + accessToken: HF_TOKEN ?? HF_ACCESS_TOKEN, weight: 1, model: m, }); @@ -192,7 +196,7 @@ export const models = await Promise.all(modelsRaw.map((e) => processModel(e).the export const defaultModel = models[0]; // Models that have been deprecated -export const oldModels = env.OLD_MODELS +export const oldModels = OLD_MODELS ? z .array( z.object({ @@ -201,7 +205,7 @@ export const oldModels = env.OLD_MODELS displayName: z.string().min(1).optional(), }) ) - .parse(JSON5.parse(env.OLD_MODELS)) + .parse(JSON5.parse(OLD_MODELS)) .map((m) => ({ ...m, id: m.id || m.name, displayName: m.displayName || m.name })) : []; @@ -212,9 +216,9 @@ export const validateModel = (_models: BackendModel[]) => { // if `TASK_MODEL` is string & name of a model in `MODELS`, then we use `MODELS[TASK_MODEL]`, else we try to parse `TASK_MODEL` as a model config itself -export const smallModel = env.TASK_MODEL - ? (models.find((m) => m.name === env.TASK_MODEL) || - (await processModel(modelConfig.parse(JSON5.parse(env.TASK_MODEL))).then((m) => +export const smallModel = TASK_MODEL + ? (models.find((m) => m.name === TASK_MODEL) || + (await processModel(modelConfig.parse(JSON5.parse(TASK_MODEL))).then((m) => addEndpoint(m) ))) ?? defaultModel diff --git a/src/lib/server/preprocessMessages.ts b/src/lib/server/preprocessMessages.ts index 9cdcaae04873b334dc5573334acaccb4d6c37347..53768fa6f62e55e8e3b418ebc61bf733e62336ba 100644 --- a/src/lib/server/preprocessMessages.ts +++ b/src/lib/server/preprocessMessages.ts @@ -2,7 +2,6 @@ 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[], @@ -45,7 +44,7 @@ Answer the question: ${lastQuestion}`; const b64 = image.toString("base64"); return `![](data:${mime};base64,${b64})})`; } catch (e) { - logger.error(e); + console.error(e); } }) ); diff --git a/src/lib/server/summarize.ts b/src/lib/server/summarize.ts index 4cef6174dc9d516ab20c99ef88ede10640d97078..aeeb4bacd07cfdaffbbffc303f4d049ccfd1518a 100644 --- a/src/lib/server/summarize.ts +++ b/src/lib/server/summarize.ts @@ -1,10 +1,9 @@ -import { env } from "$env/dynamic/private"; +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 (!env.LLM_SUMMERIZATION) { + if (!LLM_SUMMERIZATION) { return prompt.split(/\s+/g).slice(0, 5).join(" "); } @@ -28,11 +27,7 @@ export async function summarize(prompt: string) { return await generateFromDefaultEndpoint({ messages, - preprompt: - "You are a summarization AI. Summarize the user's request into a single short sentence of four words or less. Do not try to answer it, only summarize the user's query. Always start your answer with an emoji relevant to the summary", - generateSettings: { - max_new_tokens: 15, - }, + preprompt: `You are a summarization AI. You'll never answer a user's question directly, but instead summarize the user's request into a single short sentence of four words or less. Always start your answer with an emoji relevant to the summary.`, }) .then((summary) => { // add an emoji if none is found in the first three characters @@ -42,7 +37,7 @@ export async function summarize(prompt: string) { return summary; }) .catch((e) => { - logger.error(e); + console.error(e); return null; }); } diff --git a/src/lib/server/usageLimits.ts b/src/lib/server/usageLimits.ts index e1f2390388a60b9cb0f2045ee713ea82706f35a7..0323e83fb50a7c86c3662267b509d83f021be403 100644 --- a/src/lib/server/usageLimits.ts +++ b/src/lib/server/usageLimits.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { env } from "$env/dynamic/private"; +import { USAGE_LIMITS, RATE_LIMIT } from "$env/static/private"; import JSON5 from "json5"; // RATE_LIMIT is the legacy way to define messages per minute limit @@ -12,7 +12,7 @@ export const usageLimitsSchema = z messagesPerMinute: z .preprocess((val) => { if (val === undefined) { - return env.RATE_LIMIT; + return RATE_LIMIT; } return val; }, z.coerce.number().optional()) @@ -20,4 +20,4 @@ export const usageLimitsSchema = z }) .optional(); -export const usageLimits = usageLimitsSchema.parse(JSON5.parse(env.USAGE_LIMITS)); +export const usageLimits = usageLimitsSchema.parse(JSON5.parse(USAGE_LIMITS)); diff --git a/src/lib/server/websearch/generateQuery.ts b/src/lib/server/websearch/generateQuery.ts index 189d698acd7beb46ba8303fa57299993c67eb18e..ac14634dc83fbaa0c9fc024f1d4f9d98c61bb97e 100644 --- a/src/lib/server/websearch/generateQuery.ts +++ b/src/lib/server/websearch/generateQuery.ts @@ -64,9 +64,6 @@ Current Question: Where is it being hosted?`, const webQuery = await generateFromDefaultEndpoint({ messages: convQuery, preprompt: `You are tasked with generating web search queries. Give me an appropriate query to answer my question for google search. Answer with only the query. Today is ${currentDate}`, - generateSettings: { - max_new_tokens: 30, - }, }); return webQuery.trim(); diff --git a/src/lib/server/websearch/runWebSearch.ts b/src/lib/server/websearch/runWebSearch.ts index 1d06a39c5a8dfc605d122944a2f15a4381009150..8eba2ddd627f3a6b5c99b21e7558789ad0d85cfd 100644 --- a/src/lib/server/websearch/runWebSearch.ts +++ b/src/lib/server/websearch/runWebSearch.ts @@ -5,7 +5,7 @@ import { chunk } from "$lib/utils/chunk"; import { findSimilarSentences } from "$lib/server/sentenceSimilarity"; import { getWebSearchProvider } from "./searchWeb"; import { defaultEmbeddingModel, embeddingModels } from "$lib/server/embeddingModels"; -import { env } from "$env/dynamic/private"; +import { WEBSEARCH_ALLOWLIST, WEBSEARCH_BLOCKLIST, ENABLE_LOCAL_FETCH } from "$env/static/private"; import type { Conversation } from "$lib/types/Conversation"; import type { MessageUpdate } from "$lib/types/MessageUpdate"; @@ -22,8 +22,8 @@ const MAX_N_PAGES_EMBED = 5 as const; const listSchema = z.array(z.string()).default([]); -const allowList = listSchema.parse(JSON5.parse(env.WEBSEARCH_ALLOWLIST)); -const blockList = listSchema.parse(JSON5.parse(env.WEBSEARCH_BLOCKLIST)); +const allowList = listSchema.parse(JSON5.parse(WEBSEARCH_ALLOWLIST)); +const blockList = listSchema.parse(JSON5.parse(WEBSEARCH_BLOCKLIST)); export async function runWebSearch( conv: Conversation, @@ -52,7 +52,7 @@ export async function runWebSearch( let linksToUse = [...ragSettings.allowedLinks]; - if (env.ENABLE_LOCAL_FETCH !== "true") { + if (ENABLE_LOCAL_FETCH !== "true") { const localLinks = await Promise.all( linksToUse.map(async (link) => { try { diff --git a/src/lib/server/websearch/searchSearxng.ts b/src/lib/server/websearch/searchSearxng.ts index 9507d5369690c7be7669beec8965150f88ca1424..a432003cb1632445000f0637e7ac48690b90c32d 100644 --- a/src/lib/server/websearch/searchSearxng.ts +++ b/src/lib/server/websearch/searchSearxng.ts @@ -1,12 +1,11 @@ -import { env } from "$env/dynamic/private"; -import { logger } from "$lib/server/logger"; +import { SEARXNG_QUERY_URL } from "$env/static/private"; export async function searchSearxng(query: string) { const abortController = new AbortController(); setTimeout(() => abortController.abort(), 10000); // Insert the query into the URL template - let url = env.SEARXNG_QUERY_URL.replace("", query); + let url = SEARXNG_QUERY_URL.replace("", query); // Check if "&format=json" already exists in the URL if (!url.includes("&format=json")) { @@ -19,7 +18,7 @@ export async function searchSearxng(query: string) { }) .then((response) => response.json() as Promise<{ results: { url: string }[] }>) .catch((error) => { - logger.error("Failed to fetch or parse JSON", error); + console.error("Failed to fetch or parse JSON", error); throw new Error("Failed to fetch or parse JSON"); }); diff --git a/src/lib/server/websearch/searchWeb.ts b/src/lib/server/websearch/searchWeb.ts index 724be1227a7544c6944aaf69e8617d76f94e8e88..94021e5c014230d91662644363cf4341f951f003 100644 --- a/src/lib/server/websearch/searchWeb.ts +++ b/src/lib/server/websearch/searchWeb.ts @@ -1,6 +1,13 @@ import type { YouWebSearch } from "../../types/WebSearch"; import { WebSearchProvider } from "../../types/WebSearch"; -import { env } from "$env/dynamic/private"; +import { + SERPAPI_KEY, + SERPER_API_KEY, + SERPSTACK_API_KEY, + USE_LOCAL_WEBSEARCH, + SEARXNG_QUERY_URL, + YDC_API_KEY, +} from "$env/static/private"; import { getJson } from "serpapi"; import type { GoogleParameters } from "serpapi"; import { searchWebLocal } from "./searchWebLocal"; @@ -8,9 +15,9 @@ import { searchSearxng } from "./searchSearxng"; // get which SERP api is providing web results export function getWebSearchProvider() { - if (env.YDC_API_KEY) { + if (YDC_API_KEY) { return WebSearchProvider.YOU; - } else if (env.SEARXNG_QUERY_URL) { + } else if (SEARXNG_QUERY_URL) { return WebSearchProvider.SEARXNG; } else { return WebSearchProvider.GOOGLE; @@ -19,22 +26,22 @@ export function getWebSearchProvider() { // Show result as JSON export async function searchWeb(query: string) { - if (env.USE_LOCAL_WEBSEARCH) { + if (USE_LOCAL_WEBSEARCH) { return await searchWebLocal(query); } - if (env.SEARXNG_QUERY_URL) { + if (SEARXNG_QUERY_URL) { return await searchSearxng(query); } - if (env.SERPER_API_KEY) { + if (SERPER_API_KEY) { return await searchWebSerper(query); } - if (env.YDC_API_KEY) { + if (YDC_API_KEY) { return await searchWebYouApi(query); } - if (env.SERPAPI_KEY) { + if (SERPAPI_KEY) { return await searchWebSerpApi(query); } - if (env.SERPSTACK_API_KEY) { + if (SERPSTACK_API_KEY) { return await searchSerpStack(query); } throw new Error("No You.com or Serper.dev or SerpAPI key found"); @@ -51,7 +58,7 @@ export async function searchWebSerper(query: string) { method: "POST", body: JSON.stringify(params), headers: { - "x-api-key": env.SERPER_API_KEY, + "x-api-key": SERPER_API_KEY, "Content-type": "application/json; charset=UTF-8", }, }); @@ -77,7 +84,7 @@ export async function searchWebSerpApi(query: string) { hl: "en", gl: "us", google_domain: "google.com", - api_key: env.SERPAPI_KEY, + api_key: SERPAPI_KEY, } satisfies GoogleParameters; // Show result as JSON @@ -90,7 +97,7 @@ export async function searchWebYouApi(query: string) { const response = await fetch(`https://api.ydc-index.io/search?query=${query}`, { method: "GET", headers: { - "X-API-Key": env.YDC_API_KEY, + "X-API-Key": YDC_API_KEY, "Content-type": "application/json; charset=UTF-8", }, }); @@ -116,7 +123,7 @@ export async function searchWebYouApi(query: string) { export async function searchSerpStack(query: string) { const response = await fetch( - `http://api.serpstack.com/search?access_key=${env.SERPSTACK_API_KEY}&query=${query}&hl=en&gl=us`, + `http://api.serpstack.com/search?access_key=${SERPSTACK_API_KEY}&query=${query}&hl=en&gl=us`, { method: "GET", headers: { diff --git a/src/lib/utils/getShareUrl.ts b/src/lib/utils/getShareUrl.ts index 5278ab6fd6ef21b1c34468a0c9b81a164f78b52b..ef4259f6ad3a33c17cb29c676b3994a903663e9a 100644 --- a/src/lib/utils/getShareUrl.ts +++ b/src/lib/utils/getShareUrl.ts @@ -1,8 +1,6 @@ import { base } from "$app/paths"; -import { env as envPublic } from "$env/dynamic/public"; +import { PUBLIC_ORIGIN, PUBLIC_SHARE_PREFIX } from "$env/static/public"; export function getShareUrl(url: URL, shareId: string): string { - return `${ - envPublic.PUBLIC_SHARE_PREFIX || `${envPublic.PUBLIC_ORIGIN || url.origin}${base}` - }/r/${shareId}`; + return `${PUBLIC_SHARE_PREFIX || `${PUBLIC_ORIGIN || url.origin}${base}`}/r/${shareId}`; } diff --git a/src/lib/utils/isHuggingChat.ts b/src/lib/utils/isHuggingChat.ts index df1ad80039eb147a5427cd5ca1980e92b5c2c22a..fbcbefbc546054af65fce8dc0d918b16bf05bc35 100644 --- a/src/lib/utils/isHuggingChat.ts +++ b/src/lib/utils/isHuggingChat.ts @@ -1,3 +1,3 @@ -import { env as envPublic } from "$env/dynamic/public"; +import { PUBLIC_APP_ASSETS } from "$env/static/public"; -export const isHuggingChat = envPublic.PUBLIC_APP_ASSETS === "huggingchat"; +export const isHuggingChat = PUBLIC_APP_ASSETS === "huggingchat"; diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index 376412a7ffdbef85a845927136bf014169a32fd6..6836376aa41a8ef0bef4064e070e5de5c184879e 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -11,10 +11,5 @@

{$page.status}

{$page.error?.message}

- {#if $page.error?.errorId} -
-
{$page.error
-					.errorId}
- {/if}
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index a79c470f6885acbd324a4ed05b530f413f512b49..c353bc4f47cb00bb8477345c2fc894fdbdba4786 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -5,7 +5,17 @@ import { UrlDependency } from "$lib/types/UrlDependency"; import { defaultModel, models, oldModels, validateModel } from "$lib/server/models"; import { authCondition, requiresUser } from "$lib/server/auth"; import { DEFAULT_SETTINGS } from "$lib/types/Settings"; -import { env } from "$env/dynamic/private"; +import { + SERPAPI_KEY, + SERPER_API_KEY, + SERPSTACK_API_KEY, + MESSAGES_BEFORE_LOGIN, + YDC_API_KEY, + USE_LOCAL_WEBSEARCH, + SEARXNG_QUERY_URL, + ENABLE_ASSISTANTS, + ENABLE_ASSISTANTS_RAG, +} from "$env/static/private"; import { ObjectId } from "mongodb"; import type { ConvSidebar } from "$lib/types/ConvSidebar"; @@ -37,7 +47,7 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => { }); } - const enableAssistants = env.ENABLE_ASSISTANTS === "true"; + const enableAssistants = ENABLE_ASSISTANTS === "true"; const assistantActive = !models.map(({ id }) => id).includes(settings?.activeModel ?? ""); @@ -77,7 +87,7 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => { const assistants = await collections.assistants.find({ _id: { $in: assistantIds } }).toArray(); - const messagesBeforeLogin = env.MESSAGES_BEFORE_LOGIN ? parseInt(env.MESSAGES_BEFORE_LOGIN) : 0; + const messagesBeforeLogin = MESSAGES_BEFORE_LOGIN ? parseInt(MESSAGES_BEFORE_LOGIN) : 0; let loginRequired = false; @@ -126,12 +136,12 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => { }) satisfies ConvSidebar[], settings: { searchEnabled: !!( - env.SERPAPI_KEY || - env.SERPER_API_KEY || - env.SERPSTACK_API_KEY || - env.YDC_API_KEY || - env.USE_LOCAL_WEBSEARCH || - env.SEARXNG_QUERY_URL + SERPAPI_KEY || + SERPER_API_KEY || + SERPSTACK_API_KEY || + YDC_API_KEY || + USE_LOCAL_WEBSEARCH || + SEARXNG_QUERY_URL ), ethicsModalAccepted: !!settings?.ethicsModalAcceptedAt, ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null, @@ -178,7 +188,7 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => { }, assistant, enableAssistants, - enableAssistantsRAG: env.ENABLE_ASSISTANTS_RAG === "true", + enableAssistantsRAG: ENABLE_ASSISTANTS_RAG === "true", loginRequired, loginEnabled: requiresUser, guestMode: requiresUser && messagesBeforeLogin > 0, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 18f9a884b26608292974a4a053717d19c0122d09..bcfa7c05f37b0bbaa1fdfb2ad9139f122c5405fa 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,7 +7,13 @@ import { page } from "$app/stores"; import { browser } from "$app/environment"; - import { env as envPublic } from "$env/dynamic/public"; + import { + PUBLIC_APPLE_APP_ID, + PUBLIC_APP_DESCRIPTION, + PUBLIC_ORIGIN, + PUBLIC_PLAUSIBLE_SCRIPT_URL, + } from "$env/static/public"; + import { PUBLIC_APP_ASSETS, PUBLIC_APP_NAME } from "$env/static/public"; import { error } from "$lib/stores/errors"; import { createSettingsStore } from "$lib/stores/settings"; @@ -128,7 +134,7 @@ - {envPublic.PUBLIC_APP_NAME} + {PUBLIC_APP_NAME} @@ -136,49 +142,44 @@ {#if !$page.url.pathname.includes("/assistant/") && $page.route.id !== "/assistants" && !$page.url.pathname.includes("/models/")} - + - + - + {/if} - {#if envPublic.PUBLIC_PLAUSIBLE_SCRIPT_URL && envPublic.PUBLIC_ORIGIN} + {#if PUBLIC_PLAUSIBLE_SCRIPT_URL && PUBLIC_ORIGIN} {/if} - {#if envPublic.PUBLIC_APPLE_APP_ID} - + {#if PUBLIC_APPLE_APP_ID} + {/if} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index abedc1c8862c1ac1a81770408cbdf3fe16ce8f5a..d0410802aa4a53a3ca7d1b9d8be6e40d838f0b59 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,7 +1,7 @@ - {envPublic.PUBLIC_APP_NAME} + {PUBLIC_APP_NAME} " -H "Content-Type: application/json" -d '{"model": "OpenAssistant/oasst-sft-6-llama-30b-xor"}' export async function POST({ request }) { - if (!env.PARQUET_EXPORT_DATASET || !env.PARQUET_EXPORT_HF_TOKEN) { + if (!PARQUET_EXPORT_DATASET || !PARQUET_EXPORT_HF_TOKEN) { throw error(500, "Parquet export is not configured."); } @@ -42,7 +41,7 @@ export async function POST({ request }) { const writer = await parquet.ParquetWriter.openFile(schema, fileName); let count = 0; - logger.info("Exporting conversations for model", model); + console.log("Exporting conversations for model", model); for await (const conversation of collections.settings.aggregate<{ title: string; @@ -89,11 +88,11 @@ export async function POST({ request }) { ++count; if (count % 1_000 === 0) { - logger.info("Exported", count, "conversations"); + console.log("Exported", count, "conversations"); } } - logger.info("exporting convos with userId"); + console.log("exporting convos with userId"); for await (const conversation of collections.settings.aggregate<{ title: string; @@ -134,24 +133,24 @@ export async function POST({ request }) { ++count; if (count % 1_000 === 0) { - logger.info("Exported", count, "conversations"); + console.log("Exported", count, "conversations"); } } await writer.close(); - logger.info("Uploading", fileName, "to Hugging Face Hub"); + console.log("Uploading", fileName, "to Hugging Face Hub"); await uploadFile({ file: pathToFileURL(fileName) as URL, - credentials: { accessToken: env.PARQUET_EXPORT_HF_TOKEN }, + credentials: { accessToken: PARQUET_EXPORT_HF_TOKEN }, repo: { type: "dataset", - name: env.PARQUET_EXPORT_DATASET, + name: PARQUET_EXPORT_DATASET, }, }); - logger.info("Upload done"); + console.log("Upload done"); await unlink(fileName); diff --git a/src/routes/admin/stats/compute/+server.ts b/src/routes/admin/stats/compute/+server.ts index d8d7f0ec605075c2acf490d1196dacd6fd701a10..5e73c36c26ab2b80f69bfd52326861c71a4aef12 100644 --- a/src/routes/admin/stats/compute/+server.ts +++ b/src/routes/admin/stats/compute/+server.ts @@ -1,16 +1,15 @@ import { json } from "@sveltejs/kit"; import type { ConversationStats } from "$lib/types/ConversationStats"; -import { CONVERSATION_STATS_COLLECTION, collections } from "$lib/server/database"; -import { logger } from "$lib/server/logger"; +import { CONVERSATION_STATS_COLLECTION, collections } from "$lib/server/database.js"; // 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(logger.error); - computeStats({ dateField: "createdAt", type: "conversation", span }).catch(logger.error); - computeStats({ dateField: "createdAt", type: "message", span }).catch(logger.error); + 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); } return json({}, { status: 202 }); @@ -30,7 +29,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); - logger.info("Computing stats for", params.type, params.span, params.dateField, "from", minDate); + console.log("Computing stats for", params.type, params.span, params.dateField, "from", minDate); const dateField = params.type === "message" ? "messages." + params.dateField : params.dateField; @@ -214,5 +213,5 @@ async function computeStats(params: { await collections.conversations.aggregate(pipeline, { allowDiskUse: true }).next(); - logger.info("Computed stats for", params.type, params.span, params.dateField); + console.log("Computed stats for", params.type, params.span, params.dateField); } diff --git a/src/routes/api/assistants/+server.ts b/src/routes/api/assistants/+server.ts index ac588676cbc910ca1a45b2c0b21417801229b462..f62a36d9041dce17c0041a70249a8c37b643318b 100644 --- a/src/routes/api/assistants/+server.ts +++ b/src/routes/api/assistants/+server.ts @@ -1,9 +1,8 @@ -import { collections } from "$lib/server/database"; +import { collections } from "$lib/server/database.js"; import type { Assistant } from "$lib/types/Assistant"; import type { User } from "$lib/types/User"; import { generateQueryTokens } from "$lib/utils/searchTokens.js"; import type { Filter } from "mongodb"; -import { env } from "$env/dynamic/private"; const NUM_PER_PAGE = 24; @@ -25,23 +24,12 @@ export async function GET({ url, locals }) { } } - // if there is no user, we show community assistants, so only show featured assistants - const shouldBeFeatured = - env.REQUIRE_FEATURED_ASSISTANTS === "true" && !user ? { featured: true } : {}; - - // if the user queried is not the current user, only show "public" assistants that have been shared before - const shouldHaveBeenShared = - env.REQUIRE_FEATURED_ASSISTANTS === "true" && !createdByCurrentUser - ? { userCount: { $gt: 1 } } - : {}; - // fetch the top assistants sorted by user count from biggest to smallest, filter out all assistants with only 1 users. filter by model too if modelId is provided const filter: Filter = { ...(modelId && { modelId }), - ...(user && { createdById: user._id }), + ...(!createdByCurrentUser && { userCount: { $gt: 1 } }), + ...(user ? { createdById: user._id } : { featured: true }), ...(query && { searchTokens: { $all: generateQueryTokens(query) } }), - ...shouldBeFeatured, - ...shouldHaveBeenShared, }; const assistants = await collections.assistants .find(filter) diff --git a/src/routes/api/models/+server.ts b/src/routes/api/models/+server.ts index ab70f22b5e753dae0dd4154ccee25edf499176d3..ab8b40fe4df416a66753fc6f370cb8c6c5218c38 100644 --- a/src/routes/api/models/+server.ts +++ b/src/routes/api/models/+server.ts @@ -1,23 +1,21 @@ import { models } from "$lib/server/models"; export async function GET() { - const res = models - .filter((m) => m.unlisted == false) - .map((model) => ({ - id: model.id, - name: model.name, - websiteUrl: model.websiteUrl ?? "https://huggingface.co", - modelUrl: model.modelUrl ?? "https://huggingface.co", - tokenizer: model.tokenizer, - datasetName: model.datasetName, - datasetUrl: model.datasetUrl, - displayName: model.displayName, - description: model.description ?? "", - logoUrl: model.logoUrl, - promptExamples: model.promptExamples ?? [], - preprompt: model.preprompt ?? "", - multimodal: model.multimodal ?? false, - unlisted: model.unlisted ?? false, - })); + const res = models.map((model) => ({ + id: model.id, + name: model.name, + websiteUrl: model.websiteUrl, + modelUrl: model.modelUrl, + tokenizer: model.tokenizer, + datasetName: model.datasetName, + datasetUrl: model.datasetUrl, + displayName: model.displayName, + description: model.description, + logoUrl: model.logoUrl, + promptExamples: model.promptExamples, + preprompt: model.preprompt, + multimodal: model.multimodal, + unlisted: model.unlisted, + })); return Response.json(res); } diff --git a/src/routes/assistant/[assistantId]/+page.server.ts b/src/routes/assistant/[assistantId]/+page.server.ts index fddb181b4b88dcc7d8830af23688676c991ca792..ac14877dbc4367ef9498e6bdd4a6207b79039aab 100644 --- a/src/routes/assistant/[assistantId]/+page.server.ts +++ b/src/routes/assistant/[assistantId]/+page.server.ts @@ -1,5 +1,5 @@ import { base } from "$app/paths"; -import { collections } from "$lib/server/database"; +import { collections } from "$lib/server/database.js"; import { redirect } from "@sveltejs/kit"; import { ObjectId } from "mongodb"; diff --git a/src/routes/assistant/[assistantId]/+page.svelte b/src/routes/assistant/[assistantId]/+page.svelte index f19b879023bad0ec39e5a45bcfd25e5b586987b2..16608d07ef2b744869baf0802273de99f808ea00 100644 --- a/src/routes/assistant/[assistantId]/+page.svelte +++ b/src/routes/assistant/[assistantId]/+page.svelte @@ -6,7 +6,7 @@ import { useSettingsStore } from "$lib/stores/settings"; import type { PageData } from "./$types"; import { applyAction, enhance } from "$app/forms"; - import { env as envPublic } from "$env/dynamic/public"; + import { PUBLIC_APP_NAME, PUBLIC_ORIGIN } from "$env/static/public"; import { page } from "$app/stores"; import IconGear from "~icons/bi/gear-fill"; @@ -24,16 +24,15 @@ - + diff --git a/src/routes/assistants/+page.server.ts b/src/routes/assistants/+page.server.ts index 0cf3663f704a4565a162e511f4c11ead8a6e9002..00b01f4feba1380e62d2aea58518f13785bd3543 100644 --- a/src/routes/assistants/+page.server.ts +++ b/src/routes/assistants/+page.server.ts @@ -1,6 +1,6 @@ import { base } from "$app/paths"; -import { env } from "$env/dynamic/private"; -import { Database, collections } from "$lib/server/database.js"; +import { ENABLE_ASSISTANTS } from "$env/static/private"; +import { collections } from "$lib/server/database.js"; import { SortKey, type Assistant } from "$lib/types/Assistant"; import type { User } from "$lib/types/User"; import { generateQueryTokens } from "$lib/utils/searchTokens.js"; @@ -10,7 +10,7 @@ import type { Filter } from "mongodb"; const NUM_PER_PAGE = 24; export const load = async ({ url, locals }) => { - if (!env.ENABLE_ASSISTANTS) { + if (!ENABLE_ASSISTANTS) { throw redirect(302, `${base}/`); } @@ -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.TRENDING; + const sort = url.searchParams.get("sort")?.trim() ?? SortKey.POPULAR; const createdByCurrentUser = locals.user?.username && locals.user.username === username; let user: Pick | null = null; @@ -32,27 +32,15 @@ export const load = async ({ url, locals }) => { } } - // if there is no user, we show community assistants, so only show featured assistants - const shouldBeFeatured = - env.REQUIRE_FEATURED_ASSISTANTS === "true" && !user ? { featured: true } : {}; - - // if the user queried is not the current user, only show "public" assistants that have been shared before - const shouldHaveBeenShared = - env.REQUIRE_FEATURED_ASSISTANTS === "true" && !createdByCurrentUser - ? { userCount: { $gt: 1 } } - : {}; - - // fetch the top assistants sorted by user count from biggest to smallest. filter by model too if modelId is provided or query if query is provided + // fetch the top assistants sorted by user count from biggest to smallest, filter out all assistants with only 1 users. filter by model too if modelId is provided const filter: Filter = { ...(modelId && { modelId }), - ...(user && { createdById: user._id }), + ...(!createdByCurrentUser && { userCount: { $gt: 1 } }), + ...(user ? { createdById: user._id } : { featured: true }), ...(query && { searchTokens: { $all: generateQueryTokens(query) } }), - ...shouldBeFeatured, - ...shouldHaveBeenShared, }; - const assistants = await Database.getInstance() - .getCollections() - .assistants.find(filter) + const assistants = await collections.assistants + .find(filter) .skip(NUM_PER_PAGE * pageIndex) .sort({ ...(sort === SortKey.TRENDING && { last24HoursCount: -1 }), @@ -61,9 +49,7 @@ export const load = async ({ url, locals }) => { .limit(NUM_PER_PAGE) .toArray(); - const numTotalItems = await Database.getInstance() - .getCollections() - .assistants.countDocuments(filter); + const numTotalItems = await collections.assistants.countDocuments(filter); return { assistants: JSON.parse(JSON.stringify(assistants)) as Array, diff --git a/src/routes/assistants/+page.svelte b/src/routes/assistants/+page.svelte index d2f90b6323cb8f455e3626701cdaff51698c6e08..b27524aacc395ce0e405ec28c1d09c48592a04c0 100644 --- a/src/routes/assistants/+page.svelte +++ b/src/routes/assistants/+page.svelte @@ -1,7 +1,7 @@ - + - + diff --git a/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte b/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte index 86af4085e1f81f486278cebe2b9f9129421b2cc1..b76e362af73397948cfa1277562b4123cdae6463 100644 --- a/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +++ b/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte @@ -1,5 +1,5 @@