mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 14:10:15 -07:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bf552c491 | |||
| 3bf9923f86 | |||
| a6a8a28f59 | |||
| 56a8e452bf | |||
| 6bec0bf70d | |||
| 5dc9c8f90e | |||
| e3290e12b1 | |||
| 9f37ce9e42 | |||
| 8904c0c811 | |||
| b0d021b7f2 | |||
| 0175f3b8a1 | |||
| 0fa9d5bf62 | |||
| 4919e38e3e | |||
| 2e99533f96 | |||
| f095645d89 | |||
| 757c83142e | |||
| 36d274ca9f | |||
| ec11b61f67 | |||
| 7765271d63 | |||
| 7c2464bba7 | |||
| 17e010f93c | |||
| 452d630a2a | |||
| f317a3e38f | |||
| f56195058e | |||
| 2e93dbb10c | |||
| f862456d73 | |||
| d99b0b2137 | |||
| 1d390f9aa7 | |||
| 514beb7940 | |||
| c7bdfce734 | |||
| e5fe4b06ad | |||
| 89b7c265d3 | |||
| 698c31943e | |||
| b70060d46e | |||
| 6ddc5ef53e | |||
| 212023c7e4 | |||
| b687f23c95 | |||
| 7a05d01554 |
@@ -15,9 +15,25 @@ Or fork & run on Vercel
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI&env=OPENAI_API_KEY&envDescription=Backend%20API%20keys%2C%20optional%20and%20may%20be%20overridden%20by%20the%20UI.&envLink=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI%2Fblob%2Fmain%2Fdocs%2Fenvironment-variables.md&project-name=big-AGI)
|
||||
|
||||
## 👉 [roadmap](https://github.com/users/enricoros/projects/4/views/2) 👉 [documentation](docs/README.md)
|
||||
## 👉 [roadmap](https://github.com/users/enricoros/projects/4/views/2) 👉 [installation](docs/installation.md) 👉 [documentation](docs/README.md)
|
||||
|
||||
big-AGI is an open book; see the **[ready-to-ship and future ideas](https://github.com/users/enricoros/projects/4/views/2)** in our open roadmap
|
||||
[//]: # (big-AGI is an open book; see the **[ready-to-ship and future ideas](https://github.com/users/enricoros/projects/4/views/2)** in our open roadmap)
|
||||
|
||||
#### What's New in 1.15.1 · April 10, 2024 (minor release, models support)
|
||||
|
||||
- Support for the newly released Gemini Pro 1.5 models
|
||||
- Support for the new OpenAI 2024-04-09 Turbo models
|
||||
- Ctrl+S and Ctrl+O to save/load chats on desktop
|
||||
- Resilience fixes after the large success of 1.15.0
|
||||
|
||||
> Note: Beam-2 and new larger features are being cooked outside of `main`.
|
||||
|
||||
### 3,000 Commits Milestone · April 7, 2024
|
||||
|
||||

|
||||
|
||||
- 🥇 Today we <b>celebrate commit 3000</b> in just over one year, and going stronger 🚀
|
||||
- 📢️ Thanks everyone for your support and words of love for Big-AGI, we are committed to creating the best AI experiences for everyone.
|
||||
|
||||
### What's New in 1.15.0 · April 1, 2024 · Beam
|
||||
|
||||
@@ -141,6 +157,22 @@ Add extra functionality with these integrations:
|
||||
|
||||
<br/>
|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
To get started with big-AGI, follow our comprehensive [Installation Guide](docs/installation.md).
|
||||
The guide covers various installation options, whether you're spinning it up on
|
||||
your local computer, deploying on Vercel, on Cloudflare, or rolling it out
|
||||
through Docker.
|
||||
|
||||
Whether you're a developer, system integrator, or enterprise user, you'll find step-by-step instructions
|
||||
to set up big-AGI quickly and easily.
|
||||
|
||||
[](docs/installation.md)
|
||||
|
||||
Or bring your API keys and jump straight into our free instance on [big-AGI.com](https://big-agi.com).
|
||||
|
||||
<br/>
|
||||
|
||||
# 🌟 Get Involved!
|
||||
|
||||
[//]: # ([](https://discord.gg/MkH4qj2Jp9))
|
||||
@@ -150,86 +182,10 @@ Add extra functionality with these integrations:
|
||||
- [ ] ⭐ **Give us a star** on GitHub 👆
|
||||
- [ ] 🚀 **Do you like code**? You'll love this gem of a project! [_Pick up a task!_](https://github.com/users/enricoros/projects/4/views/4) - _easy_ to _pro_
|
||||
- [ ] 💡 Got a feature suggestion? [_Add your roadmap ideas_](https://github.com/enricoros/big-agi/issues/new?&template=roadmap-request.md)
|
||||
- [ ] ✨ Deploy your [fork](docs/customizations.md) for your friends and family, or [customize it for work](docs/customizations.md)
|
||||
- [ ] Check out some of the big-AGI [**community projects**](docs/customizations.md)
|
||||
|
||||
| Project | Features | GitHub |
|
||||
|---------|----------------------------------------------------|-------------------------------------------------------------------------------------|
|
||||
| CoolAGI | Code Interpreter, Vision, Mind maps, and much more | [nextgen-user/CoolAGI](https://github.com/nextgen-user/CoolAGI) |
|
||||
| HL-GPT | Fully remodeled UI | [harlanlewis/nextjs-chatgpt-app](https://github.com/harlanlewis/nextjs-chatgpt-app) |
|
||||
- [ ] ✨ [Deploy](docs/installation.md) your [fork](docs/customizations.md) for your friends and family, or [customize it for work](docs/customizations.md)
|
||||
|
||||
<br/>
|
||||
|
||||
# 🧩 Develop
|
||||
|
||||
[//]: # ()
|
||||
|
||||
[//]: # ()
|
||||
|
||||
[//]: # ()
|
||||
|
||||
To download and run this Typescript/React/Next.js project locally, the only prerequisite is Node.js with the `npm` package manager.
|
||||
Clone this repo, install the dependencies (all local), and run the development server (which auto-watches the
|
||||
files for changes):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/enricoros/big-agi.git
|
||||
cd big-agi
|
||||
npm install
|
||||
npm run dev
|
||||
|
||||
# You will see something like:
|
||||
#
|
||||
# ▲ Next.js 14.1.0
|
||||
# - Local: http://localhost:3000
|
||||
# ✓ Ready in 2.6s
|
||||
```
|
||||
|
||||
The development app will be running on `http://localhost:3000`. Development builds have the advantage of not requiring
|
||||
a build step, but can be slower than production builds. Also, development builds won't have timeout on edge functions.
|
||||
|
||||
## 🛠️ Deploy from source
|
||||
|
||||
The _production_ build of the application is optimized for performance and is performed by the `npm run build` command,
|
||||
after installing the required dependencies.
|
||||
|
||||
```bash
|
||||
# .. repeat the steps above up to `npm install`, then:
|
||||
npm run build
|
||||
next start --port 3000
|
||||
```
|
||||
|
||||
The app will be running on the specified port, e.g. `http://localhost:3000`.
|
||||
|
||||
Want to deploy with username/password? See the [Authentication](docs/deploy-authentication.md) guide.
|
||||
|
||||
## 🐳 Deploy with Docker
|
||||
|
||||
For more detailed information on deploying with Docker, please refer to the [docker deployment documentation](docs/deploy-docker.md).
|
||||
|
||||
Build and run:
|
||||
|
||||
```bash
|
||||
docker build -t big-agi .
|
||||
docker run -d -p 3000:3000 big-agi
|
||||
```
|
||||
|
||||
Or run the official container:
|
||||
|
||||
- manually: `docker run -d -p 3000:3000 ghcr.io/enricoros/big-agi`
|
||||
- or, with docker-compose: `docker-compose up` or see [the documentation](docs/deploy-docker.md) for a composer file with integrated browsing
|
||||
|
||||
## ☁️ Deploy on Cloudflare Pages
|
||||
|
||||
Please refer to the [Cloudflare deployment documentation](docs/deploy-cloudflare.md).
|
||||
|
||||
## 🚀 Deploy on Vercel
|
||||
|
||||
Create your GitHub fork, create a Vercel project over that fork, and deploy it. Or press the button below for convenience.
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI&env=OPENAI_API_KEY&envDescription=Backend%20API%20keys%2C%20optional%20and%20may%20be%20overridden%20by%20the%20UI.&envLink=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI%2Fblob%2Fmain%2Fdocs%2Fenvironment-variables.md&project-name=big-AGI)
|
||||
|
||||
|
||||
[//]: # ([](https://github.com/enricoros/big-agi/stargazers))
|
||||
|
||||
[//]: # ([](https://github.com/enricoros/big-agi/network))
|
||||
|
||||
+8
-14
@@ -37,22 +37,16 @@ System integrators, administrators, whitelabelers: instead of using the public b
|
||||
|
||||
Step-by-step deployment and system configuration instructions.
|
||||
|
||||
- **Deploy Your Own**
|
||||
- straightforward: **Local development**, **Vercel 1-Click**
|
||||
- **[Cloudflare Deployment](deploy-cloudflare.md)**
|
||||
- **[Docker Deployment](deploy-docker.md)**: Containers for Local or Cloud deployments
|
||||
- **[Installation](installation.md)**: Set up your own instance of big-AGI and related products
|
||||
- build from source or use pre-built
|
||||
- locally, in the public cloud, or on your own servers
|
||||
|
||||
|
||||
- **Deployment Server Features**
|
||||
- **[Database Setup](deploy-database.md)**: Optional, only required to enable "Chat Link Sharing"
|
||||
- **[Environment Variables](environment-variables.md)**: 📌 Set server-side API keys and special features in your deployments
|
||||
- **[HTTP Basic Authentication](deploy-authentication.md)**: Optional, Secure your big-AGI instance with a username and password
|
||||
|
||||
## Customization & Derivative UIs
|
||||
|
||||
👏 Customize big-AGI to fit your needs.
|
||||
|
||||
- **[Customizing big-AGI](customizations.md)**: how to alter source code and server-side configuration
|
||||
- **Advanced Customizations**:
|
||||
- **[Source code alterations guide](customizations.md)**: source code primer and alterations guidelines
|
||||
- **[Basic Authentication](deploy-authentication.md)**: Optional, adds a username and password wall
|
||||
- **[Database Setup](deploy-database.md)**: Optional, enables "Chat Link Sharing"
|
||||
- **[Environment Variables](environment-variables.md)**: 📌 Pre-configures models and services
|
||||
|
||||
## Support and Community
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ If you have an `API Endpoint` and `API Key`, you can configure big-AGI as follow
|
||||
The deployed models are now available in the application. If you don't have a configured
|
||||
Azure OpenAI service instance, continue with the next section.
|
||||
|
||||
In addition to using the UI, configuration can also be done using
|
||||
[environment variables](environment-variables.md).
|
||||
|
||||
## Setting Up Azure
|
||||
|
||||
### Step 1: Azure Account & Subscription
|
||||
|
||||
@@ -37,6 +37,9 @@ Check the URL and modify if different.
|
||||
2. Enter the API URL: `http://localhost:1234` (modify if different)
|
||||
3. Refresh by clicking on the `Models` button to load models from LM Studio
|
||||
|
||||
In addition to using the UI, configuration can also be done using
|
||||
[environment variables](environment-variables.md).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Missing @mui/material**: Execute `npm install @mui/material` or `yarn add @mui/material`
|
||||
|
||||
@@ -36,6 +36,9 @@ Follow the guide at: https://localai.io/basics/getting_started/
|
||||
- Load the models (click on `Models 🔄`)
|
||||
- Select the model and chat
|
||||
|
||||
In addition to using the UI, configuration can also be done using
|
||||
[environment variables](environment-variables.md).
|
||||
|
||||
### Integration: Models Gallery
|
||||
|
||||
If the running LocalAI instance is configured with a [Model Gallery](https://localai.io/models/):
|
||||
|
||||
@@ -13,7 +13,7 @@ _Last updated Dec 16, 2023_
|
||||
|
||||
1. **Ensure Ollama API Server is Running**: Follow the official instructions to get Ollama up and running on your machine
|
||||
- For detailed instructions on setting up the Ollama API server, please refer to the
|
||||
[Ollama download page](https://ollama.ai/download) and [instructions for linux](https://github.com/jmorganca/ollama/blob/main/docs/linux.md).
|
||||
[Ollama download page](https://ollama.ai/download) and [instructions for linux](https://github.com/jmorganca/ollama/blob/main/docs/linux.md).
|
||||
2. **Add Ollama as a Model Source**: In `big-AGI`, navigate to the **Models** section, select **Add a model source**, and choose **Ollama**
|
||||
3. **Enter Ollama Host URL**: Provide the Ollama Host URL where the API server is accessible (e.g., `http://localhost:11434`)
|
||||
4. **Refresh Model List**: Once connected, refresh the list of available models to include the Ollama models
|
||||
@@ -22,6 +22,9 @@ _Last updated Dec 16, 2023_
|
||||
you'll have to press the 'Pull' button again, until a green message appears.
|
||||
5. **Chat with Ollama models**: select an Ollama model and begin chatting with AI personas
|
||||
|
||||
In addition to using the UI, configuration can also be done using
|
||||
[environment variables](environment-variables.md).
|
||||
|
||||
**Visual Configuration Guide**:
|
||||
|
||||
* After adding the `Ollama` model vendor, entering the IP address of an Ollama server, and refreshing models:<br/>
|
||||
@@ -37,7 +40,7 @@ _Last updated Dec 16, 2023_
|
||||
|
||||
### ⚠️ Network Troubleshooting
|
||||
|
||||
If you get errors about the server having trouble connecting with Ollama, please see
|
||||
If you get errors about the server having trouble connecting with Ollama, please see
|
||||
[this message](https://github.com/enricoros/big-AGI/issues/276#issuecomment-1858591483) on Issue #276.
|
||||
|
||||
And in brief, make sure the Ollama endpoint is accessible from the servers where you run big-AGI (which could
|
||||
@@ -75,7 +78,7 @@ Then, edit the nginx configuration file `/etc/nginx/sites-enabled/default` and a
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
|
||||
# Disable buffering for the streaming responses
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@ This guide assumes that **big-AGI** is already installed on your system. Note th
|
||||
- Stop the Web UI as we need to modify the startup flags to enable the OpenAI API
|
||||
2. Enable the **openai extension**
|
||||
- Edit `CMD_FLAGS.txt`
|
||||
- Make sure that `--listen --api` is present and uncommented
|
||||
- Make sure that `--listen --api` is present and uncommented
|
||||
3. Restart text-generation-webui
|
||||
- Double-click on "start"
|
||||
- You should see something like:
|
||||
- You should see something like:
|
||||
```
|
||||
2023-12-07 21:51:21 INFO:Loading the extension "openai"...
|
||||
2023-12-07 21:51:21 INFO:OpenAI-compatible API URL:
|
||||
|
||||
http://0.0.0.0:5000
|
||||
|
||||
http://0.0.0.0:5000
|
||||
...
|
||||
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
|
||||
Running on local URL: http://0.0.0.0:7860
|
||||
|
||||
@@ -22,6 +22,9 @@ This document details the process of integrating OpenRouter with big-AGI.
|
||||

|
||||
4. OpenAI GPT4-32k and other models will now be accessible and selectable in the application.
|
||||
|
||||
In addition to using the UI, configuration can also be done using
|
||||
[environment variables](environment-variables.md).
|
||||
|
||||
### Pricing
|
||||
|
||||
OpenRouter independently manages its service and pricing and is not affiliated with big-AGI.
|
||||
|
||||
@@ -66,7 +66,7 @@ Test your application thoroughly using local development (refer to README.md for
|
||||
|
||||
We introduced the `/info/debug` page that provides a detailed overview of the application's environment, including the API keys, environment variables, and other configuration settings.
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Community Projects - Share Your Project
|
||||
|
||||
@@ -74,12 +74,12 @@ After deployment, share your project with the community. We will link to your pr
|
||||
|
||||
| Project | Features | GitHub |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
|
||||
| 🚀 CoolAGI: Where AI meets Imagination<br/> | Code Interpreter, Vision, Mind maps, Web Searches, Advanced Data Analytics, Large Data Handling and more! | [nextgen-user/CoolAGI](https://github.com/nextgen-user/CoolAGI) |
|
||||
| HL-GPT | Fully remodeled UI | [harlanlewis/nextjs-chatgpt-app](https://github.com/harlanlewis/nextjs-chatgpt-app) |
|
||||
| 🚀 CoolAGI: Where AI meets Imagination<br/> | Code Interpreter, Vision, Mind maps, Web Searches, Advanced Data Analytics, Large Data Handling and more! | [nextgen-user/CoolAGI](https://github.com/nextgen-user/CoolAGI) |
|
||||
| HL-GPT | Fully remodeled UI | [harlanlewis/nextjs-chatgpt-app](https://github.com/harlanlewis/nextjs-chatgpt-app) |
|
||||
|
||||
For public projects, update your README.md with your modifications and submit a pull request to add your project to our list, aiding in its discovery.
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Best Practices
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ As of Feb 27, 2024, this feature is in development.
|
||||
|
||||
## Configurations
|
||||
|
||||
| Scope | Default | Description / Instructions |
|
||||
| Scope | Default | Description / Instructions |
|
||||
|-----------------------------------------------------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| Your source builds of big-AGI | None | **Vercel**: enable Vercel Analytics from the dashboard. · **Google Analytics**: set environment variable at build time. |
|
||||
| Your docker builds of big-AGI | None | **Vercel**: n/a. · **Google Analytics**: set environment variable at `docker build` time. |
|
||||
|
||||
@@ -9,7 +9,7 @@ Docker ensures faster development cycles, easier collaboration, and seamless env
|
||||
```bash
|
||||
git clone https://github.com/enricoros/big-agi.git
|
||||
cd big-agi
|
||||
```
|
||||
```
|
||||
2. **Build the Docker Image**: Build a local docker image from the provided Dockerfile:
|
||||
```bash
|
||||
docker build -t big-agi .
|
||||
|
||||
@@ -91,7 +91,7 @@ requiring the user to enter an API key
|
||||
| `ANTHROPIC_API_HOST` | Changes the backend host for the Anthropic vendor, to enable platforms such as [config-aws-bedrock.md](config-aws-bedrock.md) | Optional |
|
||||
| `GEMINI_API_KEY` | The API key for Google AI's Gemini | Optional |
|
||||
| `GROQ_API_KEY` | The API key for Groq Cloud | Optional |
|
||||
| `LOCALAI_API_HOST` | Sets the URL of the LocalAI server, or defaults to http://127.0.0.1:8080 | Optional |
|
||||
| `LOCALAI_API_HOST` | Sets the URL of the LocalAI server, or defaults to http://127.0.0.1:8080 | Optional |
|
||||
| `LOCALAI_API_KEY` | The (Optional) API key for LocalAI | Optional |
|
||||
| `MISTRAL_API_KEY` | The API key for Mistral | Optional |
|
||||
| `OLLAMA_API_HOST` | Changes the backend host for the Ollama vendor. See [config-local-ollama.md](config-local-ollama) | |
|
||||
@@ -128,7 +128,7 @@ Enable the app to Talk, Draw, and Google things up.
|
||||
| `GOOGLE_CSE_ID` | Google Custom/Programmable Search Engine ID - [Link to PSE](https://programmablesearchengine.google.com/) |
|
||||
| **Browse** | |
|
||||
| `PUPPETEER_WSS_ENDPOINT` | Puppeteer WebSocket endpoint - used for browsing (pade downloadeing), etc. |
|
||||
| **Backend** | |
|
||||
| **Backend** | |
|
||||
| `BACKEND_ANALYTICS` | Semicolon-separated list of analytics flags (see backend.analytics.ts). Flags: `domain` logs the responding domain. |
|
||||
| `HTTP_BASIC_AUTH_USERNAME` | See the [Authentication](deploy-authentication.md) guide. Username for HTTP Basic Authentication. |
|
||||
| `HTTP_BASIC_AUTH_PASSWORD` | Password for HTTP Basic Authentication. |
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# Installation Guide
|
||||
|
||||
Welcome to the big-AGI Installation Guide - Whether you're a developer
|
||||
eager to explore, a system integrator, or an enterprise looking for a
|
||||
white-label solution, this comprehensive guide ensures a smooth setup
|
||||
process for your own instance of big-AGI and related products.
|
||||
|
||||
**Try big-AGI** - You don't need to install anything if you want to play with big-AGI
|
||||
and have your API keys to various model services. You can access our free instance on [big-AGI.com](https://big-agi.com).
|
||||
The free instance runs the latest `main-stable` branch from this repository.
|
||||
|
||||
## 🧩 Build-your-own
|
||||
|
||||
If you want to change the code, have a deeper configuration,
|
||||
add your own models, or run your own instance, follow the steps below.
|
||||
|
||||
### Local Development
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Node.js and npm installed on your machine.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Clone the big-AGI repository:
|
||||
```bash
|
||||
git clone https://github.com/enricoros/big-AGI.git
|
||||
cd big-AGI
|
||||
```
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
3. Run the development server:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
Your big-AGI instance is now running at `http://localhost:3000`.
|
||||
|
||||
### Local Production build
|
||||
|
||||
The production build is optimized for performance and follows
|
||||
the same steps 1 and 2 as for [local development](#local-development).
|
||||
|
||||
3. Build the production version:
|
||||
```bash
|
||||
# .. repeat the steps above up to `npm install`, then:
|
||||
npm run build
|
||||
```
|
||||
4. Start the production server:
|
||||
```bash
|
||||
next start --port 3000
|
||||
```
|
||||
Your big-AGI production instance is on `http://localhost:3000`.
|
||||
|
||||
### Advanced Customization
|
||||
|
||||
Want to pre-enable models, customize the interface, or deploy with username/password or alter code to your needs?
|
||||
Check out the [Customizations Guide](README.md) for detailed instructions.
|
||||
|
||||
## ☁️ Cloud Deployment Options
|
||||
|
||||
To deploy big-AGI on a public server, you have several options. Choose the one that best fits your needs.
|
||||
|
||||
### Deploy on Vercel
|
||||
|
||||
Install big-AGI on Vercel with just a few clicks.
|
||||
|
||||
Create your GitHub fork, create a Vercel project over that fork, and deploy it. Or press the button below for convenience.
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI&env=OPENAI_API_KEY&envDescription=Backend%20API%20keys%2C%20optional%20and%20may%20be%20overridden%20by%20the%20UI.&envLink=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-AGI%2Fblob%2Fmain%2Fdocs%2Fenvironment-variables.md&project-name=big-AGI)
|
||||
|
||||
### Deploy on Cloudflare
|
||||
|
||||
Deploy on Cloudflare's global network by installing big-AGI on
|
||||
Cloudflare Pages. Check out the [Cloudflare Installation Guide](deploy-cloudflare.md)
|
||||
for step-by-step instructions.
|
||||
|
||||
### Docker Deployments
|
||||
|
||||
Containerize your big-AGI installation using Docker for portability and scalability.
|
||||
Our [Docker Deployment Guide](deploy-docker.md) will walk you through the process,
|
||||
or follow the steps below for a quick start.
|
||||
|
||||
1. (optional) Build the Docker image - if you do not want to use the [pre-built Docker images](https://github.com/enricoros/big-AGI/pkgs/container/big-agi):
|
||||
```bash
|
||||
docker build -t big-agi .
|
||||
```
|
||||
2. Run the Docker container with either:
|
||||
```bash
|
||||
# 2A. if you built the image yourself:
|
||||
docker run -d -p 3000:3000 big-agi
|
||||
|
||||
# 2B. or use the pre-built image:
|
||||
docker run -d -p 3000:3000 ghcr.io/enricoros/big-agi
|
||||
|
||||
# 2C. or use docker-compose:
|
||||
docker-compose up
|
||||
```
|
||||
Access your big-AGI instance at `http://localhost:3000`.
|
||||
|
||||
### Midori AI Subsystem for Docker Deployment
|
||||
|
||||
Follow the instructions found on [Midori AI Subsystem Site](https://io.midori-ai.xyz/subsystem/manager/)
|
||||
for your host OS. After completing the setup process, install the Big-AGI docker backend to the Midori AI Subsystem.
|
||||
|
||||
## Enterprise-Grade Installation
|
||||
|
||||
For businesses seeking a fully-managed, scalable solution, consider our managed installations.
|
||||
Enjoy all the features of big-AGI without the hassle of infrastructure management. [hello@big-agi.com](mailto:hello@big-agi.com) to learn more.
|
||||
|
||||
## Support
|
||||
|
||||
Join our vibrant community of developers, researchers, and AI enthusiasts. Share your projects, get help, and collaborate with others.
|
||||
|
||||
- [Discord Community](https://discord.gg/MkH4qj2Jp9)
|
||||
- [Twitter](https://twitter.com/yourusername)
|
||||
|
||||
For any questions or inquiries, please don't hesitate to [reach out to our team](mailto:hello@big-agi.com).
|
||||
Generated
+236
-250
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"name": "big-agi",
|
||||
"version": "1.15.0",
|
||||
"version": "1.15.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "big-agi",
|
||||
"version": "1.15.0",
|
||||
"version": "1.15.1",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.14",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.15",
|
||||
"@mui/joy": "^5.0.0-beta.32",
|
||||
"@next/bundle-analyzer": "^14.1.4",
|
||||
"@next/third-parties": "^14.1.4",
|
||||
"@prisma/client": "^5.11.0",
|
||||
"@next/third-parties": "^14.2.0-canary.60",
|
||||
"@prisma/client": "^5.12.1",
|
||||
"@sanity/diff-match-patch": "^3.1.1",
|
||||
"@t3-oss/env-nextjs": "^0.9.2",
|
||||
"@tanstack/react-query": "~4.36.1",
|
||||
@@ -42,10 +42,10 @@
|
||||
"react-katex": "^3.0.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-player": "^2.15.1",
|
||||
"react-resizable-panels": "^2.0.13",
|
||||
"react-resizable-panels": "^2.0.16",
|
||||
"react-timeago": "^7.2.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sharp": "^0.33.2",
|
||||
"sharp": "^0.33.3",
|
||||
"superjson": "^2.2.1",
|
||||
"tesseract.js": "^5.0.5",
|
||||
"tiktoken": "^1.0.13",
|
||||
@@ -55,22 +55,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/puppeteer": "0.0.5",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/node": "^20.12.5",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/plantuml-encoder": "^1.4.2",
|
||||
"@types/prismjs": "^1.26.3",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react": "^18.2.74",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-csv": "^1.1.10",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.24",
|
||||
"@types/react-katex": "^3.0.4",
|
||||
"@types/react-timeago": "^4.1.7",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^14.1.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prisma": "^5.11.0",
|
||||
"typescript": "^5.4.3"
|
||||
"prisma": "^5.12.1",
|
||||
"typescript": "^5.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.0.0 || ^18.0.0"
|
||||
@@ -203,9 +203,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
|
||||
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
|
||||
"version": "7.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
|
||||
"integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -252,9 +252,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "0.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz",
|
||||
"integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz",
|
||||
"integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
@@ -332,9 +332,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
|
||||
"integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
|
||||
"integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.1",
|
||||
"@emotion/memoize": "^0.8.1",
|
||||
@@ -368,14 +368,14 @@
|
||||
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
|
||||
},
|
||||
"node_modules/@emotion/styled": {
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
|
||||
"integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
|
||||
"version": "11.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
|
||||
"integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.11.0",
|
||||
"@emotion/is-prop-valid": "^1.2.1",
|
||||
"@emotion/serialize": "^1.1.2",
|
||||
"@emotion/is-prop-valid": "^1.2.2",
|
||||
"@emotion/serialize": "^1.1.4",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
|
||||
"@emotion/utils": "^1.2.1"
|
||||
},
|
||||
@@ -530,15 +530,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
|
||||
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
||||
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz",
|
||||
"integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz",
|
||||
"integrity": "sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -557,13 +557,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.1"
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz",
|
||||
"integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz",
|
||||
"integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -582,13 +582,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.1"
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz",
|
||||
"integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -607,9 +607,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz",
|
||||
"integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz",
|
||||
"integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -628,9 +628,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz",
|
||||
"integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz",
|
||||
"integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -649,9 +649,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz",
|
||||
"integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -670,9 +670,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz",
|
||||
"integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz",
|
||||
"integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -691,9 +691,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz",
|
||||
"integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz",
|
||||
"integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -712,9 +712,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz",
|
||||
"integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -733,9 +733,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz",
|
||||
"integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz",
|
||||
"integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -754,9 +754,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz",
|
||||
"integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz",
|
||||
"integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -775,13 +775,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.0.1"
|
||||
"@img/sharp-libvips-linux-arm": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz",
|
||||
"integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz",
|
||||
"integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -800,13 +800,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.1"
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz",
|
||||
"integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.3.tgz",
|
||||
"integrity": "sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -825,13 +825,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.1"
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz",
|
||||
"integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz",
|
||||
"integrity": "sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -850,13 +850,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.0.1"
|
||||
"@img/sharp-libvips-linux-x64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz",
|
||||
"integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.3.tgz",
|
||||
"integrity": "sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -875,13 +875,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.1"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz",
|
||||
"integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz",
|
||||
"integrity": "sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -900,19 +900,19 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.1"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz",
|
||||
"integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz",
|
||||
"integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^0.45.0"
|
||||
"@emnapi/runtime": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||
@@ -925,9 +925,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz",
|
||||
"integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz",
|
||||
"integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -946,9 +946,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz",
|
||||
"integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz",
|
||||
"integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1062,18 +1062,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "5.15.14",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz",
|
||||
"integrity": "sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==",
|
||||
"version": "5.15.15",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
|
||||
"integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/icons-material": {
|
||||
"version": "5.15.14",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.14.tgz",
|
||||
"integrity": "sha512-vj/51k7MdFmt+XVw94sl30SCvGx6+wJLsNYjZRgxhS6y3UtnWnypMOsm3Kmg8TN+P0dqwsjy4/fX7B1HufJIhw==",
|
||||
"version": "5.15.15",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.15.tgz",
|
||||
"integrity": "sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9"
|
||||
},
|
||||
@@ -1136,15 +1136,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "5.15.14",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.14.tgz",
|
||||
"integrity": "sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==",
|
||||
"version": "5.15.15",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
|
||||
"integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@mui/base": "5.0.0-beta.40",
|
||||
"@mui/core-downloads-tracker": "^5.15.14",
|
||||
"@mui/system": "^5.15.14",
|
||||
"@mui/core-downloads-tracker": "^5.15.15",
|
||||
"@mui/system": "^5.15.15",
|
||||
"@mui/types": "^7.2.14",
|
||||
"@mui/utils": "^5.15.14",
|
||||
"@types/react-transition-group": "^4.4.10",
|
||||
@@ -1238,9 +1238,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "5.15.14",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.14.tgz",
|
||||
"integrity": "sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==",
|
||||
"version": "5.15.15",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
|
||||
"integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@mui/private-theming": "^5.15.14",
|
||||
@@ -1474,9 +1474,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/third-parties": {
|
||||
"version": "14.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-14.1.4.tgz",
|
||||
"integrity": "sha512-e/kpEFq5/ZPhHkxpJkvhMfLp3OC6KCBou0/BV8BvgjXAPgEo6sa0ZXe0l4ZEb3wcA7NIEHIpqCmjU5Z7QAHcKQ==",
|
||||
"version": "14.2.0-canary.60",
|
||||
"resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-14.2.0-canary.60.tgz",
|
||||
"integrity": "sha512-Y/B3WxgsDkaDmSKGs/C9N/DwVYXAA0RtDOcCCGCwS+anpzcfD3wJBC6Ms9C1ieP1V65HN2NdvA2beGGJYPARKA==",
|
||||
"dependencies": {
|
||||
"third-party-capital": "1.0.20"
|
||||
},
|
||||
@@ -1545,9 +1545,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.11.0.tgz",
|
||||
"integrity": "sha512-SWshvS5FDXvgJKM/a0y9nDC1rqd7KG0Q6ZVzd+U7ZXK5soe73DJxJJgbNBt2GNXOa+ysWB4suTpdK5zfFPhwiw==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.12.1.tgz",
|
||||
"integrity": "sha512-6/JnizEdlSBxDIdiLbrBdMW5NqDxOmhXAJaNXiPpgzAPr/nLZResT6MMpbOHLo5yAbQ1Vv5UU8PTPRzb0WIxdA==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
@@ -1562,54 +1562,54 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.11.0.tgz",
|
||||
"integrity": "sha512-N6yYr3AbQqaiUg+OgjkdPp3KPW1vMTAgtKX6+BiB/qB2i1TjLYCrweKcUjzOoRM5BriA4idrkTej9A9QqTfl3A==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.12.1.tgz",
|
||||
"integrity": "sha512-kd/wNsR0klrv79o1ITsbWxYyh4QWuBidvxsXSParPsYSu0ircUmNk3q4ojsgNc3/81b0ozg76iastOG43tbf8A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.11.0.tgz",
|
||||
"integrity": "sha512-gbrpQoBTYWXDRqD+iTYMirDlF9MMlQdxskQXbhARhG6A/uFQjB7DZMYocMQLoiZXO/IskfDOZpPoZE8TBQKtEw==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.12.1.tgz",
|
||||
"integrity": "sha512-HQDdglLw2bZR/TXD2Y+YfDMvi5Q8H+acbswqOsWyq9pPjBLYJ6gzM+ptlTU/AV6tl0XSZLU1/7F4qaWa8bqpJA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.11.0",
|
||||
"@prisma/engines-version": "5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102",
|
||||
"@prisma/fetch-engine": "5.11.0",
|
||||
"@prisma/get-platform": "5.11.0"
|
||||
"@prisma/debug": "5.12.1",
|
||||
"@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
|
||||
"@prisma/fetch-engine": "5.12.1",
|
||||
"@prisma/get-platform": "5.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102.tgz",
|
||||
"integrity": "sha512-WXCuyoymvrS4zLz4wQagSsc3/nE6CHy8znyiMv8RKazKymOMd5o9FP5RGwGHAtgoxd+aB/BWqxuP/Ckfu7/3MA==",
|
||||
"version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab.tgz",
|
||||
"integrity": "sha512-6yvO8s80Tym61aB4QNtYZfWVmE3pwqe807jEtzm8C5VDe7nw8O1FGX3TXUaXmWV0fQTIAfRbeL2Gwrndabp/0g==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.11.0.tgz",
|
||||
"integrity": "sha512-994viazmHTJ1ymzvWugXod7dZ42T2ROeFuH6zHPcUfp/69+6cl5r9u3NFb6bW8lLdNjwLYEVPeu3hWzxpZeC0w==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.12.1.tgz",
|
||||
"integrity": "sha512-qSs3KcX1HKcea1A+hlJVK/ljj0PNIUHDxAayGMvgJBqmaN32P9tCidlKz1EGv6WoRFICYnk3Dd/YFLBwnFIozA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.11.0",
|
||||
"@prisma/engines-version": "5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102",
|
||||
"@prisma/get-platform": "5.11.0"
|
||||
"@prisma/debug": "5.12.1",
|
||||
"@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
|
||||
"@prisma/get-platform": "5.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.11.0.tgz",
|
||||
"integrity": "sha512-rxtHpMLxNTHxqWuGOLzR2QOyQi79rK1u1XYAVLZxDGTLz/A+uoDnjz9veBFlicrpWjwuieM4N6jcnjj/DDoidw==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.12.1.tgz",
|
||||
"integrity": "sha512-pgIR+pSvhYHiUcqXVEZS31NrFOTENC9yFUdEAcx7cdQBoZPmHVjtjN4Ss6NzVDMYPrKJJ51U14EhEoeuBlMioQ==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.11.0"
|
||||
"@prisma/debug": "5.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.8.0.tgz",
|
||||
"integrity": "sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==",
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",
|
||||
"integrity": "sha512-S3Kq8e7LqxkA9s7HKLqXGTGck1uwis5vAXan3FnU5yw1Ec5hsSGnq4s/UCaSqABPOnOTg7zASLyst7+ohgWexg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sanity/diff-match-patch": {
|
||||
@@ -1809,9 +1809,9 @@
|
||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||
"version": "20.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz",
|
||||
"integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
@@ -1844,17 +1844,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.67",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.67.tgz",
|
||||
"integrity": "sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw==",
|
||||
"version": "18.2.74",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz",
|
||||
"integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
@@ -1877,9 +1876,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz",
|
||||
"integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==",
|
||||
"version": "18.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.24.tgz",
|
||||
"integrity": "sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
@@ -1923,11 +1922,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
|
||||
@@ -2580,9 +2574,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001600",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
|
||||
"integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
|
||||
"version": "1.0.30001606",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz",
|
||||
"integrity": "sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -3149,9 +3143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz",
|
||||
"integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==",
|
||||
"version": "1.23.3",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
|
||||
"integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-buffer-byte-length": "^1.0.1",
|
||||
@@ -3193,11 +3187,11 @@
|
||||
"safe-regex-test": "^1.0.3",
|
||||
"string.prototype.trim": "^1.2.9",
|
||||
"string.prototype.trimend": "^1.0.8",
|
||||
"string.prototype.trimstart": "^1.0.7",
|
||||
"string.prototype.trimstart": "^1.0.8",
|
||||
"typed-array-buffer": "^1.0.2",
|
||||
"typed-array-byte-length": "^1.0.1",
|
||||
"typed-array-byte-offset": "^1.0.2",
|
||||
"typed-array-length": "^1.0.5",
|
||||
"typed-array-length": "^1.0.6",
|
||||
"unbox-primitive": "^1.0.2",
|
||||
"which-typed-array": "^1.1.15"
|
||||
},
|
||||
@@ -4108,9 +4102,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
|
||||
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
@@ -4442,9 +4436,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/inline-style-parser": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz",
|
||||
"integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ=="
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz",
|
||||
"integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g=="
|
||||
},
|
||||
"node_modules/internal-slot": {
|
||||
"version": "1.0.7",
|
||||
@@ -5025,9 +5019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.9",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz",
|
||||
"integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==",
|
||||
"version": "0.16.10",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
|
||||
"integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
@@ -6316,13 +6310,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/object.hasown": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz",
|
||||
"integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz",
|
||||
"integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-properties": "^1.2.0",
|
||||
"es-abstract": "^1.22.1"
|
||||
"define-properties": "^1.2.1",
|
||||
"es-abstract": "^1.23.2",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -6502,12 +6500,12 @@
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
|
||||
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
|
||||
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6525,25 +6523,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path2d": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d/-/path2d-0.1.1.tgz",
|
||||
"integrity": "sha512-/+S03c8AGsDYKKBtRDqieTJv2GlkMb0bWjnqOgtF6MkjdUQ9a8ARAtxWf9NgKLGm2+WQr6+/tqJdU8HNGsIDoA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/path2d-polyfill": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.1.1.tgz",
|
||||
"integrity": "sha512-4Rka5lN+rY/p0CdD8+E+BFv51lFaFvJOrlOhyQ+zjzyQrzyh3ozmxd1vVGGDdIbUFSBtIZLSnspxTgPT0iJhvA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
|
||||
"integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"path2d": "0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pdfjs-dist": {
|
||||
@@ -6641,13 +6627,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.11.0.tgz",
|
||||
"integrity": "sha512-KCLiug2cs0Je7kGkQBN9jDWoZ90ogE/kvZTUTgz2h94FEo8pczCkPH7fPNXkD1sGU7Yh65risGGD1HQ5DF3r3g==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.12.1.tgz",
|
||||
"integrity": "sha512-SkMnb6wyIxTv9ACqiHBI2u9gD6y98qXRoCoLEnZsF6yee5Qg828G+ARrESN+lQHdw4maSZFFSBPPDpvSiVTo0Q==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "5.11.0"
|
||||
"@prisma/engines": "5.12.1"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js"
|
||||
@@ -6685,9 +6671,9 @@
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz",
|
||||
"integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
|
||||
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
@@ -6875,9 +6861,9 @@
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
},
|
||||
"node_modules/react-resizable-panels": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.0.13.tgz",
|
||||
"integrity": "sha512-DZxLv5Pc6rfuqkgQ+2JW3eLPiX3BgAAR38Cd0lXuCVHXEZLrD+3W4Nag5TqCoNgEM/4IUU9L/pLM2toOUOd6UQ==",
|
||||
"version": "2.0.16",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.0.16.tgz",
|
||||
"integrity": "sha512-UrnxmTZaTnbCl/xIOX38ig35RicqGfLuqt2x5fytpNlQvCRuxyXZwIBEhmF+pmrEGxfajyXFBoCplNxLvhF0CQ==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
|
||||
@@ -7279,42 +7265,42 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz",
|
||||
"integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==",
|
||||
"version": "0.33.3",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.3.tgz",
|
||||
"integrity": "sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.2",
|
||||
"semver": "^7.5.4"
|
||||
"detect-libc": "^2.0.3",
|
||||
"semver": "^7.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"libvips": ">=8.15.1",
|
||||
"libvips": ">=8.15.2",
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.33.2",
|
||||
"@img/sharp-darwin-x64": "0.33.2",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.1",
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.1",
|
||||
"@img/sharp-libvips-linux-arm": "1.0.1",
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.1",
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.1",
|
||||
"@img/sharp-libvips-linux-x64": "1.0.1",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.1",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.1",
|
||||
"@img/sharp-linux-arm": "0.33.2",
|
||||
"@img/sharp-linux-arm64": "0.33.2",
|
||||
"@img/sharp-linux-s390x": "0.33.2",
|
||||
"@img/sharp-linux-x64": "0.33.2",
|
||||
"@img/sharp-linuxmusl-arm64": "0.33.2",
|
||||
"@img/sharp-linuxmusl-x64": "0.33.2",
|
||||
"@img/sharp-wasm32": "0.33.2",
|
||||
"@img/sharp-win32-ia32": "0.33.2",
|
||||
"@img/sharp-win32-x64": "0.33.2"
|
||||
"@img/sharp-darwin-arm64": "0.33.3",
|
||||
"@img/sharp-darwin-x64": "0.33.3",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.2",
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.2",
|
||||
"@img/sharp-libvips-linux-arm": "1.0.2",
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.2",
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.2",
|
||||
"@img/sharp-libvips-linux-x64": "1.0.2",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.2",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.2",
|
||||
"@img/sharp-linux-arm": "0.33.3",
|
||||
"@img/sharp-linux-arm64": "0.33.3",
|
||||
"@img/sharp-linux-s390x": "0.33.3",
|
||||
"@img/sharp-linux-x64": "0.33.3",
|
||||
"@img/sharp-linuxmusl-arm64": "0.33.3",
|
||||
"@img/sharp-linuxmusl-x64": "0.33.3",
|
||||
"@img/sharp-wasm32": "0.33.3",
|
||||
"@img/sharp-win32-ia32": "0.33.3",
|
||||
"@img/sharp-win32-x64": "0.33.3"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
@@ -7622,9 +7608,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/stringify-entities": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz",
|
||||
"integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
|
||||
"integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
|
||||
"dependencies": {
|
||||
"character-entities-html4": "^2.0.0",
|
||||
"character-entities-legacy": "^3.0.0"
|
||||
@@ -7681,11 +7667,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-object": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz",
|
||||
"integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz",
|
||||
"integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==",
|
||||
"dependencies": {
|
||||
"inline-style-parser": "0.2.2"
|
||||
"inline-style-parser": "0.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-jsx": {
|
||||
@@ -7803,9 +7789,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tesseract.js-core": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.0.0.tgz",
|
||||
"integrity": "sha512-lJur5LzjinW5VYMKlVNnBU2JPLpO+A9VqAYBeuV+ZgH0hKvsnm+536Yyp+/zRTBdLe7D6Kok0FN9g+TE4J8qGA=="
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.1.0.tgz",
|
||||
"integrity": "sha512-D4gc5ET1DF/sDayF/eVmHgVGo7nqVC2e3d7uVgVOSAk4NOcmUqvJRTj8etqEmI/2390ZkXCRiDMxTD1RFYyp1g=="
|
||||
},
|
||||
"node_modules/tesseract.js/node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
@@ -8025,9 +8011,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
|
||||
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
|
||||
"integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "big-agi",
|
||||
"version": "1.15.0",
|
||||
"version": "1.15.1",
|
||||
"private": true,
|
||||
"author": "Enrico Ros <enrico.ros@gmail.com>",
|
||||
"repository": "https://github.com/enricoros/big-agi",
|
||||
@@ -21,12 +21,12 @@
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.14",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.15",
|
||||
"@mui/joy": "^5.0.0-beta.32",
|
||||
"@next/bundle-analyzer": "^14.1.4",
|
||||
"@next/third-parties": "^14.1.4",
|
||||
"@prisma/client": "^5.11.0",
|
||||
"@next/third-parties": "^14.2.0-canary.60",
|
||||
"@prisma/client": "^5.12.1",
|
||||
"@sanity/diff-match-patch": "^3.1.1",
|
||||
"@t3-oss/env-nextjs": "^0.9.2",
|
||||
"@tanstack/react-query": "~4.36.1",
|
||||
@@ -51,10 +51,10 @@
|
||||
"react-katex": "^3.0.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-player": "^2.15.1",
|
||||
"react-resizable-panels": "^2.0.13",
|
||||
"react-resizable-panels": "^2.0.16",
|
||||
"react-timeago": "^7.2.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sharp": "^0.33.2",
|
||||
"sharp": "^0.33.3",
|
||||
"superjson": "^2.2.1",
|
||||
"tesseract.js": "^5.0.5",
|
||||
"tiktoken": "^1.0.13",
|
||||
@@ -64,22 +64,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/puppeteer": "0.0.5",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/node": "^20.12.5",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/plantuml-encoder": "^1.4.2",
|
||||
"@types/prismjs": "^1.26.3",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react": "^18.2.74",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-csv": "^1.1.10",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.24",
|
||||
"@types/react-katex": "^3.0.4",
|
||||
"@types/react-timeago": "^4.1.7",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^14.1.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prisma": "^5.11.0",
|
||||
"typescript": "^5.4.3"
|
||||
"prisma": "^5.12.1",
|
||||
"typescript": "^5.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.0.0 || ^18.0.0"
|
||||
|
||||
@@ -32,6 +32,7 @@ import { useUXLabsStore } from '~/common/state/store-ux-labs';
|
||||
// utils access
|
||||
import { clientHostName, isChromeDesktop, isFirefox, isIPhoneUser, isMacUser, isPwa, isVercelFromFrontend } from '~/common/util/pwaUtils';
|
||||
import { getGA4MeasurementId } from '~/common/components/GoogleAnalytics';
|
||||
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
|
||||
import { supportsClipboardRead } from '~/common/util/clipboardUtils';
|
||||
import { supportsScreenCapture } from '~/common/util/screenCaptureUtils';
|
||||
|
||||
@@ -127,7 +128,7 @@ function AppDebug() {
|
||||
const handleDownload = async () => {
|
||||
fileSave(
|
||||
new Blob([JSON.stringify({ client: cClient, agi: cProduct, backend: cBackend }, null, 2)], { type: 'application/json' }),
|
||||
{ fileName: `big-agi-debug-${new Date().toISOString().replace(/:/g, '-')}.json`, extensions: ['.json'] },
|
||||
{ fileName: `big-agi_debug_${prettyTimestampForFilenames()}.json`, extensions: ['.json'] },
|
||||
)
|
||||
.then(() => setSaved(true))
|
||||
.catch(e => console.error('Error saving debug.json', e));
|
||||
|
||||
+19
-1
@@ -3,9 +3,16 @@
|
||||
"short_name": "big-AGI",
|
||||
"theme_color": "#32383E",
|
||||
"background_color": "#9FA6AD",
|
||||
"description": "Personal AGI App",
|
||||
"description": "Your Generative AI Suite",
|
||||
"categories": [
|
||||
"productivity",
|
||||
"AI",
|
||||
"tool",
|
||||
"utilities"
|
||||
],
|
||||
"display": "standalone",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192x192.png",
|
||||
@@ -24,6 +31,17 @@
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"file_handlers": [
|
||||
{
|
||||
"action": "/link/share_target",
|
||||
"accept": {
|
||||
"application/big-agi": [
|
||||
".agi",
|
||||
".agi.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"share_target": {
|
||||
"action": "/link/share_target",
|
||||
"method": "GET",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTheme } from '@mui/joy';
|
||||
import { DiagramConfig, DiagramsModal } from '~/modules/aifn/digrams/DiagramsModal';
|
||||
import { FlattenerModal } from '~/modules/aifn/flatten/FlattenerModal';
|
||||
import { TradeConfig, TradeModal } from '~/modules/trade/TradeModal';
|
||||
import { downloadConversation, openAndLoadConversations } from '~/modules/trade/trade.client';
|
||||
import { getChatLLMId, useChatLLM } from '~/modules/llms/store-llms';
|
||||
import { imaginePromptFromText } from '~/modules/aifn/imagine/imaginePromptFromText';
|
||||
import { speakText } from '~/modules/elevenlabs/elevenlabs.client';
|
||||
@@ -398,6 +399,32 @@ export function AppChat() {
|
||||
setTradeConfig({ dir: 'export', conversationId, exportAll });
|
||||
}, []);
|
||||
|
||||
const handleFileOpenConversation = React.useCallback(() => {
|
||||
openAndLoadConversations(true)
|
||||
.then((outcome) => {
|
||||
// activate the last (most recent) imported conversation
|
||||
if (outcome?.activateConversationId) {
|
||||
showNextTitleChange.current = true;
|
||||
handleOpenConversationInFocusedPane(outcome.activateConversationId);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
addSnackbar({ key: 'chat-import-fail', message: 'Could not open the file.', type: 'issue' });
|
||||
});
|
||||
}, [handleOpenConversationInFocusedPane]);
|
||||
|
||||
const handleFileSaveConversation = React.useCallback((conversationId: DConversationId | null) => {
|
||||
const conversation = getConversation(conversationId);
|
||||
conversation && downloadConversation(conversation, 'json')
|
||||
.then(() => {
|
||||
addSnackbar({ key: 'chat-save-as-ok', message: 'File saved.', type: 'success' });
|
||||
})
|
||||
.catch((err: any) => {
|
||||
if (err?.name !== 'AbortError')
|
||||
addSnackbar({ key: 'chat-save-as-fail', message: `Could not save the file. ${err?.message || ''}`, type: 'issue' });
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleConversationBranch = React.useCallback((srcConversationId: DConversationId, messageId: string | null): DConversationId | null => {
|
||||
// clone data
|
||||
const branchedConversationId = branchConversation(srcConversationId, messageId);
|
||||
@@ -458,6 +485,8 @@ export function AppChat() {
|
||||
['b', true, true, false, handleMessageBeamLastInFocusedPane],
|
||||
['r', true, true, false, handleMessageRegenerateLastInFocusedPane],
|
||||
['n', true, false, true, handleConversationNewInFocusedPane],
|
||||
['o', true, false, false, handleFileOpenConversation],
|
||||
['s', true, false, false, () => handleFileSaveConversation(focusedPaneConversationId)],
|
||||
['b', true, false, true, () => isFocusedChatEmpty || (focusedPaneConversationId && handleConversationBranch(focusedPaneConversationId, null))],
|
||||
['x', true, false, true, () => isFocusedChatEmpty || (focusedPaneConversationId && handleConversationClear(focusedPaneConversationId))],
|
||||
['d', true, false, true, () => focusedPaneConversationId && handleDeleteConversations([focusedPaneConversationId], false)],
|
||||
@@ -467,7 +496,7 @@ export function AppChat() {
|
||||
['o', true, true, false, handleOpenChatLlmOptions],
|
||||
['+', true, true, false, useUIPreferencesStore.getState().increaseContentScaling],
|
||||
['-', true, true, false, useUIPreferencesStore.getState().decreaseContentScaling],
|
||||
], [focusedPaneConversationId, handleConversationBranch, handleConversationClear, handleConversationNewInFocusedPane, handleDeleteConversations, handleMessageBeamLastInFocusedPane, handleMessageRegenerateLastInFocusedPane, handleNavigateHistoryInFocusedPane, handleOpenChatLlmOptions, isFocusedChatEmpty]);
|
||||
], [focusedPaneConversationId, handleConversationBranch, handleConversationClear, handleConversationNewInFocusedPane, handleFileOpenConversation, handleFileSaveConversation, handleDeleteConversations, handleMessageBeamLastInFocusedPane, handleMessageRegenerateLastInFocusedPane, handleNavigateHistoryInFocusedPane, handleOpenChatLlmOptions, isFocusedChatEmpty]);
|
||||
useGlobalShortcuts(shortcuts);
|
||||
|
||||
|
||||
|
||||
@@ -678,20 +678,32 @@ export function Composer(props: {
|
||||
{/* overlay: Mic */}
|
||||
{micIsRunning && (
|
||||
<Card
|
||||
color='primary' variant='soft' invertedColors
|
||||
color='primary' variant='soft'
|
||||
sx={{
|
||||
display: 'flex',
|
||||
position: 'absolute', bottom: 0, left: 0, right: 0, top: 0,
|
||||
// alignItems: 'center', justifyContent: 'center',
|
||||
border: '1px solid',
|
||||
borderColor: 'primary.solidBg',
|
||||
borderRadius: 'sm',
|
||||
zIndex: zIndexComposerOverlayMic,
|
||||
px: 1.5, py: 1,
|
||||
pl: 1.5,
|
||||
pr: { xs: 1.5, md: 5 },
|
||||
py: 0.625,
|
||||
overflow: 'auto',
|
||||
}}>
|
||||
<Typography sx={{
|
||||
color: 'primary.softColor',
|
||||
lineHeight: lineHeightTextareaMd,
|
||||
'& .interim': {
|
||||
textDecoration: 'underline',
|
||||
textDecorationThickness: '0.25em',
|
||||
textDecorationColor: 'rgba(var(--joy-palette-primary-mainChannel) / 0.1)',
|
||||
textDecorationSkipInk: 'none',
|
||||
textUnderlineOffset: '0.25em',
|
||||
},
|
||||
}}>
|
||||
<Typography>
|
||||
{speechInterimResult.transcript}{' '}
|
||||
<span style={{ opacity: 0.8 }}>{speechInterimResult.interimTranscript}</span>
|
||||
<span className={speechInterimResult.interimTranscript !== 'Listening...' ? 'interim' : undefined}>{speechInterimResult.interimTranscript}</span>
|
||||
</Typography>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { StaticImageData } from 'next/image';
|
||||
|
||||
import { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Chip, SvgIconProps, Typography } from '@mui/joy';
|
||||
import AutoStoriesOutlinedIcon from '@mui/icons-material/AutoStoriesOutlined';
|
||||
import GoogleIcon from '@mui/icons-material/Google';
|
||||
import LaunchIcon from '@mui/icons-material/Launch';
|
||||
|
||||
import { AnthropicIcon } from '~/common/components/icons/vendors/AnthropicIcon';
|
||||
import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon';
|
||||
import { ExternalLink } from '~/common/components/ExternalLink';
|
||||
import { GroqIcon } from '~/common/components/icons/vendors/GroqIcon';
|
||||
import { LocalAIIcon } from '~/common/components/icons/vendors/LocalAIIcon';
|
||||
import { MistralIcon } from '~/common/components/icons/vendors/MistralIcon';
|
||||
@@ -29,7 +27,7 @@ import coverV114 from '../../../public/images/covers/release-cover-v1.14.0.png';
|
||||
import coverV113 from '../../../public/images/covers/release-cover-v1.13.0.png';
|
||||
// An image of a capybara sculpted entirely from black cotton candy, set against a minimalist backdrop with splashes of bright, contrasting sparkles. The capybara is calling on a 3D origami old-school pink telephone and the camera is zooming on the telephone. Close up photography, bokeh, white background.
|
||||
import coverV112 from '../../../public/images/covers/release-cover-v1.12.0.png';
|
||||
import { beamBlogUrl, beamReleaseDate } from './beam.data';
|
||||
import { beamBlogUrl } from './beam.data';
|
||||
|
||||
|
||||
interface NewsItem {
|
||||
@@ -59,18 +57,19 @@ export const NewsItems: NewsItem[] = [
|
||||
]
|
||||
}*/
|
||||
{
|
||||
versionCode: '1.15.0',
|
||||
versionCode: '1.15.1',
|
||||
versionName: 'Beam',
|
||||
versionDate: new Date(beamReleaseDate),
|
||||
versionDate: new Date('2024-04-10T08:00:00Z'),
|
||||
versionCoverImage: coverV115,
|
||||
items: [
|
||||
{ text: <><B href={beamBlogUrl} wow>Beam</B>: Find better answers with multi-model AI reasoning</>, issue: 443, icon: ChatBeamIcon },
|
||||
{ text: <><B>Explore diverse perspectives</B> and <B>synthesize optimal responses</B></>, noBullet: true },
|
||||
// { text: <><B>Explore diverse perspectives</B> and <B>synthesize optimal responses</B></>, noBullet: true },
|
||||
{ text: <><B issue={436}>Auto-configure</B> models for managed deployments</>, issue: 436 },
|
||||
{ text: <>Message <B issue={476}>starring ⭐</B>, filtering and attachment</>, issue: 476 },
|
||||
{ text: <>Default persona improvements</> },
|
||||
{ text: <>Fixes to Gemini models and SVGs, improvements to UI and icons, and more</> },
|
||||
{ text: <>Developers: imperative LLM models discovery</>, dev: true },
|
||||
{ text: <>1.15.1: Support for <B>Gemini Pro 1.5</B> and <B>OpenAI 2024-04-09</B> models</> },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -277,15 +276,6 @@ export const NewsItems: NewsItem[] = [
|
||||
];
|
||||
|
||||
|
||||
const wowStyle: SxProps = {
|
||||
textDecoration: 'underline',
|
||||
textDecorationThickness: '0.4em',
|
||||
textDecorationColor: 'rgba(var(--joy-palette-primary-lightChannel) / 1)',
|
||||
// textDecorationColor: 'rgba(0 255 0 / 0.5)',
|
||||
textDecorationSkipInk: 'none',
|
||||
// textUnderlineOffset: '-0.5em',
|
||||
};
|
||||
|
||||
function B(props: {
|
||||
// one-of
|
||||
href?: string,
|
||||
@@ -299,7 +289,6 @@ function B(props: {
|
||||
props.issue ? `${Brand.URIs.OpenRepo}/issues/${props.issue}`
|
||||
: props.code ? `${Brand.URIs.OpenRepo}/blob/main/${props.code}`
|
||||
: props.href;
|
||||
const isExtIcon = !props.issue;
|
||||
const boldText = (
|
||||
<Typography component='span' color={!!href ? 'primary' : 'neutral'} sx={{ fontWeight: 'lg' }}>
|
||||
{props.children}
|
||||
@@ -308,8 +297,8 @@ function B(props: {
|
||||
if (!href)
|
||||
return boldText;
|
||||
return (
|
||||
<Link href={href + clientUtmSource()} target='_blank' sx={props.wow ? wowStyle : undefined}>
|
||||
{boldText} {isExtIcon ? <LaunchIcon sx={{ mx: 0.5, fontSize: 16 }} /> : <AutoStoriesOutlinedIcon sx={{ mx: 0.5, fontSize: 16 }} />}
|
||||
</Link>
|
||||
<ExternalLink href={href + clientUtmSource()} highlight={props.wow} icon={props.issue ? 'issue' : undefined}>
|
||||
{boldText}
|
||||
</ExternalLink>
|
||||
);
|
||||
}
|
||||
@@ -187,7 +187,7 @@ export function Creator(props: { display: boolean }) {
|
||||
fontWeight: 'lg',
|
||||
},
|
||||
// first element
|
||||
'& > *:first-child': { borderTopLeftRadius: '0.5rem' },
|
||||
'& > *:first-of-type': { borderTopLeftRadius: '0.5rem' },
|
||||
}}
|
||||
>
|
||||
<Tab>From YouTube</Tab>
|
||||
|
||||
@@ -19,12 +19,14 @@ const shortcutsMd = platformAwareKeystrokes(`
|
||||
| Ctrl + Shift + V | Attach clipboard (better than Ctrl + V) |
|
||||
| Ctrl + M | Microphone (voice typing) |
|
||||
| **Chats** | |
|
||||
| Ctrl + Alt + Left | **Previous** chat (in history) |
|
||||
| Ctrl + Alt + Right | **Next** chat (in history) |
|
||||
| Ctrl + O | Open Chat ... |
|
||||
| Ctrl + S | Save Chat ... |
|
||||
| Ctrl + Alt + N | **New** chat |
|
||||
| Ctrl + Alt + X | **Reset** chat |
|
||||
| Ctrl + Alt + D | **Delete** chat |
|
||||
| Ctrl + Alt + B | **Branch** chat |
|
||||
| Ctrl + Alt + Left | **Previous** chat (in history) |
|
||||
| Ctrl + Alt + Right | **Next** chat (in history) |
|
||||
| **Settings** | |
|
||||
| Ctrl + Shift + P | ⚙️ Preferences |
|
||||
| Ctrl + Shift + M | 🧠 Models |
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { sendGAEvent } from '@next/third-parties/google';
|
||||
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Button, Step, stepClasses, StepIndicator, stepIndicatorClasses, Stepper, Typography } from '@mui/joy';
|
||||
@@ -12,6 +13,7 @@ import { AgiSquircleIcon } from '~/common/components/icons/AgiSquircleIcon';
|
||||
import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon';
|
||||
import { GlobalShortcutItem, ShortcutKeyName, useGlobalShortcuts } from '~/common/components/useGlobalShortcut';
|
||||
import { createDMessage } from '~/common/state/store-chats';
|
||||
import { hasGoogleAnalytics } from '~/common/components/GoogleAnalytics';
|
||||
import { useIsMobile } from '~/common/components/useMatchMedia';
|
||||
|
||||
|
||||
@@ -90,9 +92,9 @@ export interface ExplainerPage extends ExplainerStep {
|
||||
}
|
||||
|
||||
export function ExplainerCarousel(props: {
|
||||
explainerId: string,
|
||||
steps: ExplainerPage[],
|
||||
footer?: React.ReactNode,
|
||||
showPrevious?: boolean,
|
||||
onFinished: () => any,
|
||||
}) {
|
||||
|
||||
@@ -119,11 +121,21 @@ export function ExplainerCarousel(props: {
|
||||
}, []);
|
||||
|
||||
const handleNextPage = React.useCallback(() => {
|
||||
if (isLastPage)
|
||||
if (isLastPage) {
|
||||
hasGoogleAnalytics && sendGAEvent('event', 'tutorial_complete', { tutorial_id: props.explainerId });
|
||||
onFinished();
|
||||
else
|
||||
} else
|
||||
setStepIndex(step => step < props.steps.length - 1 ? step + 1 : step);
|
||||
}, [isLastPage, onFinished, props.steps.length]);
|
||||
}, [isLastPage, onFinished, props.explainerId, props.steps.length]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const recordTutorialBegun = () => {
|
||||
hasGoogleAnalytics && sendGAEvent('event', 'tutorial_begin', { tutorial_id: props.explainerId });
|
||||
};
|
||||
|
||||
const timeoutId = setTimeout(recordTutorialBegun, 500);
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [props.explainerId]);
|
||||
|
||||
|
||||
const shortcuts = React.useMemo((): GlobalShortcutItem[] => [
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { SxProps, TypographySystem } from '@mui/joy/styles/types';
|
||||
import AutoStoriesOutlinedIcon from '@mui/icons-material/AutoStoriesOutlined';
|
||||
import LaunchIcon from '@mui/icons-material/Launch';
|
||||
|
||||
import { Link } from './Link';
|
||||
|
||||
|
||||
const wowStyle: SxProps = {
|
||||
textDecoration: 'underline',
|
||||
textDecorationThickness: '0.4em',
|
||||
textDecorationColor: 'rgba(var(--joy-palette-primary-lightChannel) / 1)',
|
||||
// textDecorationColor: 'rgba(0 255 0 / 0.5)',
|
||||
textDecorationSkipInk: 'none',
|
||||
// textUnderlineOffset: '-0.5em',
|
||||
};
|
||||
|
||||
|
||||
export function ExternalLink(props: {
|
||||
href: string,
|
||||
level?: keyof TypographySystem | 'inherit',
|
||||
highlight?: boolean,
|
||||
icon?: 'issue',
|
||||
children: React.ReactNode,
|
||||
}) {
|
||||
return (
|
||||
<Link level={props.level} href={props.href} target='_blank' sx={props.highlight ? wowStyle : undefined}>
|
||||
{props.children} {props.icon === 'issue'
|
||||
? <AutoStoriesOutlinedIcon sx={{ mx: 0.5, fontSize: 16 }} />
|
||||
: <LaunchIcon sx={{ mx: 0.5, fontSize: 16 }} />
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
|
||||
export function FormInputKey(props: {
|
||||
id: string, // introduced to avoid clashes
|
||||
autoCompleteId: string, // introduced to avoid clashes
|
||||
label?: string, rightLabel?: string | React.JSX.Element,
|
||||
description?: string | React.JSX.Element,
|
||||
value: string, onChange: (value: string) => void,
|
||||
@@ -27,8 +27,10 @@ export function FormInputKey(props: {
|
||||
</IconButton>
|
||||
), [props.value, props.noKey, isVisible]);
|
||||
|
||||
const acId = (props.noKey ? 'input-text-' : 'input-key-') + props.autoCompleteId;
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormControl id={acId}>
|
||||
|
||||
{!!props.label && <Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'baseline', flexWrap: 'wrap', justifyContent: 'space-between' }}>
|
||||
<FormLabel>{props.label}</FormLabel>
|
||||
@@ -38,7 +40,10 @@ export function FormInputKey(props: {
|
||||
</Box>}
|
||||
|
||||
<Input
|
||||
id={props.id}
|
||||
key={acId}
|
||||
name={acId}
|
||||
autoComplete='off'
|
||||
// autoComplete={props.noKey ? 'off' : 'new-password'}
|
||||
variant={props.required ? 'outlined' : 'outlined' /* 'soft */}
|
||||
value={props.value} onChange={handleChange}
|
||||
placeholder={props.required ? props.placeholder ? 'required: ' + props.placeholder : 'required' : props.placeholder || '...'}
|
||||
|
||||
@@ -9,16 +9,28 @@ import { FormLabelStart } from './FormLabelStart';
|
||||
* Text form field (e.g. enter a host)
|
||||
*/
|
||||
export function FormTextField(props: {
|
||||
autoCompleteId: string,
|
||||
title: string | React.JSX.Element,
|
||||
description?: string | React.JSX.Element,
|
||||
tooltip?: string | React.JSX.Element,
|
||||
placeholder?: string, isError?: boolean, disabled?: boolean,
|
||||
value: string | undefined, onChange: (text: string) => void,
|
||||
}) {
|
||||
const acId = 'text-' + props.autoCompleteId;
|
||||
return (
|
||||
<FormControl orientation='horizontal' disabled={props.disabled} sx={{ flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<FormControl
|
||||
id={acId}
|
||||
orientation='horizontal'
|
||||
disabled={props.disabled}
|
||||
sx={{
|
||||
flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<FormLabelStart title={props.title} description={props.description} tooltip={props.tooltip} />
|
||||
<Input
|
||||
key={acId}
|
||||
name={acId}
|
||||
autoComplete='off'
|
||||
variant='outlined' placeholder={props.placeholder} error={props.isError}
|
||||
value={props.value} onChange={event => props.onChange(event.target.value)}
|
||||
sx={{ flexGrow: 1 }}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
import { browserLangOrUS } from '~/common/util/pwaUtils';
|
||||
|
||||
|
||||
// UI Preferences
|
||||
@@ -54,7 +55,7 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
|
||||
|
||||
// UI Features
|
||||
|
||||
preferredLanguage: (typeof navigator !== 'undefined') && navigator.language || 'en-US',
|
||||
preferredLanguage: browserLangOrUS,
|
||||
setPreferredLanguage: (preferredLanguage: string) => set({ preferredLanguage }),
|
||||
|
||||
centerMode: 'wide',
|
||||
|
||||
@@ -4,6 +4,10 @@ import { isBrowser, isFirefox } from './pwaUtils';
|
||||
export function copyToClipboard(text: string, typeLabel: string) {
|
||||
if (!isBrowser)
|
||||
return;
|
||||
if (!window.navigator.clipboard?.writeText) {
|
||||
alert('Clipboard access is blocked. Please enable it in your browser settings.');
|
||||
return;
|
||||
}
|
||||
window.navigator.clipboard.writeText(text)
|
||||
.then(() => {
|
||||
addSnackbar({
|
||||
|
||||
@@ -10,6 +10,11 @@ export const isMacUser = /Macintosh|MacIntel|MacPPC|Mac68K|iPad/.test(safeUA);
|
||||
export const isChromeDesktop = safeUA.includes('Chrome') && !safeUA.includes('Mobile');
|
||||
export const isFirefox = safeUA.includes('Firefox');
|
||||
|
||||
// frontend language
|
||||
const browserLang = isBrowser ? window.navigator.language : '';
|
||||
export const browserLangOrUS = browserLang || 'en-US';
|
||||
export const browserLangNotUS = browserLangOrUS !== 'en-US';
|
||||
|
||||
// deployment environment
|
||||
export const isVercelFromBackendOrSSR = !!process.env.VERCEL_ENV;
|
||||
export const isVercelFromFrontend = !!process.env.NEXT_PUBLIC_VERCEL_URL;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export function prettyTimestampForFilenames(useSeconds: boolean = true) {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // JavaScript months are 0-based.
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hour = String(now.getHours()).padStart(2, '0');
|
||||
const minute = String(now.getMinutes()).padStart(2, '0');
|
||||
const second = String(now.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}-${hour}${minute}${useSeconds ? second : ''}`; // YYYY-MM-DD_HHMM[SS] format
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
* Functions to deal with HTML5Video elements.
|
||||
*/
|
||||
|
||||
import { prettyTimestampForFilenames } from './timeUtils';
|
||||
|
||||
export function downloadVideoFrameAsPNG(videoElement: HTMLVideoElement, prefixName: string) {
|
||||
// current video frame -> canvas -> dataURL PNG
|
||||
const renderedFrame = _renderVideoFrameToCanvas(videoElement);
|
||||
@@ -30,9 +32,8 @@ export async function renderVideoFrameAsPNGFile(videoElement: HTMLVideoElement,
|
||||
}
|
||||
|
||||
function _prettyFileName(prefixName: string, renderedFrame: HTMLCanvasElement) {
|
||||
const prettyDate = new Date().toISOString().replace(/[:-]/g, '').replace('T', '-').replace('Z', '');
|
||||
const prettyResolution = `${renderedFrame.width}x${renderedFrame.height}`;
|
||||
return `${prefixName}-${prettyDate}-${prettyResolution}.png`;
|
||||
return `${prefixName}_${prettyTimestampForFilenames()}_${prettyResolution}.png`;
|
||||
}
|
||||
|
||||
function _renderVideoFrameToCanvas(videoElement: HTMLVideoElement): HTMLCanvasElement {
|
||||
|
||||
@@ -116,6 +116,7 @@ export function BeamExplainer(props: {
|
||||
>
|
||||
|
||||
<ExplainerCarousel
|
||||
explainerId='beam-onboard'
|
||||
steps={beamSteps}
|
||||
footer={
|
||||
<Typography level='body-xs' sx={{ textAlign: 'center', maxWidth: '400px', mx: 'auto' }}>
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as React from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import type { SxProps, VariantProp } from '@mui/joy/styles/types';
|
||||
import { Box, Button, Typography, useTheme } from '@mui/joy';
|
||||
import { Alert, Box, Button, Typography, useTheme } from '@mui/joy';
|
||||
import AddCircleOutlineRoundedIcon from '@mui/icons-material/AddCircleOutlineRounded';
|
||||
import LanguageIcon from '@mui/icons-material/Language';
|
||||
|
||||
import { Fusion } from './Fusion';
|
||||
import { findFusionFactory, FusionFactorySpec } from './instructions/beam.gather.factories';
|
||||
@@ -11,6 +12,7 @@ import { findFusionFactory, FusionFactorySpec } from './instructions/beam.gather
|
||||
import { BeamCard, beamCardClasses } from '../BeamCard';
|
||||
import { BeamStoreApi, useBeamStore } from '../store-beam.hooks';
|
||||
import { GATHER_COLOR } from '../beam.config';
|
||||
import { browserLangNotUS } from '~/common/util/pwaUtils';
|
||||
|
||||
|
||||
const fusionGridDesktopSx: SxProps = {
|
||||
@@ -150,6 +152,18 @@ export function BeamFusionGrid(props: {
|
||||
</BeamCard>
|
||||
)}
|
||||
|
||||
{/* Full-width warning if not */}
|
||||
{browserLangNotUS && (
|
||||
<Alert color='warning' sx={{
|
||||
// full row of the grid
|
||||
gridColumn: '1 / -1',
|
||||
}}>
|
||||
<Typography level='body-sm' color='warning' startDecorator={<LanguageIcon />}>
|
||||
Note: Merges are defined in English and have not been translated to your browser language ({navigator.language}) yet.
|
||||
</Typography>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Pad items: N * <Box/> */}
|
||||
{/*{padItems > 0 && (*/}
|
||||
{/* Array.from({ length: padItems }).map((_, index) => (*/}
|
||||
|
||||
@@ -173,7 +173,7 @@ Only work with the provided {{N}} responses. Begin with listing the criteria.`.t
|
||||
createInstructions: () => [
|
||||
{
|
||||
type: 'chat-generate',
|
||||
label: 'Executing Your Merge Strategy',
|
||||
label: 'Executing Your Merge',
|
||||
method: 's-s0-h0-u0-aN-u',
|
||||
systemPrompt: `
|
||||
Your task is to synthesize a cohesive and relevant response based on the following messages: the original system message, the full conversation history up to the user query, the user query, and a set of {{N}} answers generated independently.
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as React from 'react';
|
||||
|
||||
import { IconButton, Tooltip } from '@mui/joy';
|
||||
|
||||
import { CodePenIcon } from '~/common/components/icons/3rdparty/CodePenIcon';
|
||||
import { Brand } from '~/common/app.config';
|
||||
import { CodePenIcon } from '~/common/components/icons/3rdparty/CodePenIcon';
|
||||
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
|
||||
|
||||
|
||||
// CodePen is a web-based HTML, CSS, and JavaScript code editor
|
||||
@@ -25,7 +26,7 @@ const handleOpenInCodePen = (code: string, language: string) => {
|
||||
form.target = '_blank';
|
||||
|
||||
const payload = {
|
||||
title: `${Brand.Title.Base} Code - ${new Date().toISOString()}`, // eg "GPT 2021-08-31T15:00:00.000Z"
|
||||
title: `${Brand.Title.Base} Code - ${prettyTimestampForFilenames()}`,
|
||||
css: hasCSS ? code : '',
|
||||
html: hasHTML ? code : '',
|
||||
js: hasJS ? code : '',
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as React from 'react';
|
||||
|
||||
import { IconButton, Tooltip } from '@mui/joy';
|
||||
|
||||
import { StackBlitzIcon } from '~/common/components/icons/3rdparty/StackBlitzIcon';
|
||||
import { Brand } from '~/common/app.config';
|
||||
import { StackBlitzIcon } from '~/common/components/icons/3rdparty/StackBlitzIcon';
|
||||
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
|
||||
|
||||
|
||||
const _languages = [
|
||||
@@ -42,7 +43,7 @@ const handleOpenInStackBlitz = (code: string, language: string, title?: string)
|
||||
const projectDetails = {
|
||||
files: { [fileName]: code },
|
||||
template: template,
|
||||
description: `${Brand.Title.Common} file created on ${new Date().toISOString()}`,
|
||||
description: `${Brand.Title.Common} file created on ${prettyTimestampForFilenames()}`,
|
||||
title: language == 'python' ? 'Python Starter' : title,
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
|
||||
}}>
|
||||
|
||||
{/* Markdown Title (File/Type) */}
|
||||
{blockTitle != inferredCodeLanguage && blockTitle.includes('.') && (
|
||||
{blockTitle != inferredCodeLanguage && (blockTitle.includes('.') || blockTitle.includes('://')) && (
|
||||
<Sheet sx={{ boxShadow: 'sm', borderRadius: 'sm', mb: 1 }}>
|
||||
<Typography level='title-sm' sx={{ px: 1, py: 0.5 }}>
|
||||
{blockTitle}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function BrowseSettings() {
|
||||
</FormHelperText>
|
||||
|
||||
<FormInputKey
|
||||
id='browse-wss' label='Puppeteer Endpoint' noKey
|
||||
autoCompleteId='browse-wss' label='Puppeteer Endpoint' noKey
|
||||
value={wssEndpoint} onChange={setWssEndpoint}
|
||||
rightLabel={!isServerConfig ? 'required' : '✔️ already set in server'}
|
||||
required={!isServerConfig} isError={!isClientValid && !isServerConfig}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function ElevenlabsSettings() {
|
||||
{/*</FormHelperText>*/}
|
||||
|
||||
{!isConfiguredServerSide && <FormInputKey
|
||||
id='elevenlabs-key' label='ElevenLabs API Key'
|
||||
autoCompleteId='elevenlabs-key' label='ElevenLabs API Key'
|
||||
rightLabel={isConfiguredServerSide ? '✔️ already set in server' : 'required'}
|
||||
value={apiKey} onChange={setApiKey}
|
||||
required={!isConfiguredServerSide} isError={!isValidKey}
|
||||
|
||||
@@ -14,12 +14,9 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Most powerful model for highly complex tasks',
|
||||
contextWindow: 200000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.015,
|
||||
cpmCompletion: 0.075,
|
||||
},
|
||||
trainingDataCutoff: 'Aug 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision],
|
||||
pricing: { chatIn: 15, chatOut: 75 },
|
||||
},
|
||||
{
|
||||
id: 'claude-3-sonnet-20240229',
|
||||
@@ -28,12 +25,9 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Ideal balance of intelligence and speed for enterprise workloads',
|
||||
contextWindow: 200000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.003,
|
||||
cpmCompletion: 0.015,
|
||||
},
|
||||
trainingDataCutoff: 'Aug 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision],
|
||||
pricing: { chatIn: 3, chatOut: 15 },
|
||||
},
|
||||
{
|
||||
id: 'claude-3-haiku-20240307',
|
||||
@@ -42,12 +36,9 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Fastest and most compact model for near-instant responsiveness',
|
||||
contextWindow: 200000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.00025,
|
||||
cpmCompletion: 0.00125,
|
||||
},
|
||||
trainingDataCutoff: 'Aug 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision],
|
||||
pricing: { chatIn: 0.25, chatOut: 1.25 },
|
||||
},
|
||||
|
||||
// Claude 2 models
|
||||
@@ -58,11 +49,8 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Superior performance on tasks that require complex reasoning, with reduced model hallucination rates',
|
||||
contextWindow: 200000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.008,
|
||||
cpmCompletion: 0.024,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
pricing: { chatIn: 8, chatOut: 24 },
|
||||
},
|
||||
{
|
||||
id: 'claude-2.0',
|
||||
@@ -71,11 +59,8 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Superior performance on tasks that require complex reasoning',
|
||||
contextWindow: 100000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.008,
|
||||
cpmCompletion: 0.024,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
pricing: { chatIn: 8, chatOut: 24 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
@@ -85,10 +70,7 @@ export const hardcodedAnthropicModels: ModelDescriptionSchema[] = [
|
||||
description: 'Low-latency, high throughput model',
|
||||
contextWindow: 100000,
|
||||
maxCompletionTokens: 4096,
|
||||
pricing: {
|
||||
cpmPrompt: 0.00163,
|
||||
cpmCompletion: 0.00551,
|
||||
},
|
||||
pricing: { chatIn: 0.8, chatOut: 2.4 },
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -72,10 +72,13 @@ export function anthropicMessagesPayloadOrThrow(model: OpenAIModelSchema, histor
|
||||
if (history[0]?.role === 'system' && history.length > 1)
|
||||
systemPrompt = history.shift()?.content;
|
||||
|
||||
// Transform the OpenAIHistorySchema into the target messages format, ensuring that roles alternate between 'user' and 'assistant's
|
||||
// Transform the OpenAIHistorySchema into the target messages format, ensuring that roles alternate between 'user' and 'assistant'
|
||||
const messages = history.reduce(
|
||||
(acc, historyItem, index) => {
|
||||
|
||||
// skip empty messages
|
||||
if (!historyItem.content.trim()) return acc;
|
||||
|
||||
const lastMessage: AnthropicWireMessagesRequest['messages'][number] | undefined = acc[acc.length - 1];
|
||||
const anthropicRole = historyItem.role === 'assistant' ? 'assistant' : 'user';
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { GeminiModelSchema } from './gemini.wiretypes';
|
||||
import type { ModelDescriptionSchema } from '../llm.server.types';
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Vision } from '../../store-llms';
|
||||
|
||||
|
||||
const filterUnallowedNames = ['Legacy'];
|
||||
const filterUnallowedInterfaces: GeminiModelSchema['supportedGenerationMethods'] = ['generateAnswer', 'embedContent', 'embedText'];
|
||||
|
||||
const geminiLinkModels = ['models/gemini-pro', 'models/gemini-pro-vision'];
|
||||
|
||||
// interfaces mapping
|
||||
const geminiChatInterfaces: GeminiModelSchema['supportedGenerationMethods'] = ['generateContent'];
|
||||
const geminiVisionNames = ['-vision'];
|
||||
|
||||
|
||||
export function geminiFilterModels(geminiModel: GeminiModelSchema): boolean {
|
||||
const isAllowed = !filterUnallowedNames.some(name => geminiModel.displayName.includes(name));
|
||||
const isSupported = !filterUnallowedInterfaces.some(iface => geminiModel.supportedGenerationMethods.includes(iface));
|
||||
return isAllowed && isSupported;
|
||||
}
|
||||
|
||||
export function geminiSortModels(a: ModelDescriptionSchema, b: ModelDescriptionSchema): number {
|
||||
// hidden to the bottom, then names descending
|
||||
if (a.hidden && !b.hidden) return 1;
|
||||
if (!a.hidden && b.hidden) return -1;
|
||||
return b.label.localeCompare(a.label);
|
||||
}
|
||||
|
||||
export function geminiModelToModelDescription(geminiModel: GeminiModelSchema, allModels: GeminiModelSchema[]): ModelDescriptionSchema {
|
||||
const { description, displayName, name: modelId, supportedGenerationMethods } = geminiModel;
|
||||
|
||||
// handle symlinks
|
||||
const isSymlink = geminiLinkModels.includes(modelId);
|
||||
const symlinked = isSymlink ? allModels.find(m => m.displayName === displayName && m.name !== modelId) : null;
|
||||
const label = isSymlink ? `🔗 ${displayName.replace('1.0', '')} → ${symlinked ? symlinked.name : '?'}` : displayName;
|
||||
|
||||
// handle hidden models
|
||||
const hasChatInterfaces = supportedGenerationMethods.some(iface => geminiChatInterfaces.includes(iface));
|
||||
const hidden = isSymlink || !hasChatInterfaces;
|
||||
|
||||
// context window
|
||||
const { inputTokenLimit, outputTokenLimit } = geminiModel;
|
||||
const contextWindow = inputTokenLimit + outputTokenLimit;
|
||||
|
||||
// description
|
||||
const { version, topK, topP, temperature } = geminiModel;
|
||||
const descriptionLong = description + ` (Version: ${version}, Defaults: temperature=${temperature}, topP=${topP}, topK=${topK}, interfaces=[${supportedGenerationMethods.join(',')}])`;
|
||||
|
||||
const interfaces: ModelDescriptionSchema['interfaces'] = [];
|
||||
if (hasChatInterfaces) {
|
||||
interfaces.push(LLM_IF_OAI_Chat);
|
||||
if (geminiVisionNames.some(name => modelId.includes(name)))
|
||||
interfaces.push(LLM_IF_OAI_Vision);
|
||||
}
|
||||
|
||||
return {
|
||||
id: modelId,
|
||||
label,
|
||||
// created: ...
|
||||
// updated: ...
|
||||
description: descriptionLong,
|
||||
contextWindow: contextWindow,
|
||||
maxCompletionTokens: outputTokenLimit,
|
||||
// pricing: isGeminiPro ? { needs per-character and per-image pricing } : undefined,
|
||||
// rateLimits: isGeminiPro ? { reqPerMinute: 60 } : undefined,
|
||||
interfaces,
|
||||
hidden,
|
||||
};
|
||||
}
|
||||
@@ -8,13 +8,12 @@ import { createTRPCRouter, publicProcedure } from '~/server/api/trpc.server';
|
||||
import { fetchJsonOrTRPCError } from '~/server/api/trpc.router.fetchers';
|
||||
|
||||
import { fixupHost } from '~/common/util/urlUtils';
|
||||
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Vision } from '../../store-llms';
|
||||
import { llmsChatGenerateOutputSchema, llmsListModelsOutputSchema, ModelDescriptionSchema } from '../llm.server.types';
|
||||
import { llmsChatGenerateOutputSchema, llmsListModelsOutputSchema } from '../llm.server.types';
|
||||
|
||||
import { OpenAIHistorySchema, openAIHistorySchema, OpenAIModelSchema, openAIModelSchema } from '../openai/openai.router';
|
||||
|
||||
import { GeminiBlockSafetyLevel, geminiBlockSafetyLevelSchema, GeminiContentSchema, GeminiGenerateContentRequest, geminiGeneratedContentResponseSchema, geminiModelsGenerateContentPath, geminiModelsListOutputSchema, geminiModelsListPath } from './gemini.wiretypes';
|
||||
import { geminiFilterModels, geminiModelToModelDescription, geminiSortModels } from '~/modules/llms/server/gemini/gemini.models';
|
||||
|
||||
|
||||
// Default hosts
|
||||
@@ -146,43 +145,13 @@ export const llmGeminiRouter = createTRPCRouter({
|
||||
// as the List API already all the info on all the models
|
||||
|
||||
// map to our output schema
|
||||
const models = detailedModels
|
||||
.filter(geminiFilterModels)
|
||||
.map(geminiModel => geminiModelToModelDescription(geminiModel, detailedModels))
|
||||
.sort(geminiSortModels);
|
||||
|
||||
return {
|
||||
models: detailedModels.map((geminiModel) => {
|
||||
const { description, displayName, inputTokenLimit, name, outputTokenLimit, supportedGenerationMethods } = geminiModel;
|
||||
|
||||
const isSymlink = ['models/gemini-pro', 'models/gemini-pro-vision'].includes(name);
|
||||
const symlinked = isSymlink ? detailedModels.find(m => m.displayName === displayName && m.name !== name) : null;
|
||||
|
||||
const contextWindow = inputTokenLimit + outputTokenLimit;
|
||||
const hidden = !supportedGenerationMethods.includes('generateContent') || isSymlink;
|
||||
|
||||
const { version, topK, topP, temperature } = geminiModel;
|
||||
const descriptionLong = description + ` (Version: ${version}, Defaults: temperature=${temperature}, topP=${topP}, topK=${topK}, interfaces=[${supportedGenerationMethods.join(',')}])`;
|
||||
|
||||
// const isGeminiPro = name.includes('gemini-pro');
|
||||
const isGeminiProVision = name.includes('gemini-pro-vision');
|
||||
|
||||
const interfaces: ModelDescriptionSchema['interfaces'] = [];
|
||||
if (supportedGenerationMethods.includes('generateContent')) {
|
||||
interfaces.push(LLM_IF_OAI_Chat);
|
||||
if (isGeminiProVision)
|
||||
interfaces.push(LLM_IF_OAI_Vision);
|
||||
}
|
||||
|
||||
return {
|
||||
id: name,
|
||||
label: isSymlink ? `🔗 ${displayName.replace('1.0', '')} → ${symlinked ? symlinked.name : '?'}` : displayName,
|
||||
// created: ...
|
||||
// updated: ...
|
||||
description: descriptionLong,
|
||||
contextWindow: contextWindow,
|
||||
maxCompletionTokens: outputTokenLimit,
|
||||
// pricing: isGeminiPro ? { needs per-character and per-image pricing } : undefined,
|
||||
// rateLimits: isGeminiPro ? { reqPerMinute: 60 } : undefined,
|
||||
interfaces: supportedGenerationMethods.includes('generateContent') ? [LLM_IF_OAI_Chat] : [],
|
||||
hidden,
|
||||
} satisfies ModelDescriptionSchema;
|
||||
}),
|
||||
models: models,
|
||||
};
|
||||
}),
|
||||
|
||||
|
||||
@@ -10,31 +10,34 @@ export const geminiModelsStreamGenerateContentPath = '/v1beta/{model=models/*}:s
|
||||
|
||||
// models.list = /v1beta/models
|
||||
|
||||
const geminiModelSchema = z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
displayName: z.string(),
|
||||
description: z.string(),
|
||||
inputTokenLimit: z.number().int().min(1),
|
||||
outputTokenLimit: z.number().int().min(1),
|
||||
supportedGenerationMethods: z.array(z.enum([
|
||||
'countMessageTokens',
|
||||
'countTextTokens',
|
||||
'countTokens',
|
||||
'createTunedModel',
|
||||
'createTunedTextModel',
|
||||
'embedContent',
|
||||
'embedText',
|
||||
'generateAnswer',
|
||||
'generateContent',
|
||||
'generateMessage',
|
||||
'generateText',
|
||||
])),
|
||||
temperature: z.number().optional(),
|
||||
topP: z.number().optional(),
|
||||
topK: z.number().optional(),
|
||||
});
|
||||
export type GeminiModelSchema = z.infer<typeof geminiModelSchema>;
|
||||
|
||||
export const geminiModelsListOutputSchema = z.object({
|
||||
models: z.array(z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
displayName: z.string(),
|
||||
description: z.string(),
|
||||
inputTokenLimit: z.number().int().min(1),
|
||||
outputTokenLimit: z.number().int().min(1),
|
||||
supportedGenerationMethods: z.array(z.enum([
|
||||
'countMessageTokens',
|
||||
'countTextTokens',
|
||||
'countTokens',
|
||||
'createTunedModel',
|
||||
'createTunedTextModel',
|
||||
'embedContent',
|
||||
'embedText',
|
||||
'generateAnswer',
|
||||
'generateContent',
|
||||
'generateMessage',
|
||||
'generateText',
|
||||
])),
|
||||
temperature: z.number().optional(),
|
||||
topP: z.number().optional(),
|
||||
topK: z.number().optional(),
|
||||
})),
|
||||
models: z.array(geminiModelSchema),
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { createParser as createEventsourceParser, EventSourceParseCallback, EventSourceParser, ParsedEvent, ReconnectInterval } from 'eventsource-parser';
|
||||
|
||||
import { createEmptyReadableStream, debugGenerateCurlCommand, nonTrpcServerFetchOrThrow, safeErrorString, SERVER_DEBUG_WIRE, serverCapitalizeFirstLetter } from '~/server/wire';
|
||||
import { createEmptyReadableStream, debugGenerateCurlCommand, nonTrpcServerFetchOrThrow, safeErrorString, SERVER_DEBUG_WIRE, serverCapitalizeFirstLetter, ServerFetchError } from '~/server/wire';
|
||||
|
||||
|
||||
// Anthropic server imports
|
||||
@@ -109,7 +109,7 @@ export async function llmStreamingRelayHandler(req: NextRequest): Promise<Respon
|
||||
case 'perplexity':
|
||||
case 'togetherai':
|
||||
requestAccess = openAIAccess(access, model.id, '/v1/chat/completions');
|
||||
body = openAIChatCompletionPayload(model, history, null, null, 1, true);
|
||||
body = openAIChatCompletionPayload(access.dialect, model, history, null, null, 1, true);
|
||||
vendorStreamParser = createStreamParserOpenAI();
|
||||
break;
|
||||
}
|
||||
@@ -121,14 +121,18 @@ export async function llmStreamingRelayHandler(req: NextRequest): Promise<Respon
|
||||
upstreamResponse = await nonTrpcServerFetchOrThrow(requestAccess.url, 'POST', requestAccess.headers, body);
|
||||
|
||||
} catch (error: any) {
|
||||
const fetchOrVendorError = safeErrorString(error) + (error?.cause ? ' · ' + error.cause : '');
|
||||
|
||||
// server-side admins message
|
||||
console.error(`/api/llms/stream: fetch issue:`, access.dialect, fetchOrVendorError, requestAccess?.url);
|
||||
const capDialect = serverCapitalizeFirstLetter(access.dialect);
|
||||
const fetchOrVendorError = safeErrorString(error) + (error?.cause ? ' · ' + JSON.stringify(error.cause) : '');
|
||||
console.error(`[POST] /api/llms/stream: ${capDialect}: fetch issue:`, fetchOrVendorError, requestAccess?.url);
|
||||
|
||||
// client-side users visible message
|
||||
return new NextResponse(`**[Service Issue] ${serverCapitalizeFirstLetter(access.dialect)}**: ${fetchOrVendorError}`
|
||||
+ (process.env.NODE_ENV === 'development' ? ` · [URL: ${requestAccess?.url}]` : ''), { status: 500 });
|
||||
const statusCode = ((error instanceof ServerFetchError) && (error.statusCode >= 400)) ? error.statusCode : 422;
|
||||
const devMessage = process.env.NODE_ENV === 'development' ? ` [DEV_URL: ${requestAccess?.url}]` : '';
|
||||
return new NextResponse(`**[Service Issue] ${capDialect}**: ${fetchOrVendorError}${devMessage}`, {
|
||||
status: statusCode,
|
||||
});
|
||||
}
|
||||
|
||||
/* The following code is heavily inspired by the Vercel AI SDK, but simplified to our needs and in full control.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Complete, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision } from '../store-llms';
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Complete, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_Vision } from '../store-llms';
|
||||
|
||||
|
||||
// Model Description: a superset of LLM model descriptors
|
||||
|
||||
const pricingSchema = z.object({
|
||||
cpmPrompt: z.number().optional(), // Cost per thousand prompt tokens
|
||||
cpmCompletion: z.number().optional(), // Cost per thousand completion tokens
|
||||
chatIn: z.number().optional(), // Cost per Million input tokens
|
||||
chatOut: z.number().optional(), // Cost per Million output tokens
|
||||
});
|
||||
|
||||
// const rateLimitsSchema = z.object({
|
||||
@@ -21,10 +21,10 @@ const modelDescriptionSchema = z.object({
|
||||
description: z.string(),
|
||||
contextWindow: z.number().nullable(),
|
||||
maxCompletionTokens: z.number().optional(),
|
||||
pricing: pricingSchema.optional(),
|
||||
// rateLimits: rateLimitsSchema.optional(),
|
||||
trainingDataCutoff: z.string().optional(),
|
||||
interfaces: z.array(z.enum([LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Complete, LLM_IF_OAI_Vision])),
|
||||
interfaces: z.array(z.enum([LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Complete, LLM_IF_OAI_Vision, LLM_IF_OAI_Json])),
|
||||
pricing: pricingSchema.optional(),
|
||||
hidden: z.boolean().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,98 +1,164 @@
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Complete, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision } from '../../store-llms';
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Complete, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_Vision } from '../../store-llms';
|
||||
|
||||
import type { ModelDescriptionSchema } from '../llm.server.types';
|
||||
import type { OpenAIWire } from './openai.wiretypes';
|
||||
import { wireGroqModelsListOutputSchema } from './groq.wiretypes';
|
||||
import { wireMistralModelsListOutputSchema } from './mistral.wiretypes';
|
||||
import { wireOpenrouterModelsListOutputSchema } from './openrouter.wiretypes';
|
||||
import { wireTogetherAIListOutputSchema } from './togetherai.wiretypes';
|
||||
|
||||
|
||||
// [Azure] / [OpenAI]
|
||||
const _knownOpenAIChatModels: ManualMappings = [
|
||||
// GPT4 Vision
|
||||
|
||||
// GPT4 Turbo with Vision -> 2024-04-09
|
||||
{
|
||||
idPrefix: 'gpt-4-vision-preview',
|
||||
label: 'GPT-4 Turbo · Vision',
|
||||
description: 'GPT-4 Turbo model featuring improved instruction following, JSON mode, reproducible outputs, parallel function calling, and more. Returns a maximum of 4,096 output tokens.',
|
||||
idPrefix: 'gpt-4-turbo',
|
||||
label: 'GPT-4 Turbo',
|
||||
description: 'GPT-4 Turbo with Vision. The latest GPT-4 Turbo model with vision capabilities. Vision requests can now use JSON mode and function calling. Currently points to gpt-4-turbo-2024-04-09.',
|
||||
symLink: 'gpt-4-turbo-2024-04-09',
|
||||
hidden: true,
|
||||
// copied from symlinked
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision],
|
||||
hidden: true, // because no 'image input' support yet
|
||||
trainingDataCutoff: 'Dec 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
},
|
||||
{
|
||||
isLatest: true,
|
||||
idPrefix: 'gpt-4-turbo-2024-04-09',
|
||||
label: 'GPT-4 Turbo (2024-04-09)',
|
||||
description: 'GPT-4 Turbo with Vision model. Vision requests can now use JSON mode and function calling. gpt-4-turbo currently points to this version.',
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Dec 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
},
|
||||
|
||||
// GPT4 Turbo
|
||||
// GPT4 Turbo Previews
|
||||
{
|
||||
idPrefix: 'gpt-4-0125-preview',
|
||||
label: 'GPT-4 Turbo (0125)',
|
||||
description: 'The latest GPT-4 model intended to reduce cases of “laziness” where the model doesn’t complete a task.',
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
isLatest: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-1106-preview',
|
||||
label: 'GPT-4 Turbo (1106)',
|
||||
description: '128k context, fresher knowledge, cheaper than GPT-4.',
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-turbo-preview',
|
||||
label: 'GPT-4 Turbo',
|
||||
description: 'Currently points to gpt-4-0125-preview.',
|
||||
idPrefix: 'gpt-4-turbo-preview', // GPT-4 Turbo preview model -> 0125
|
||||
label: 'GPT-4 Preview Turbo',
|
||||
description: 'GPT-4 Turbo preview model. Currently points to gpt-4-0125-preview.',
|
||||
symLink: 'gpt-4-0125-preview',
|
||||
hidden: true,
|
||||
// copied
|
||||
// copied from symlinked
|
||||
isPreview: true,
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
trainingDataCutoff: 'Dec 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-0125-preview', // GPT-4 Turbo preview model
|
||||
label: 'GPT-4 Turbo (0125)',
|
||||
description: 'GPT-4 Turbo preview model intended to reduce cases of "laziness" where the model doesn\'t complete a task. Returns a maximum of 4,096 output tokens.',
|
||||
isPreview: true,
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Dec 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-1106-preview', // GPT-4 Turbo preview model
|
||||
label: 'GPT-4 Turbo (1106)',
|
||||
description: 'GPT-4 Turbo preview model featuring improved instruction following, JSON mode, reproducible outputs, parallel function calling, and more. Returns a maximum of 4,096 output tokens.',
|
||||
isPreview: true,
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Apr 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
hidden: true,
|
||||
},
|
||||
|
||||
// GPT4 Vision Previews
|
||||
{
|
||||
idPrefix: 'gpt-4-vision-preview', // GPT-4 Turbo vision preview
|
||||
label: 'GPT-4 Preview Vision',
|
||||
description: 'GPT-4 model with the ability to understand images, in addition to all other GPT-4 Turbo capabilities. This is a preview model, we recommend developers to now use gpt-4-turbo which includes vision capabilities. Currently points to gpt-4-1106-vision-preview.',
|
||||
symLink: 'gpt-4-1106-vision-preview',
|
||||
// copied from symlinked
|
||||
isPreview: true,
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Apr 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
hidden: true, // Deprecated in favor of gpt-4-turbo
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-1106-vision-preview',
|
||||
label: 'GPT-4 Preview Vision (1106)',
|
||||
description: 'GPT-4 model with the ability to understand images, in addition to all other GPT-4 Turbo capabilities. This is a preview model, we recommend developers to now use gpt-4-turbo which includes vision capabilities. Returns a maximum of 4,096 output tokens.',
|
||||
isPreview: true,
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Apr 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
hidden: true, // Deprecated in favor of gpt-4-turbo
|
||||
},
|
||||
|
||||
|
||||
// GPT4-32k's
|
||||
{
|
||||
idPrefix: 'gpt-4-32k',
|
||||
label: 'GPT-4 32k',
|
||||
description: 'Currently points to gpt-4-32k-0613. This model was never rolled out widely in favor of GPT-4 Turbo.',
|
||||
symLink: 'gpt-4-32k-0613',
|
||||
// copied from symlinked
|
||||
contextWindow: 32768,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
pricing: { chatIn: 60, chatOut: 120 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-32k-0613',
|
||||
label: 'GPT-4 32k (0613)',
|
||||
description: 'Snapshot of gpt-4-32 from June 13th 2023.',
|
||||
description: 'Snapshot of gpt-4-32k from June 13th 2023 with improved function calling support. This model was never rolled out widely in favor of GPT-4 Turbo.',
|
||||
contextWindow: 32768,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
isLatest: true,
|
||||
pricing: { chatIn: 60, chatOut: 120 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-32k-0314',
|
||||
label: 'GPT-4 32k (0314)',
|
||||
description: 'Snapshot of gpt-4-32 from March 14th 2023. Will be deprecated on June 13th 2024 at the earliest.',
|
||||
contextWindow: 32768,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-32k',
|
||||
label: 'GPT-4 32k',
|
||||
description: 'Currently points to gpt-4-32k-0613.',
|
||||
symLink: 'gpt-4-32k-0613',
|
||||
// copied
|
||||
contextWindow: 32768,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
pricing: { chatIn: 60, chatOut: 120 },
|
||||
hidden: true,
|
||||
},
|
||||
|
||||
|
||||
// GPT4's
|
||||
{
|
||||
isLatest: true,
|
||||
idPrefix: 'gpt-4-0613',
|
||||
label: 'GPT-4 (0613)',
|
||||
description: 'Snapshot of gpt-4 from June 13th 2023 with function calling data. Data up to Sep 2021.',
|
||||
description: 'Snapshot of gpt-4 from June 13th 2023 with improved function calling support. Data up to Sep 2021.',
|
||||
contextWindow: 8192,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
isLatest: true,
|
||||
pricing: { chatIn: 30, chatOut: 60 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-0314',
|
||||
label: 'GPT-4 (0314)',
|
||||
description: 'Snapshot of gpt-4 from March 14th 2023 with function calling data. Data up to Sep 2021.',
|
||||
contextWindow: 8192,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 30, chatOut: 60 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
@@ -100,10 +166,12 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
label: 'GPT-4',
|
||||
description: 'Currently points to gpt-4-0613.',
|
||||
symLink: 'gpt-4-0613',
|
||||
// copied
|
||||
contextWindow: 8192,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
hidden: true,
|
||||
// copied from symlinked
|
||||
contextWindow: 8192,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 30, chatOut: 60 },
|
||||
},
|
||||
|
||||
|
||||
@@ -113,20 +181,24 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
label: '3.5-Turbo Instruct',
|
||||
description: 'Similar capabilities as GPT-3 era models. Compatible with legacy Completions endpoint and not Chat Completions.',
|
||||
contextWindow: 4097,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [/* NO: LLM_IF_OAI_Chat,*/ LLM_IF_OAI_Complete],
|
||||
pricing: { chatIn: 1.5, chatOut: 2 },
|
||||
hidden: true,
|
||||
},
|
||||
|
||||
|
||||
// 3.5-Turbo-16k's
|
||||
{
|
||||
isLatest: true,
|
||||
idPrefix: 'gpt-3.5-turbo-0125',
|
||||
label: '3.5-Turbo (0125)',
|
||||
description: 'The latest GPT-3.5 Turbo model with higher accuracy at responding in requested formats and a fix for a bug which caused a text encoding issue for non-English language function calls.',
|
||||
description: 'The latest GPT-3.5 Turbo model with higher accuracy at responding in requested formats and a fix for a bug which caused a text encoding issue for non-English language function calls. Returns a maximum of 4,096 output tokens.',
|
||||
contextWindow: 16385,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
isLatest: true,
|
||||
pricing: { chatIn: 0.5, chatOut: 1.5 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-3.5-turbo-1106',
|
||||
@@ -134,60 +206,23 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
description: 'The latest GPT-3.5 Turbo model with improved instruction following, JSON mode, reproducible outputs, parallel function calling, and more.',
|
||||
contextWindow: 16385,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
pricing: { chatIn: 1, chatOut: 2 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-3.5-turbo-16k-0613',
|
||||
label: '3.5-Turbo 16k (0613)',
|
||||
description: 'Snapshot of gpt-3.5-turbo-16k from June 13th 2023.',
|
||||
contextWindow: 16385,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
isLegacy: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-3.5-turbo-16k',
|
||||
label: '3.5-Turbo 16k',
|
||||
description: 'Currently points to gpt-3.5-turbo-16k-0613.',
|
||||
symLink: 'gpt-3.5-turbo-16k-0613',
|
||||
// copied
|
||||
contextWindow: 16385,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
isLegacy: true,
|
||||
},
|
||||
|
||||
// 3.5-Turbo's (original, 4ks)
|
||||
{
|
||||
idPrefix: 'gpt-3.5-turbo-0613',
|
||||
label: '3.5-Turbo (0613)',
|
||||
description: 'Snapshot of gpt-3.5-turbo from June 13th 2023. Will be deprecated on June 13, 2024.',
|
||||
contextWindow: 4097,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
hidden: true,
|
||||
isLegacy: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-3.5-turbo-0301',
|
||||
label: '3.5-Turbo (0301)',
|
||||
description: 'Snapshot of gpt-3.5-turbo from March 1st 2023. Will be deprecated on June 13th 2024.',
|
||||
contextWindow: 4097,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
isLegacy: true,
|
||||
},
|
||||
{
|
||||
// NOTE: will link to 0125 on Feb 16th 2024 - we are pre-ready for it on the dev branch
|
||||
idPrefix: 'gpt-3.5-turbo',
|
||||
label: '3.5-Turbo',
|
||||
description: 'Currently points to gpt-3.5-turbo-0125.',
|
||||
symLink: 'gpt-3.5-turbo-0125',
|
||||
hidden: true,
|
||||
// copied
|
||||
contextWindow: 16385,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Sep 2021',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
hidden: true,
|
||||
pricing: { chatIn: 0.5, chatOut: 1.5 },
|
||||
},
|
||||
|
||||
|
||||
@@ -216,7 +251,31 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
] as const;
|
||||
];
|
||||
|
||||
const openAIModelsDenyList: string[] = [
|
||||
/* /v1/audio/speech */
|
||||
'tts-1-hd', 'tts-1',
|
||||
/* /v1/embeddings */
|
||||
'text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002',
|
||||
/* /v1/audio/transcriptions, /v1/audio/translations */
|
||||
'whisper-1',
|
||||
/* /v1/images/generations */
|
||||
'dall-e-3', 'dall-e-2',
|
||||
/* /v1/completions (Legacy) */
|
||||
'-turbo-instruct', 'davinci-', 'babbage-',
|
||||
|
||||
// just Legacy models, that we should drop
|
||||
'gpt-3.5-turbo-16k-0613', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-0301', 'gpt-3.5-turbo-16k',
|
||||
];
|
||||
|
||||
export function openAIModelFilter(model: OpenAIWire.Models.ModelDescription) {
|
||||
return !openAIModelsDenyList.some(deny => model.id.includes(deny));
|
||||
}
|
||||
|
||||
export function openAIModelToModelDescription(modelId: string, modelCreated: number, modelUpdated?: number): ModelDescriptionSchema {
|
||||
return fromManualMapping(_knownOpenAIChatModels, modelId, modelCreated, modelUpdated);
|
||||
}
|
||||
|
||||
export function azureModelToModelDescription(azureDeploymentRef: string, openAIModelIdBase: string, modelCreated: number, modelUpdated?: number): ModelDescriptionSchema {
|
||||
// if the deployment name mataches an OpenAI model prefix, use that
|
||||
@@ -224,10 +283,6 @@ export function azureModelToModelDescription(azureDeploymentRef: string, openAIM
|
||||
return fromManualMapping(_knownOpenAIChatModels, known ? azureDeploymentRef : openAIModelIdBase, modelCreated, modelUpdated);
|
||||
}
|
||||
|
||||
export function openAIModelToModelDescription(modelId: string, modelCreated: number, modelUpdated?: number): ModelDescriptionSchema {
|
||||
return fromManualMapping(_knownOpenAIChatModels, modelId, modelCreated, modelUpdated);
|
||||
}
|
||||
|
||||
|
||||
// [LM Studio]
|
||||
export function lmStudioModelToModelDescription(modelId: string): ModelDescriptionSchema {
|
||||
@@ -520,11 +575,11 @@ export function openRouterModelToModelDescription(wireModel: object): ModelDescr
|
||||
const model = wireOpenrouterModelsListOutputSchema.parse(wireModel);
|
||||
|
||||
// parse pricing
|
||||
const pricing = {
|
||||
cpmPrompt: parseFloat(model.pricing.prompt),
|
||||
cpmCompletion: parseFloat(model.pricing.completion),
|
||||
const pricing: ModelDescriptionSchema['pricing'] = {
|
||||
chatIn: parseFloat(model.pricing.prompt) * 1000,
|
||||
chatOut: parseFloat(model.pricing.completion),
|
||||
};
|
||||
const isFree = pricing.cpmPrompt === 0 && pricing.cpmCompletion === 0;
|
||||
const isFree = pricing.chatIn === 0 && pricing.chatOut === 0;
|
||||
|
||||
// openrouter provides the fields we need as part of the model object
|
||||
let label = model.name || model.id.replace('/', ' · ');
|
||||
@@ -558,10 +613,6 @@ const _knownTogetherAIChatModels: ManualMappings = [
|
||||
label: 'Nous Hermes 2 - Mixtral 8x7B-DPO',
|
||||
description: 'Nous Hermes 2 Mixtral 7bx8 DPO is the new flagship Nous Research model trained over the Mixtral 7bx8 MoE LLM. The model was trained on over 1,000,000 entries of primarily GPT-4 generated data, as well as other high quality data from open datasets across the AI landscape, achieving state of the art performance on a variety of tasks.',
|
||||
contextWindow: 32768,
|
||||
pricing: {
|
||||
cpmPrompt: 0.0006,
|
||||
cpmCompletion: 0.0006,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
@@ -569,10 +620,6 @@ const _knownTogetherAIChatModels: ManualMappings = [
|
||||
label: 'Nous Hermes 2 - Mixtral 8x7B-SFT',
|
||||
description: 'Nous Hermes 2 Mixtral 7bx8 SFT is the new flagship Nous Research model trained over the Mixtral 7bx8 MoE LLM. The model was trained on over 1,000,000 entries of primarily GPT-4 generated data, as well as other high quality data from open datasets across the AI landscape, achieving state of the art performance on a variety of tasks.',
|
||||
contextWindow: 32768,
|
||||
pricing: {
|
||||
cpmPrompt: 0.0006,
|
||||
cpmCompletion: 0.0006,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
@@ -580,10 +627,6 @@ const _knownTogetherAIChatModels: ManualMappings = [
|
||||
label: 'Mixtral-8x7B Instruct',
|
||||
description: 'The Mixtral-8x7B Large Language Model (LLM) is a pretrained generative Sparse Mixture of Experts.',
|
||||
contextWindow: 32768,
|
||||
pricing: {
|
||||
cpmPrompt: 0.0006,
|
||||
cpmCompletion: 0.0006,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
@@ -591,10 +634,6 @@ const _knownTogetherAIChatModels: ManualMappings = [
|
||||
label: 'Mistral (7B) Instruct v0.2',
|
||||
description: 'The Mistral-7B-Instruct-v0.2 Large Language Model (LLM) is an improved instruct fine-tuned version of Mistral-7B-Instruct-v0.1.',
|
||||
contextWindow: 32768,
|
||||
pricing: {
|
||||
cpmPrompt: 0.0002,
|
||||
cpmCompletion: 0.0002,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
@@ -602,10 +641,6 @@ const _knownTogetherAIChatModels: ManualMappings = [
|
||||
label: 'Nous Hermes-2 Yi (34B)',
|
||||
description: 'Nous Hermes 2 - Yi-34B is a state of the art Yi Fine-tune',
|
||||
contextWindow: 4097,
|
||||
pricing: {
|
||||
cpmPrompt: 0.0008,
|
||||
cpmCompletion: 0.0008,
|
||||
},
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
] as const;
|
||||
@@ -642,14 +677,14 @@ export function togetherAIModelsToModelDescriptions(wireModels: unknown): ModelD
|
||||
// Perplexity
|
||||
|
||||
const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
|
||||
{
|
||||
id: 'codellama-34b-instruct',
|
||||
label: 'Codellama 34B Instruct (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Codellama 70B Instruct as a replacement.',
|
||||
contextWindow: 16384,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
// {
|
||||
// id: 'codellama-34b-instruct',
|
||||
// label: 'Codellama 34B Instruct (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Codellama 70B Instruct as a replacement.',
|
||||
// contextWindow: 16384,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
{
|
||||
id: 'codellama-70b-instruct',
|
||||
label: 'Codellama 70B Instruct',
|
||||
@@ -657,14 +692,14 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
|
||||
contextWindow: 16384,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
id: 'llama-2-70b-chat',
|
||||
label: 'Llama 2 70B Chat (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try mixtral-8x7b-instruct as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
// {
|
||||
// id: 'llama-2-70b-chat',
|
||||
// label: 'Llama 2 70B Chat (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try mixtral-8x7b-instruct as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
{
|
||||
id: 'mistral-7b-instruct',
|
||||
label: 'Mistral 7B Instruct',
|
||||
@@ -679,54 +714,54 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
|
||||
contextWindow: 16384,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
id: 'pplx-7b-online',
|
||||
label: 'Perplexity 7B Online (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Small Online as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'pplx-70b-online',
|
||||
label: 'Perplexity 70B Online (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Medium Online as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'pplx-8x7b-online',
|
||||
label: 'Perplexity 8x7B Online (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Medium Online as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'pplx-7b-chat',
|
||||
label: 'Perplexity 7B Chat (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Small Chat as a replacement.',
|
||||
contextWindow: 8192,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'pplx-70b-chat',
|
||||
label: 'Perplexity 70B Chat (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Medium Chat as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'pplx-8x7b-chat',
|
||||
label: 'Perplexity 8x7B Chat (deprecated)',
|
||||
description: 'Will be removed on March 15th, 2024. Try Sonar Medium Chat as a replacement.',
|
||||
contextWindow: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
hidden: true,
|
||||
},
|
||||
// {
|
||||
// id: 'pplx-7b-online',
|
||||
// label: 'Perplexity 7B Online (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Small Online as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'pplx-70b-online',
|
||||
// label: 'Perplexity 70B Online (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Medium Online as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'pplx-8x7b-online',
|
||||
// label: 'Perplexity 8x7B Online (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Medium Online as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'pplx-7b-chat',
|
||||
// label: 'Perplexity 7B Chat (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Small Chat as a replacement.',
|
||||
// contextWindow: 8192,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'pplx-70b-chat',
|
||||
// label: 'Perplexity 70B Chat (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Medium Chat as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'pplx-8x7b-chat',
|
||||
// label: 'Perplexity 8x7B Chat (deprecated)',
|
||||
// description: 'Will be removed on March 15th, 2024. Try Sonar Medium Chat as a replacement.',
|
||||
// contextWindow: 4096,
|
||||
// interfaces: [LLM_IF_OAI_Chat],
|
||||
// hidden: true,
|
||||
// },
|
||||
{
|
||||
id: 'sonar-small-chat',
|
||||
label: 'Sonar Small Chat',
|
||||
@@ -745,14 +780,14 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
|
||||
id: 'sonar-small-online',
|
||||
label: 'Sonar Small Online 🌐',
|
||||
description: 'Sonar Small Online',
|
||||
contextWindow: 4096,
|
||||
contextWindow: 12000,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
{
|
||||
id: 'sonar-medium-online',
|
||||
label: 'Sonar Medium Online 🌐',
|
||||
description: 'Sonar Medium Online',
|
||||
contextWindow: 4096,
|
||||
contextWindow: 12000,
|
||||
interfaces: [LLM_IF_OAI_Chat],
|
||||
},
|
||||
];
|
||||
@@ -811,18 +846,21 @@ export function groqModelToModelDescription(_model: unknown): ModelDescriptionSc
|
||||
|
||||
// Helpers
|
||||
|
||||
type ManualMapping = ({ idPrefix: string, isLatest?: boolean, isLegacy?: boolean, symLink?: string } & Omit<ModelDescriptionSchema, 'id' | 'created' | 'updated'>);
|
||||
type ManualMapping = ({ idPrefix: string, isLatest?: boolean, isPreview?: boolean, isLegacy?: boolean, symLink?: string } & Omit<ModelDescriptionSchema, 'id' | 'created' | 'updated'>);
|
||||
type ManualMappings = ManualMapping[];
|
||||
|
||||
function fromManualMapping(mappings: ManualMappings, id: string, created?: number, updated?: number, fallback?: ManualMapping): ModelDescriptionSchema {
|
||||
|
||||
// find the closest known model, or fall back, or take the last
|
||||
const known = mappings.find(base => id.startsWith(base.idPrefix)) || fallback || mappings[mappings.length - 1];
|
||||
const known = mappings.find(base => id === base.idPrefix)
|
||||
|| mappings.find(base => id.startsWith(base.idPrefix))
|
||||
|| fallback
|
||||
|| mappings[mappings.length - 1];
|
||||
|
||||
// label for symlinks
|
||||
let label = known.label;
|
||||
if (known.symLink && id === known.idPrefix)
|
||||
label = `🔗 ${known.label} → ${known.symLink}`;
|
||||
label = `🔗 ${known.label} → ${known.symLink/*.replace(known.idPrefix, '')*/}`;
|
||||
|
||||
// check whether this is a partial map, which indicates an unknown/new variant
|
||||
const suffix = id.slice(known.idPrefix.length).trim();
|
||||
|
||||
@@ -11,13 +11,19 @@ import { Brand } from '~/common/app.config';
|
||||
import { fixupHost } from '~/common/util/urlUtils';
|
||||
|
||||
import { OpenAIWire, WireOpenAICreateImageOutput, wireOpenAICreateImageOutputSchema, WireOpenAICreateImageRequest } from './openai.wiretypes';
|
||||
import { azureModelToModelDescription, groqModelToModelDescription, lmStudioModelToModelDescription, localAIModelToModelDescription, mistralModelsSort, mistralModelToModelDescription, oobaboogaModelToModelDescription, openAIModelToModelDescription, openRouterModelFamilySortFn, openRouterModelToModelDescription, perplexityAIModelDescriptions, perplexityAIModelSort, togetherAIModelsToModelDescriptions } from './models.data';
|
||||
import { azureModelToModelDescription, groqModelToModelDescription, lmStudioModelToModelDescription, localAIModelToModelDescription, mistralModelsSort, mistralModelToModelDescription, oobaboogaModelToModelDescription, openAIModelFilter, openAIModelToModelDescription, openRouterModelFamilySortFn, openRouterModelToModelDescription, perplexityAIModelDescriptions, perplexityAIModelSort, togetherAIModelsToModelDescriptions } from './models.data';
|
||||
import { llmsChatGenerateWithFunctionsOutputSchema, llmsListModelsOutputSchema, ModelDescriptionSchema } from '../llm.server.types';
|
||||
import { wilreLocalAIModelsApplyOutputSchema, wireLocalAIModelsAvailableOutputSchema, wireLocalAIModelsListOutputSchema } from './localai.wiretypes';
|
||||
|
||||
|
||||
// module configuration
|
||||
const ABERRATION_FIXUP_SQUASH = '\n\n\n---\n\n\n';
|
||||
|
||||
|
||||
const openAIDialects = z.enum([
|
||||
'azure', 'groq', 'lmstudio', 'localai', 'mistral', 'oobabooga', 'openai', 'openrouter', 'perplexity', 'togetherai',
|
||||
]);
|
||||
type OpenAIDialects = z.infer<typeof openAIDialects>;
|
||||
|
||||
export const openAIAccessSchema = z.object({
|
||||
dialect: openAIDialects,
|
||||
@@ -194,7 +200,7 @@ export const llmOpenAIRouter = createTRPCRouter({
|
||||
models = openAIModels
|
||||
|
||||
// limit to only 'gpt' and 'non instruct' models
|
||||
.filter(model => model.id.includes('gpt') && !model.id.includes('-instruct'))
|
||||
.filter(openAIModelFilter)
|
||||
|
||||
// to model description
|
||||
.map((model): ModelDescriptionSchema => openAIModelToModelDescription(model.id, model.created))
|
||||
@@ -205,12 +211,18 @@ export const llmOpenAIRouter = createTRPCRouter({
|
||||
// fix the OpenAI model names to be chronologically sorted
|
||||
function remapReleaseDate(id: string): string {
|
||||
return id
|
||||
.replace('0314', '230314')
|
||||
.replace('0613', '230613')
|
||||
.replace('1106', '231106')
|
||||
.replace('0125', '240125');
|
||||
.replace('0314', '2023-03-14')
|
||||
.replace('0613', '2023-06-13')
|
||||
.replace('1106', '2023-11-06')
|
||||
.replace('0125', '2024-01-25');
|
||||
}
|
||||
|
||||
// stuff with '[legacy]' at the bottom
|
||||
const aLegacy = a.label.includes('[legacy]');
|
||||
const bLegacy = b.label.includes('[legacy]');
|
||||
if (aLegacy !== bLegacy)
|
||||
return aLegacy ? 1 : -1;
|
||||
|
||||
// due to using by-label, sorting doesn't require special cases anymore
|
||||
return remapReleaseDate(b.label).localeCompare(remapReleaseDate(a.label));
|
||||
|
||||
@@ -257,10 +269,9 @@ export const llmOpenAIRouter = createTRPCRouter({
|
||||
const { access, model, history, functions, forceFunctionName } = input;
|
||||
const isFunctionsCall = !!functions && functions.length > 0;
|
||||
|
||||
const completionsBody = openAIChatCompletionPayload(access.dialect, model, history, isFunctionsCall ? functions : null, forceFunctionName ?? null, 1, false);
|
||||
const wireCompletions = await openaiPOST<OpenAIWire.ChatCompletion.Response, OpenAIWire.ChatCompletion.Request>(
|
||||
access, model.id,
|
||||
openAIChatCompletionPayload(model, history, isFunctionsCall ? functions : null, forceFunctionName ?? null, 1, false),
|
||||
'/v1/chat/completions',
|
||||
access, model.id, completionsBody, '/v1/chat/completions',
|
||||
);
|
||||
|
||||
// expect a single output
|
||||
@@ -565,7 +576,41 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu
|
||||
}
|
||||
}
|
||||
|
||||
export function openAIChatCompletionPayload(model: OpenAIModelSchema, history: OpenAIHistorySchema, functions: OpenAIFunctionsSchema | null, forceFunctionName: string | null, n: number, stream: boolean): OpenAIWire.ChatCompletion.Request {
|
||||
|
||||
export function openAIChatCompletionPayload(dialect: OpenAIDialects, model: OpenAIModelSchema, history: OpenAIHistorySchema, functions: OpenAIFunctionsSchema | null, forceFunctionName: string | null, n: number, stream: boolean): OpenAIWire.ChatCompletion.Request {
|
||||
|
||||
// Hotfixes to comply with API restrictions
|
||||
const hotfixAlternateUARoles = dialect === 'perplexity';
|
||||
const hotfixSkipEmptyMessages = dialect === 'perplexity';
|
||||
const performFixes = hotfixAlternateUARoles || hotfixSkipEmptyMessages;
|
||||
|
||||
// recreate history for hotfixes
|
||||
// NOTE: we do not like that we have to introduce aberrations by altering history, but it's a necessary evil
|
||||
if (performFixes) {
|
||||
history = history.reduce((acc, historyItem) => {
|
||||
|
||||
// skip empty messages
|
||||
if (hotfixSkipEmptyMessages && !historyItem.content.trim()) return acc;
|
||||
|
||||
// if the current item has the same role as the last item, concatenate their content
|
||||
if (hotfixAlternateUARoles && acc.length > 0) {
|
||||
const lastItem = acc[acc.length - 1];
|
||||
if (lastItem.role === historyItem.role) {
|
||||
// replace the last item with the new concatenatedItem
|
||||
acc[acc.length - 1] = {
|
||||
...lastItem,
|
||||
content: lastItem.content + ABERRATION_FIXUP_SQUASH + historyItem.content,
|
||||
};
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's not a case for concatenation, just push the current item to the accumulator
|
||||
acc.push(historyItem);
|
||||
return acc;
|
||||
}, [] as OpenAIHistorySchema);
|
||||
}
|
||||
|
||||
return {
|
||||
model: model.id,
|
||||
messages: history,
|
||||
|
||||
@@ -47,8 +47,9 @@ export type DLLMId = string;
|
||||
|
||||
// Model interfaces (chat, and function calls) - here as a preview, will be used more broadly in the future
|
||||
export const LLM_IF_OAI_Chat = 'oai-chat';
|
||||
export const LLM_IF_OAI_Vision = 'oai-vision';
|
||||
export const LLM_IF_OAI_Fn = 'oai-fn';
|
||||
export const LLM_IF_OAI_Json = 'oai-chat-json';
|
||||
export const LLM_IF_OAI_Vision = 'oai-chat-vision';
|
||||
export const LLM_IF_OAI_Fn = 'oai-chat-fn';
|
||||
export const LLM_IF_OAI_Complete = 'oai-complete';
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { Alert } from '@mui/joy';
|
||||
|
||||
import { ExternalLink } from '~/common/components/ExternalLink';
|
||||
import { FormInputKey } from '~/common/components/forms/FormInputKey';
|
||||
import { FormTextField } from '~/common/components/forms/FormTextField';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
@@ -40,12 +41,12 @@ export function AnthropicSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
|
||||
<Alert variant='soft' color='success'>
|
||||
<div>
|
||||
Note: <strong>Claude-3</strong> models are now supported.
|
||||
Enjoy <b>Opus</b>, <b>Sonnet</b> and <b>Haiku</b>. Anthropic <ExternalLink level='body-sm' href='https://status.anthropic.com/'>server status</ExternalLink>.
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
<FormInputKey
|
||||
id='anthropic-key' label={!!anthropicHost ? 'API Key' : 'Anthropic API Key'}
|
||||
autoCompleteId='anthropic-key' label={!!anthropicHost ? 'API Key' : 'Anthropic API Key'}
|
||||
rightLabel={<>{needsUserKey
|
||||
? !anthropicKey && <Link level='body-sm' href='https://www.anthropic.com/earlyaccess' target='_blank'>request Key</Link>
|
||||
: '✔️ already set in server'
|
||||
@@ -57,6 +58,7 @@ export function AnthropicSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='anthropic-host'
|
||||
title='API Host'
|
||||
description={<>e.g., <Link level='body-sm' href='https://github.com/enricoros/big-agi/blob/main/docs/config-aws-bedrock.md' target='_blank'>bedrock-claude</Link></>}
|
||||
placeholder='deployment.service.region.amazonaws.com'
|
||||
@@ -66,6 +68,7 @@ export function AnthropicSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>}
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='anthropic-helicone-key'
|
||||
title='Helicone Key' disabled={!!anthropicHost}
|
||||
description={<>Generate <Link level='body-sm' href='https://www.helicone.ai/keys' target='_blank'>here</Link></>}
|
||||
placeholder='sk-...'
|
||||
|
||||
+2
-1
@@ -36,6 +36,7 @@ export function AzureSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormTextField
|
||||
autoCompleteId='azure-endpoint'
|
||||
title='Azure Endpoint'
|
||||
description={<Link level='body-sm' href='https://github.com/enricoros/big-agi/blob/main/docs/config-azure-openai.md' target='_blank'>configuration</Link>}
|
||||
placeholder='https://your-resource-name.openai.azure.com/'
|
||||
@@ -45,7 +46,7 @@ export function AzureSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>
|
||||
|
||||
<FormInputKey
|
||||
id='azure-key' label='Azure Key'
|
||||
autoCompleteId='azure-key' label='Azure Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !azureKey && <Link level='body-sm' href='https://azure.microsoft.com/en-us/products/ai-services/openai-service' target='_blank'>request Key</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ export function GeminiSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='gemini-key' label='Gemini API Key'
|
||||
autoCompleteId='gemini-key' label='Gemini API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !geminiKey && <Link level='body-sm' href={GEMINI_API_KEY_LINK} target='_blank'>request Key</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ export function GroqSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='groq-key' label='Groq API Key'
|
||||
autoCompleteId='groq-key' label='Groq API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !groqKey && <Link level='body-sm' href={GROQ_REG_LINK} target='_blank'>API keys</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
@@ -55,7 +55,7 @@ export function LMStudioSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
</Typography>
|
||||
|
||||
<FormInputKey
|
||||
id='lmstudio-url' label='LM Studio API'
|
||||
autoCompleteId='lmstudio-url' label='LM Studio API'
|
||||
required noKey
|
||||
rightLabel={<Link level='body-sm' href='https://github.com/enricoros/big-agi/blob/main/docs/config-local-lmstudio.md' target='_blank'>Learn more</Link>}
|
||||
placeholder='e.g., http://127.0.0.1:1234'
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ export function LocalAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
</Typography>
|
||||
|
||||
<FormInputKey
|
||||
id='localai-host' label='LocalAI URL'
|
||||
autoCompleteId='localai-host' label='LocalAI URL'
|
||||
placeholder='e.g., http://127.0.0.1:8080'
|
||||
noKey
|
||||
required={userHostRequired}
|
||||
@@ -86,7 +86,7 @@ export function LocalAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>
|
||||
|
||||
<FormInputKey
|
||||
id='localai-host' label='(optional) API Key'
|
||||
autoCompleteId='localai-key' label='(optional) API Key'
|
||||
placeholder='...'
|
||||
required={false}
|
||||
rightLabel={backendHasKey ? '✔️ already set in server' : undefined}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ export function MistralSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='mistral-key' label='Mistral Key'
|
||||
autoCompleteId='mistral-key' label='Mistral Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !mistralKey && <Link level='body-sm' href={MISTRAL_REG_LINK} target='_blank'>request Key</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
@@ -39,6 +39,7 @@ export function OllamaSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormTextField
|
||||
autoCompleteId='ollama-host'
|
||||
title='Ollama Host'
|
||||
description={<Link level='body-sm' href='https://github.com/enricoros/big-agi/blob/main/docs/config-local-ollama.md' target='_blank'>information</Link>}
|
||||
placeholder='http://127.0.0.1:11434'
|
||||
|
||||
@@ -37,6 +37,7 @@ export function OobaboogaSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
</Typography>
|
||||
|
||||
<FormTextField
|
||||
autoCompleteId='oobabooga-host'
|
||||
title='API Base'
|
||||
description='Excluding /v1'
|
||||
placeholder='http://127.0.0.1:5000'
|
||||
|
||||
+6
-3
@@ -15,7 +15,7 @@ import { DModelSourceId } from '../../store-llms';
|
||||
import { useLlmUpdateModels } from '../useLlmUpdateModels';
|
||||
import { useSourceSetup } from '../useSourceSetup';
|
||||
|
||||
import { isValidOpenAIApiKey, ModelVendorOpenAI } from './openai.vendor';
|
||||
import { ModelVendorOpenAI } from './openai.vendor';
|
||||
|
||||
|
||||
// avoid repeating it all over
|
||||
@@ -34,7 +34,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
// derived state
|
||||
const { oaiKey, oaiOrg, oaiHost, heliKey, moderationCheck } = access;
|
||||
|
||||
const keyValid = isValidOpenAIApiKey(oaiKey);
|
||||
const keyValid = true; //isValidOpenAIApiKey(oaiKey);
|
||||
const keyError = (/*needsUserKey ||*/ !!oaiKey) && !keyValid;
|
||||
const shallFetchSucceed = oaiKey ? keyValid : !needsUserKey;
|
||||
|
||||
@@ -45,7 +45,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='openai-key' label='API Key'
|
||||
autoCompleteId='openai-key' label='API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !oaiKey && <><Link level='body-sm' href='https://platform.openai.com/account/api-keys' target='_blank'>create Key</Link> and <Link level='body-sm' href='https://openai.com/waitlist/gpt-4-api' target='_blank'>apply to GPT-4</Link></>
|
||||
: '✔️ already set in server'
|
||||
@@ -57,6 +57,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='openai-host'
|
||||
title='API Endpoint'
|
||||
tooltip={`An OpenAI compatible endpoint to be used in place of 'api.openai.com'.\n\nCould be used for Helicone, Cloudflare, or other OpenAI compatible cloud or local services.\n\nExamples:\n - ${HELICONE_OPENAI_HOST}\n - localhost:1234`}
|
||||
description={<><Link level='body-sm' href='https://www.helicone.ai' target='_blank'>Helicone</Link>, <Link level='body-sm' href='https://developers.cloudflare.com/ai-gateway/' target='_blank'>Cloudflare</Link></>}
|
||||
@@ -66,6 +67,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>}
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='openai-org'
|
||||
title='Organization ID'
|
||||
description={<Link level='body-sm' href={`${Brand.URIs.OpenRepo}/issues/63`} target='_blank'>What is this</Link>}
|
||||
placeholder='Optional, for enterprise users'
|
||||
@@ -74,6 +76,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
/>}
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='openai-helicone-key'
|
||||
title='Helicone Key'
|
||||
description={<>Generate <Link level='body-sm' href='https://www.helicone.ai/keys' target='_blank'>here</Link></>}
|
||||
placeholder='sk-...'
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ export const FALLBACK_LLM_TEMPERATURE = 0.5;
|
||||
|
||||
|
||||
// special symbols
|
||||
export const isValidOpenAIApiKey = (apiKey?: string) => !!apiKey && apiKey.startsWith('sk-') && apiKey.length > 40;
|
||||
// export const isValidOpenAIApiKey = (apiKey?: string) => !!apiKey && apiKey.startsWith('sk-') && apiKey.length > 40;
|
||||
|
||||
export interface SourceSetupOpenAI {
|
||||
oaiKey: string;
|
||||
|
||||
@@ -53,7 +53,7 @@ export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
</Typography>
|
||||
|
||||
<FormInputKey
|
||||
id='openrouter-key' label='OpenRouter API Key'
|
||||
autoCompleteId='openrouter-key' label='OpenRouter API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !oaiKey && <Link level='body-sm' href='https://openrouter.ai/keys' target='_blank'>your keys</Link>
|
||||
: '✔️ already set in server'
|
||||
|
||||
@@ -39,7 +39,7 @@ export function PerplexitySourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='perplexity-key' label='Perplexity API Key'
|
||||
autoCompleteId='perplexity-key' label='Perplexity API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !perplexityKey && <Link level='body-sm' href={PERPLEXITY_REG_LINK} target='_blank'>API keys</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
@@ -45,7 +45,7 @@ export function TogetherAISourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
return <>
|
||||
|
||||
<FormInputKey
|
||||
id='togetherai-key' label='Together AI Key'
|
||||
autoCompleteId='togetherai-key' label='Together AI Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !togetherKey && <Link level='body-sm' href={TOGETHERAI_REG_LINK} target='_blank'>request Key</Link>
|
||||
: '✔️ already set in server'}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ function modelDescriptionToDLLMOpenAIOptions<TSourceSetup, TLLMOptions>(model: M
|
||||
maxOutputTokens,
|
||||
hidden: !!model.hidden,
|
||||
|
||||
isFree: model.pricing?.cpmPrompt === 0 && model.pricing?.cpmCompletion === 0,
|
||||
isFree: model.pricing?.chatIn === 0 && model.pricing?.chatOut === 0,
|
||||
|
||||
sId: source.id,
|
||||
_source: source,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { DLLMId, getKnowledgeMapCutoff } from '~/modules/llms/store-llms';
|
||||
|
||||
import { browserLangOrUS } from '~/common/util/pwaUtils';
|
||||
|
||||
/*type Variables =
|
||||
| '{{Today}}'
|
||||
| '{{Cutoff}}'
|
||||
@@ -48,10 +50,9 @@ export function bareBonesPromptMixer(_template: string, assistantLlmId: DLLMId |
|
||||
|
||||
// {{LocaleNow}} - enough information to get on the same page with the user
|
||||
if (mixed.includes('{{LocaleNow}}')) {
|
||||
const userLocale = navigator.language || 'en-US';
|
||||
// const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
||||
// Format the current date and time according to the user's locale and timezone
|
||||
const formatter = new Intl.DateTimeFormat(userLocale, {
|
||||
const formatter = new Intl.DateTimeFormat(browserLangOrUS, {
|
||||
weekday: 'short', // Full name of the day of the week
|
||||
year: 'numeric', // Numeric year
|
||||
month: 'short', // Full name of the month
|
||||
|
||||
@@ -79,7 +79,7 @@ export function ProdiaSettings(props: { noSkipKey?: boolean }) {
|
||||
return <>
|
||||
|
||||
{!backendHasProdia && !!props.noSkipKey && <FormInputKey
|
||||
id='prodia-key' label='Prodia API Key'
|
||||
autoCompleteId='prodia-key' label='Prodia API Key'
|
||||
rightLabel={backendHasProdia ? '✔️ already set in server' : 'required'}
|
||||
value={apiKey} onChange={setApiKey}
|
||||
required={!backendHasProdia} isError={!isValidKey}
|
||||
|
||||
@@ -7,6 +7,8 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||
import { getBackendCapabilities } from '~/modules/backend/store-backend-capabilities';
|
||||
|
||||
import { DConversationId, getConversation } from '~/common/state/store-chats';
|
||||
import { GoodTooltip } from '~/common/components/GoodTooltip';
|
||||
import { KeyStroke } from '~/common/components/KeyStroke';
|
||||
|
||||
import { ChatLinkExport } from './link/ChatLinkExport';
|
||||
import { PublishExport } from './publish/PublishExport';
|
||||
@@ -80,15 +82,17 @@ export function ExportChats(props: { config: ExportConfig, onClose: () => void }
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant='soft' disabled={!hasConversation}
|
||||
color={downloadedJSONState === 'ok' ? 'success' : downloadedJSONState === 'fail' ? 'warning' : 'primary'}
|
||||
endDecorator={downloadedJSONState === 'ok' ? <DoneIcon /> : downloadedJSONState === 'fail' ? '✘' : <FileDownloadIcon />}
|
||||
sx={{ minWidth: 240, justifyContent: 'space-between' }}
|
||||
onClick={handleDownloadConversationJSON}
|
||||
>
|
||||
Download · JSON
|
||||
</Button>
|
||||
<GoodTooltip title={<KeyStroke dark combo='Ctrl + S' />}>
|
||||
<Button
|
||||
variant='soft' disabled={!hasConversation}
|
||||
color={downloadedJSONState === 'ok' ? 'success' : downloadedJSONState === 'fail' ? 'warning' : 'primary'}
|
||||
endDecorator={downloadedJSONState === 'ok' ? <DoneIcon /> : downloadedJSONState === 'fail' ? '✘' : <FileDownloadIcon />}
|
||||
sx={{ minWidth: 240, justifyContent: 'space-between' }}
|
||||
onClick={handleDownloadConversationJSON}
|
||||
>
|
||||
Download · JSON
|
||||
</Button>
|
||||
</GoodTooltip>
|
||||
|
||||
<Button
|
||||
variant='soft' disabled={!hasConversation}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import { fileOpen, FileWithHandle } from 'browser-fs-access';
|
||||
|
||||
import { Box, Button, FormControl, Input, Sheet, Textarea, Typography } from '@mui/joy';
|
||||
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||
|
||||
import { Brand } from '~/common/app.config';
|
||||
import { FormRadioOption } from '~/common/components/forms/FormRadioControl';
|
||||
import { GoodTooltip } from '~/common/components/GoodTooltip';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { KeyStroke } from '~/common/components/KeyStroke';
|
||||
import { OpenAIIcon } from '~/common/components/icons/vendors/OpenAIIcon';
|
||||
import { apiAsyncNode } from '~/common/util/trpc.client';
|
||||
import { createDConversation, createDMessage, DConversationId, DMessage, useChatStore } from '~/common/state/store-chats';
|
||||
import { useFormRadio } from '~/common/components/forms/useFormRadio';
|
||||
|
||||
import type { ChatGptSharedChatSchema } from './server/chatgpt';
|
||||
import { loadAllConversationsFromJson } from './trade.client';
|
||||
import { openAndLoadConversations } from './trade.client';
|
||||
|
||||
import { ImportedOutcome, ImportOutcomeModal } from './ImportOutcomeModal';
|
||||
|
||||
@@ -47,36 +48,11 @@ export function ImportChats(props: { onConversationActivate: (conversationId: DC
|
||||
|
||||
|
||||
const handleImportFromFiles = async () => {
|
||||
// pick file(s)
|
||||
let blobs: FileWithHandle[];
|
||||
try {
|
||||
blobs = await fileOpen({ description: `${Brand.Title.Base} JSON`, mimeTypes: ['application/json'], multiple: true, startIn: 'downloads' });
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
const outcome = await openAndLoadConversations(true);
|
||||
|
||||
// begin
|
||||
const outcome: ImportedOutcome = { conversations: [] };
|
||||
|
||||
// unroll files to conversations
|
||||
for (const blob of blobs) {
|
||||
const fileName = blob.name || 'unknown file';
|
||||
try {
|
||||
const fileString = await blob.text();
|
||||
const fileObject = JSON.parse(fileString);
|
||||
loadAllConversationsFromJson(fileName, fileObject, outcome);
|
||||
} catch (error: any) {
|
||||
outcome.conversations.push({ success: false, fileName, error: `Invalid file: ${error?.message || error?.toString() || 'unknown error'}` });
|
||||
}
|
||||
}
|
||||
|
||||
// import conversations (warning - will overwrite things)
|
||||
for (const conversation of [...outcome.conversations].reverse()) {
|
||||
if (conversation.success) {
|
||||
const conversationId: DConversationId = useChatStore.getState().importConversation(conversation.conversation, false);
|
||||
props.onConversationActivate(conversationId);
|
||||
}
|
||||
}
|
||||
// activate the last (most recent) imported conversation
|
||||
if (outcome?.activateConversationId)
|
||||
props.onConversationActivate(outcome.activateConversationId);
|
||||
|
||||
// show the outcome of the import
|
||||
setImportOutcome(outcome);
|
||||
@@ -90,7 +66,7 @@ export function ImportChats(props: { onConversationActivate: (conversationId: DC
|
||||
if ((isUrl && !chatGptUrlValid) || (isSource && !chatGptSource))
|
||||
return;
|
||||
|
||||
const outcome: ImportedOutcome = { conversations: [] };
|
||||
const outcome: ImportedOutcome = { conversations: [], activateConversationId: null };
|
||||
|
||||
// load the conversation
|
||||
let conversationId: DConversationId, data: ChatGptSharedChatSchema;
|
||||
@@ -152,12 +128,14 @@ export function ImportChats(props: { onConversationActivate: (conversationId: DC
|
||||
Select where to <strong>import from</strong>:
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant='soft' endDecorator={<FileUploadIcon />} sx={{ minWidth: 240, justifyContent: 'space-between' }}
|
||||
onClick={handleImportFromFiles}
|
||||
>
|
||||
{Brand.Title.Base} · JSON
|
||||
</Button>
|
||||
<GoodTooltip title={<KeyStroke dark combo='Ctrl + O' />}>
|
||||
<Button
|
||||
variant='soft' endDecorator={<FileUploadIcon />} sx={{ minWidth: 240, justifyContent: 'space-between' }}
|
||||
onClick={handleImportFromFiles}
|
||||
>
|
||||
{Brand.Title.Base} · JSON
|
||||
</Button>
|
||||
</GoodTooltip>
|
||||
|
||||
{!chatGptEdit && (
|
||||
<Button
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
import { Alert, Box, Divider, IconButton, List, ListItem, Tooltip, Typography } from '@mui/joy';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
import type { DConversation } from '~/common/state/store-chats';
|
||||
import type { DConversation, DConversationId } from '~/common/state/store-chats';
|
||||
import { GoodModal } from '~/common/components/GoodModal';
|
||||
import { copyToClipboard } from '~/common/util/clipboardUtils';
|
||||
|
||||
@@ -12,6 +12,7 @@ type ConversationOutcome = {
|
||||
success: true;
|
||||
fileName: string;
|
||||
conversation: DConversation;
|
||||
importedConversationId?: DConversationId;
|
||||
} | {
|
||||
success: false;
|
||||
fileName: string;
|
||||
@@ -21,6 +22,7 @@ type ConversationOutcome = {
|
||||
|
||||
export interface ImportedOutcome {
|
||||
conversations: ConversationOutcome[];
|
||||
activateConversationId: DConversationId | null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fileSave } from 'browser-fs-access';
|
||||
import { fileOpen, fileSave, FileWithHandle } from 'browser-fs-access';
|
||||
|
||||
import { defaultSystemPurposeId, SystemPurposeId, SystemPurposes } from '../../data';
|
||||
|
||||
@@ -9,17 +9,66 @@ import { DFolder, useFolderStore } from '~/common/state/store-folders';
|
||||
import { capitalizeFirstLetter } from '~/common/util/textUtils';
|
||||
import { conversationTitle, DConversation, type DConversationId, DMessage, useChatStore } from '~/common/state/store-chats';
|
||||
import { prettyBaseModel } from '~/common/util/modelUtils';
|
||||
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
|
||||
|
||||
import { ImportedOutcome } from './ImportOutcomeModal';
|
||||
|
||||
|
||||
/// IMPORT ///
|
||||
|
||||
/**
|
||||
* Open a file dialog and load all conversations from the selected JSON files
|
||||
*/
|
||||
export async function openAndLoadConversations(preventClash: boolean = false): Promise<ImportedOutcome | null> {
|
||||
const outcome: ImportedOutcome = { conversations: [], activateConversationId: null };
|
||||
|
||||
let blobs: FileWithHandle[];
|
||||
try {
|
||||
blobs = await fileOpen({
|
||||
description: `${Brand.Title.Base} JSON Conversations`,
|
||||
mimeTypes: ['application/json', 'application/big-agi'],
|
||||
multiple: true,
|
||||
});
|
||||
} catch (error) {
|
||||
// User closed the dialog
|
||||
return null;
|
||||
}
|
||||
|
||||
// unroll files to conversations
|
||||
for (const blob of blobs) {
|
||||
const fileName = blob.name || 'unknown file';
|
||||
try {
|
||||
const fileString = await blob.text();
|
||||
const fileObject = JSON.parse(fileString);
|
||||
loadAllConversationsFromJson(fileName, fileObject, outcome);
|
||||
} catch (error: any) {
|
||||
outcome.conversations.push({
|
||||
success: false,
|
||||
fileName,
|
||||
error: `Invalid file: ${error?.message || error?.toString() || 'unknown error'}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// import conversations
|
||||
for (const cOutcome of [...outcome.conversations].reverse()) {
|
||||
if (!cOutcome.success)
|
||||
continue;
|
||||
cOutcome.importedConversationId = useChatStore.getState().importConversation(cOutcome.conversation, preventClash);
|
||||
// the last successfully imported is the one to activate
|
||||
if (cOutcome.importedConversationId)
|
||||
outcome.activateConversationId = cOutcome.importedConversationId;
|
||||
}
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restores all conversations in a JSON
|
||||
* - supports both ExportedConversationJsonV1, and ExportedAllJsonV1 files
|
||||
*/
|
||||
export function loadAllConversationsFromJson(fileName: string, obj: any, outcome: ImportedOutcome) {
|
||||
function loadAllConversationsFromJson(fileName: string, obj: any, outcome: ImportedOutcome) {
|
||||
// heuristics
|
||||
const hasConversations = obj.hasOwnProperty('conversations');
|
||||
const hasMessages = obj.hasOwnProperty('messages');
|
||||
@@ -106,9 +155,12 @@ export async function downloadAllConversationsJson() {
|
||||
const json = JSON.stringify(payload);
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
|
||||
// link to begin the download
|
||||
const isoDate = new Date().toISOString().replace(/:/g, '-');
|
||||
await fileSave(blob, { fileName: `conversations-${isoDate}.json`, extensions: ['.json'] });
|
||||
// save file
|
||||
await fileSave(blob, {
|
||||
fileName: `conversations_${window?.location?.hostname || 'all'}_${payload.conversations.length}_${prettyTimestampForFilenames(false)}.agi.json`,
|
||||
// mimeTypes: ['application/json', 'application/big-agi'],
|
||||
extensions: ['.json'],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,11 +186,14 @@ export async function downloadConversation(conversation: DConversation, format:
|
||||
throw new Error(`Invalid download format: ${format}`);
|
||||
}
|
||||
|
||||
// bonify title for saving to file (spaces to dashes, etc)
|
||||
const fileTitle = conversationTitle(conversation).replace(/[^a-z0-9]/gi, '_').toLowerCase() || 'untitled';
|
||||
// const fileConvId = conversation.id.slice(0, 8);
|
||||
const fileTitle = conversationTitle(conversation).replace(/[^a-z0-9]/gi, '-').toLowerCase() || 'untitled';
|
||||
|
||||
// link to begin the download
|
||||
await fileSave(blob, { fileName: `conversation-${fileTitle ? fileTitle + '-' : ''}${conversation.id}${extension}`, extensions: [extension] });
|
||||
// save file
|
||||
await fileSave(blob, {
|
||||
fileName: `conversation_${fileTitle}_${prettyTimestampForFilenames(false)}.agi${extension}`,
|
||||
extensions: [extension],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
// It is used by the Big-AGI Persona Creator to create a character sheet.
|
||||
|
||||
import * as React from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { apiQuery } from '~/common/util/trpc.client';
|
||||
import { frontendSideFetch } from '~/common/util/clientFetchers';
|
||||
|
||||
import { fetchYouTubeTranscript } from './youtube.fetcher';
|
||||
import { apiAsync } from '~/common/util/trpc.client';
|
||||
|
||||
// configuration
|
||||
const USE_FRONTEND_FETCH = false;
|
||||
|
||||
|
||||
export interface YTVideoTranscript {
|
||||
@@ -19,15 +26,15 @@ export function useYouTubeTranscript(videoID: string | null, onNewTranscript: (t
|
||||
const [transcript, setTranscript] = React.useState<YTVideoTranscript | null>(null);
|
||||
|
||||
// data
|
||||
const { data, isFetching, isError, error } = apiQuery.youtube.getTranscript.useQuery({
|
||||
videoId: videoID || '',
|
||||
}, {
|
||||
const { data, isFetching, isError, error } = useQuery({
|
||||
enabled: !!videoID,
|
||||
refetchOnWindowFocus: false,
|
||||
queryKey: ['transcript', videoID],
|
||||
queryFn: async () => USE_FRONTEND_FETCH
|
||||
? fetchYouTubeTranscript(videoID!, url => frontendSideFetch(url).then(res => res.text()))
|
||||
: apiAsync.youtube.getTranscript.query({ videoId: videoID! }),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
|
||||
// update the transcript when the underlying data changes
|
||||
React.useEffect(() => {
|
||||
if (!data) {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const youtubeTranscriptionSchema = z.object({
|
||||
wireMagic: z.literal('pb3'),
|
||||
events: z.array(
|
||||
z.object({
|
||||
tStartMs: z.number(),
|
||||
dDurationMs: z.number().optional(),
|
||||
aAppend: z.number().optional(),
|
||||
segs: z.array(
|
||||
z.object({
|
||||
utf8: z.string(),
|
||||
tOffsetMs: z.number().optional(),
|
||||
}),
|
||||
).optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
function extractFromTo(html: string, from: string, to: string, label: string): string {
|
||||
const indexStart = html.indexOf(from);
|
||||
const indexEnd = html.indexOf(to, indexStart);
|
||||
if (indexStart < 0 || indexEnd <= indexStart)
|
||||
throw new Error(`[YouTube API Issue] Could not find '${label}'`);
|
||||
return html.substring(indexStart, indexEnd);
|
||||
}
|
||||
|
||||
|
||||
interface YouTubeTranscriptData {
|
||||
videoId: string;
|
||||
videoTitle: string;
|
||||
thumbnailUrl: string;
|
||||
transcript: string;
|
||||
}
|
||||
|
||||
|
||||
export async function fetchYouTubeTranscript(videoId: string, fetchTextFn: (url: string) => Promise<string>): Promise<YouTubeTranscriptData> {
|
||||
|
||||
// 1. find the captions URL within the video HTML page
|
||||
const html = await fetchTextFn(`https://www.youtube.com/watch?v=${videoId}`);
|
||||
|
||||
const captionsUrlEnc = extractFromTo(html, 'https://www.youtube.com/api/timedtext', '"', 'Captions URL');
|
||||
const captionsUrl = decodeURIComponent(captionsUrlEnc.replaceAll('\\u0026', '&'));
|
||||
const thumbnailUrl = extractFromTo(html, 'https://i.ytimg.com/vi/', '"', 'Thumbnail URL').replaceAll('maxres', 'hq');
|
||||
const videoTitle = extractFromTo(html, '<title>', '</title>', 'Video Title').slice(7).replaceAll(' - YouTube', '').trim();
|
||||
|
||||
// 2. fetch the captions
|
||||
// note: the desktop player appends this much: &fmt=json3&xorb=2&xobt=3&xovt=3&cbr=Chrome&cbrver=114.0.0.0&c=WEB&cver=2.20230628.07.00&cplayer=UNIPLAYER&cos=Windows&cosver=10.0&cplatform=DESKTOP
|
||||
const captions = await fetchTextFn(captionsUrl + `&fmt=json3`);
|
||||
|
||||
let captionsJson: any;
|
||||
try {
|
||||
captionsJson = JSON.parse(captions);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('[YouTube API Issue] Could not parse the captions');
|
||||
}
|
||||
const safeData = youtubeTranscriptionSchema.safeParse(captionsJson);
|
||||
if (!safeData.success) {
|
||||
console.error(safeData.error);
|
||||
throw new Error('[YouTube API Issue] Could not verify the captions');
|
||||
}
|
||||
|
||||
// 3. flatten to text
|
||||
const transcript = safeData.data.events
|
||||
.flatMap(event => event.segs ?? [])
|
||||
.map(seg => seg.utf8)
|
||||
.join('');
|
||||
|
||||
return {
|
||||
videoId,
|
||||
videoTitle,
|
||||
thumbnailUrl,
|
||||
transcript,
|
||||
};
|
||||
}
|
||||
@@ -2,34 +2,18 @@
|
||||
// This subsystem is responsible for fetching the transcript of a YouTube video.
|
||||
// It is used by the Big-AGI Persona Creator to create a character sheet.
|
||||
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from '~/server/api/trpc.server';
|
||||
import { fetchJsonOrTRPCError, fetchTextOrTRPCError } from '~/server/api/trpc.router.fetchers';
|
||||
import { fetchTextOrTRPCError } from '~/server/api/trpc.router.fetchers';
|
||||
|
||||
import { fetchYouTubeTranscript } from './youtube.fetcher';
|
||||
|
||||
|
||||
const inputSchema = z.object({
|
||||
videoId: z.string().min(1),
|
||||
});
|
||||
|
||||
const youtubeTranscriptionSchema = z.object({
|
||||
wireMagic: z.literal('pb3'),
|
||||
events: z.array(
|
||||
z.object({
|
||||
tStartMs: z.number(),
|
||||
dDurationMs: z.number().optional(),
|
||||
aAppend: z.number().optional(),
|
||||
segs: z.array(
|
||||
z.object({
|
||||
utf8: z.string(),
|
||||
tOffsetMs: z.number().optional(),
|
||||
}),
|
||||
).optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
export const youtubeRouter = createTRPCRouter({
|
||||
|
||||
@@ -40,48 +24,11 @@ export const youtubeRouter = createTRPCRouter({
|
||||
.input(inputSchema)
|
||||
.query(async ({ input }) => {
|
||||
const { videoId } = input;
|
||||
|
||||
// 1. find the cpations URL within the video HTML page
|
||||
const html = await fetchTextOrTRPCError(`https://www.youtube.com/watch?v=${videoId}`, 'GET', {}, undefined, 'YouTube Transcript');
|
||||
|
||||
const captionsUrlEnc = extractFromTo(html, 'https://www.youtube.com/api/timedtext', '"', 'Captions URL');
|
||||
const captionsUrl = decodeURIComponent(captionsUrlEnc.replaceAll('\\u0026', '&'));
|
||||
const thumbnailUrl = extractFromTo(html, 'https://i.ytimg.com/vi/', '"', 'Thumbnail URL').replaceAll('maxres', 'hq');
|
||||
const videoTitle = extractFromTo(html, '<title>', '</title>', 'Video Title').slice(7).replaceAll(' - YouTube', '').trim();
|
||||
|
||||
// 2. fetch the captions
|
||||
// note: the desktop player appends this much: &fmt=json3&xorb=2&xobt=3&xovt=3&cbr=Chrome&cbrver=114.0.0.0&c=WEB&cver=2.20230628.07.00&cplayer=UNIPLAYER&cos=Windows&cosver=10.0&cplatform=DESKTOP
|
||||
const captions = await fetchJsonOrTRPCError(captionsUrl + `&fmt=json3`, 'GET', {}, undefined, 'YouTube Captions');
|
||||
const safeData = youtubeTranscriptionSchema.safeParse(captions);
|
||||
if (!safeData.success) {
|
||||
console.error(safeData.error);
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: '[YouTube API Issue] Could not parse the captions' });
|
||||
}
|
||||
|
||||
// 3. flatten to text
|
||||
const transcript = safeData.data.events
|
||||
.flatMap(event => event.segs ?? [])
|
||||
.map(seg => seg.utf8)
|
||||
.join('');
|
||||
|
||||
return {
|
||||
videoId,
|
||||
videoTitle,
|
||||
thumbnailUrl,
|
||||
transcript,
|
||||
};
|
||||
return await fetchYouTubeTranscript(videoId, url => fetchTextOrTRPCError(url, 'GET', {}, undefined, 'YouTube Transcript'));
|
||||
}),
|
||||
|
||||
});
|
||||
|
||||
function extractFromTo(html: string, from: string, to: string, label: string): string {
|
||||
const indexStart = html.indexOf(from);
|
||||
const indexEnd = html.indexOf(to, indexStart);
|
||||
if (indexStart < 0 || indexEnd <= indexStart)
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: `[YouTube API Issue] Could not find ${label}` });
|
||||
return html.substring(indexStart, indexEnd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
@@ -30,12 +30,18 @@ function createFetcherFromTRPC<TPostBody, TOut>(parser: (response: Response) =>
|
||||
try {
|
||||
if (SERVER_DEBUG_WIRE)
|
||||
console.log('-> tRPC', debugGenerateCurlCommand(method, url, headers, body as any));
|
||||
|
||||
response = await fetch(url, { method, headers, ...(body !== undefined ? { body: JSON.stringify(body) } : {}) });
|
||||
} catch (error: any) {
|
||||
console.error(`${moduleName} error (fetch):`, error);
|
||||
const errorCause: object | undefined = error ? error?.cause ?? undefined : undefined;
|
||||
console.error(`[${method}] ${moduleName} error (fetch):`, errorCause || error /* circular struct, don't use JSON.stringify.. */);
|
||||
// HTTP 400
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: `**[Issue] ${moduleName}: (network):** ${safeErrorString(error) || 'Unknown fetch error'} - ${error?.cause}`,
|
||||
message: `[Issue] ${moduleName}: (network): ${safeErrorString(error) || 'unknown fetch error'}`
|
||||
+ (errorCause ? ` - ${errorCause?.toString()}` : '')
|
||||
+ ((errorCause && (errorCause as any)?.code === 'ECONNREFUSED') ? ` - is "${url}" accessible by the server?` : ''),
|
||||
cause: errorCause,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,12 +53,14 @@ function createFetcherFromTRPC<TPostBody, TOut>(parser: (response: Response) =>
|
||||
let payload: any | null = await response.json().catch(() => null);
|
||||
if (payload === null)
|
||||
payload = await response.text().catch(() => null);
|
||||
console.error(`${moduleName} error (upstream):`, response.status, response.statusText, payload);
|
||||
console.error(`[${method}] ${moduleName} error (upstream):`, response.status, response.statusText, payload);
|
||||
// HTTP 400
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: `**[Issue] ${moduleName}**: ${response.statusText}` // (${response.status})`
|
||||
message: `[Issue] ${moduleName}: ${response.statusText}` // (${response.status})`
|
||||
+ (payload ? ` - ${safeErrorString(payload)}` : '')
|
||||
+ (response.status === 403 ? ` - is "${url}" accessible by the server?` : '')
|
||||
+ (response.status === 404 ? ` - "${url}" cannot be found by the server` : '')
|
||||
+ (response.status === 502 ? ` - is "${url}" not available?` : ''),
|
||||
});
|
||||
}
|
||||
@@ -61,10 +69,11 @@ function createFetcherFromTRPC<TPostBody, TOut>(parser: (response: Response) =>
|
||||
try {
|
||||
return await parser(response);
|
||||
} catch (error: any) {
|
||||
console.error(`${moduleName} error (parse):`, error);
|
||||
console.error(`[${method}] ${moduleName} error (parse):`, error);
|
||||
// HTTP 422
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: `**[Issue] ${moduleName}: (parsing):** ${safeErrorString(error) || `Unknown ${parserName} parsing error`}`,
|
||||
code: 'UNPROCESSABLE_CONTENT',
|
||||
message: `[Issue] ${moduleName}: (parsing): ${safeErrorString(error) || `Unknown ${parserName} parsing error`}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+20
-3
@@ -2,6 +2,17 @@
|
||||
export const SERVER_DEBUG_WIRE = false;
|
||||
|
||||
|
||||
export class ServerFetchError extends Error {
|
||||
public statusCode: number;
|
||||
|
||||
constructor({ statusCode, message }: { statusCode: number, message: string }) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.name = 'ServerFetchError';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches a URL, but throws an Error if the response is not ok.
|
||||
*/
|
||||
@@ -11,8 +22,14 @@ export async function nonTrpcServerFetchOrThrow(url: string, method: 'GET' | 'PO
|
||||
// Throws an error if the response is not ok
|
||||
// Use in server-side code, and not tRPC code (which has utility functions in trpc.serverutils.ts)
|
||||
if (!response.ok) {
|
||||
const errorPayload: object | null = await response.json().catch(() => null);
|
||||
throw new Error(`${response.statusText} (${response.status})${errorPayload ? ' · ' + JSON.stringify(errorPayload) : ''}`);
|
||||
let payload: any | null = await response.json().catch(() => null);
|
||||
if (payload === null)
|
||||
payload = await response.text().catch(() => null);
|
||||
const errorPayloadString = payload ? ': ' + JSON.stringify(payload, null, 2).slice(1, -1) : '';
|
||||
throw new ServerFetchError({
|
||||
message: `${response.statusText} (${response.status})${errorPayloadString}`,
|
||||
statusCode: response.status,
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -38,7 +55,7 @@ export function safeErrorString(error: any): string | null {
|
||||
return error;
|
||||
if (typeof error === 'object') {
|
||||
try {
|
||||
return JSON.stringify(error);
|
||||
return JSON.stringify(error, null, 2).slice(1, -1);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user