Compare commits
1 Commits
main
...
revert-107
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f70b91c2cb |
15
.github/ISSUE_TEMPLATE/sweep-template.yml
vendored
15
.github/ISSUE_TEMPLATE/sweep-template.yml
vendored
@ -1,15 +0,0 @@
|
||||
name: Sweep Issue
|
||||
title: 'Sweep: '
|
||||
description: For small bugs, features, refactors, and tests to be handled by Sweep, an AI-powered junior developer.
|
||||
labels: sweep
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Details
|
||||
description: Tell Sweep where and what to edit and provide enough context for a new developer to the codebase
|
||||
placeholder: |
|
||||
Unit Tests: Write unit tests for <FILE>. Test each function in the file. Make sure to test edge cases.
|
||||
Bugs: The bug might be in <FILE>. Here are the logs: ...
|
||||
Features: the new endpoint should use the ... class from <FILE> because it contains ... logic.
|
||||
Refactors: We are migrating this function to ... version because ...
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -52,7 +52,6 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: 'pip' # caching pip dependencies
|
||||
- name: Install dependencies with poetry
|
||||
working-directory: ./api
|
||||
run: |
|
||||
@ -61,7 +60,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
working-directory: ./api
|
||||
run: |
|
||||
poetry run python -m pytest -v --color=yes
|
||||
poetry run python -m pytest
|
||||
check-sh-files:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -80,17 +79,16 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: 'pip' # caching pip dependencies
|
||||
- name: Run ruff check
|
||||
uses: chartboost/ruff-action@v1
|
||||
with:
|
||||
src: "./api"
|
||||
args: "check --verbose"
|
||||
- name: Run ruff format check
|
||||
uses: chartboost/ruff-action@v1
|
||||
args: "--verbose"
|
||||
- name: Run black check
|
||||
uses: psf/black@stable
|
||||
with:
|
||||
options: "--check --diff --verbose"
|
||||
src: "./api"
|
||||
args: "format --check --verbose"
|
||||
check-web-code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and Publish Docker Image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
4
.github/workflows/helm-test.yml
vendored
4
.github/workflows/helm-test.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.12.3
|
||||
|
||||
@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Create kind cluster
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
uses: helm/kind-action@v1.10.0
|
||||
uses: helm/kind-action@v1.9.0
|
||||
|
||||
- name: Run chart-testing (install)
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,4 +12,3 @@ api/static/*
|
||||
**/node_modules/
|
||||
**/dist
|
||||
**/.mypy_cache/
|
||||
.vscode
|
||||
@ -34,7 +34,7 @@ COPY vendor/requirements.txt /usr/src/app/requirements.txt
|
||||
|
||||
# Install api dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends dumb-init libgomp1 musl-dev \
|
||||
&& apt-get install -y --no-install-recommends dumb-init \
|
||||
&& pip install --no-cache-dir ./api \
|
||||
&& pip install -r /usr/src/app/requirements.txt \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* \
|
||||
@ -45,8 +45,7 @@ RUN apt-get update \
|
||||
&& mkdir -p /data/db \
|
||||
&& mkdir -p /usr/src/app/weights \
|
||||
&& echo "appendonly yes" >> /etc/redis/redis.conf \
|
||||
&& echo "dir /data/db/" >> /etc/redis/redis.conf \
|
||||
&& ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
|
||||
&& echo "dir /data/db/" >> /etc/redis/redis.conf
|
||||
|
||||
EXPOSE 8008
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
|
||||
@ -17,7 +17,7 @@ ENV NODE_ENV='development'
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends dumb-init musl-dev
|
||||
&& apt-get install -y --no-install-recommends dumb-init
|
||||
|
||||
# Copy database, source code, and scripts
|
||||
COPY --from=redis /usr/local/bin/redis-server /usr/local/bin/redis-server
|
||||
@ -36,8 +36,7 @@ RUN npm ci \
|
||||
&& mkdir -p /data/db \
|
||||
&& mkdir -p /usr/src/app/weights \
|
||||
&& echo "appendonly yes" >> /etc/redis/redis.conf \
|
||||
&& echo "dir /data/db/" >> /etc/redis/redis.conf \
|
||||
&& ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
|
||||
&& echo "dir /data/db/" >> /etc/redis/redis.conf
|
||||
|
||||
EXPOSE 8008
|
||||
EXPOSE 9124
|
||||
|
||||
59
README.md
59
README.md
@ -45,20 +45,9 @@ volumes:
|
||||
|
||||
Then, just visit http://localhost:8008, You can find the API documentation at http://localhost:8008/api/docs
|
||||
|
||||
### 🌍 Environment Variables
|
||||
|
||||
The following Environment Variables are available:
|
||||
|
||||
| Variable Name | Description | Default Value |
|
||||
|-----------------------|---------------------------------------------------------|--------------------------------------|
|
||||
| `SERGE_DATABASE_URL` | Database connection string | `sqlite:////data/db/sql_app.db` |
|
||||
| `SERGE_JWT_SECRET` | Key for auth token encryption. Use a random string | `uF7FGN5uzfGdFiPzR` |
|
||||
| `SERGE_SESSION_EXPIRY`| Duration in minutes before a user must reauthenticate | `60` |
|
||||
| `NODE_ENV` | Node.js running environment | `production` |
|
||||
|
||||
## 🖥️ Windows
|
||||
|
||||
Ensure you have Docker Desktop installed, WSL2 configured, and enough free RAM to run models.
|
||||
Ensure you have Docker Desktop installed, WSL2 configured, and enough free RAM to run models.
|
||||
|
||||
## ☁️ Kubernetes
|
||||
|
||||
@ -69,48 +58,36 @@ Instructions for setting up Serge on Kubernetes can be found in the [wiki](https
|
||||
| Category | Models |
|
||||
|:-------------:|:-------|
|
||||
| **Alfred** | 40B-1023 |
|
||||
| **BioMistral** | 7B |
|
||||
| **Code** | 13B, 33B |
|
||||
| **CodeLLaMA** | 7B, 7B-Instruct, 7B-Python, 13B, 13B-Instruct, 13B-Python, 34B, 34B-Instruct, 34B-Python |
|
||||
| **Codestral** | 22B v0.1 |
|
||||
| **Gemma** | 2B, 1.1-2B-Instruct, 7B, 1.1-7B-Instruct |
|
||||
| **Gorilla** | Falcon-7B-HF-v0, 7B-HF-v1, Openfunctions-v1, Openfunctions-v2 |
|
||||
| **Falcon** | 7B, 7B-Instruct, 40B, 40B-Instruct |
|
||||
| **LLaMA 2** | 7B, 7B-Chat, 7B-Coder, 13B, 13B-Chat, 70B, 70B-Chat, 70B-OASST |
|
||||
| **LLaMA 3** | 11B-Instruct, 13B-Instruct, 16B-Instruct |
|
||||
| **LLaMA Pro** | 8B, 8B-Instruct |
|
||||
| **LLaMA 2** | 7B, 7B-Chat, 7B-Coder, 13B, 13B-Chat, 70B, 70B-Chat, 70B-OASST |
|
||||
| **Med42** | 70B |
|
||||
| **Medalpaca** | 13B |
|
||||
| **Medicine** | Chat, LLM |
|
||||
| **Medicine-LLM** | 13B |
|
||||
| **Meditron** | 7B, 7B-Chat, 70B |
|
||||
| **Meta-LlaMA-3** | 8B, 8B-Instruct, 70B, 70B-Instruct |
|
||||
| **Mistral** | 7B-V0.1, 7B-Instruct-v0.2, 7B-OpenOrca |
|
||||
| **MistralLite** | 7B |
|
||||
| **Mixtral** | 8x7B-v0.1, 8x7B-Dolphin-2.7, 8x7B-Instruct-v0.1 |
|
||||
| **Neural-Chat** | 7B-v3.3 |
|
||||
| **Notus** | 7B-v1 |
|
||||
| **Notux** | 8x7b-v1 |
|
||||
| **Nous-Hermes 2** | Mistral-7B-DPO, Mixtral-8x7B-DPO, Mistral-8x7B-SFT |
|
||||
| **OpenChat** | 7B-v3.5-1210 |
|
||||
| **OpenCodeInterpreter** | DS-6.7B, DS-33B, CL-7B, CL-13B, CL-70B |
|
||||
| **OpenLLaMA** | 3B-v2, 7B-v2, 13B-v2 |
|
||||
| **Orca 2** | 7B, 13B |
|
||||
| **Phi 2** | 2.7B |
|
||||
| **Phi 3** | mini-4k-instruct, medium-4k-instruct, medium-128k-instruct |
|
||||
| **Python Code** | 13B, 33B |
|
||||
| **PsyMedRP** | 13B-v1, 20B-v1 |
|
||||
| **Starling LM** | 7B-Alpha |
|
||||
| **SOLAR** | 10.7B-v1.0, 10.7B-instruct-v1.0 |
|
||||
| **TinyLlama** | 1.1B |
|
||||
| **Vicuna** | 7B-v1.5, 13B-v1.5, 33B-v1.3, 33B-Coder |
|
||||
| **WizardLM** | 2-7B, 13B-v1.2, 70B-v1.0 |
|
||||
| **WizardLM** | 7B-v1.0, 13B-v1.2, 70B-v1.0 |
|
||||
| **Zephyr** | 3B, 7B-Alpha, 7B-Beta |
|
||||
|
||||
Additional models can be requested by opening a GitHub issue. Other models are also available at [Serge Models](https://github.com/Smartappli/serge-models).
|
||||
|
||||
## ⚠️ Memory Usage
|
||||
|
||||
LLaMA will crash if you don't have enough available memory for the model
|
||||
LLaMA will crash if you don't have enough available memory for the model:
|
||||
|
||||
## 💬 Support
|
||||
|
||||
@ -130,29 +107,3 @@ git clone https://github.com/serge-chat/serge.git
|
||||
cd serge/
|
||||
docker compose -f docker-compose.dev.yml up --build
|
||||
```
|
||||
|
||||
The solution will accept a python debugger session on port 5678. Example launch.json for VSCode:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Remote Debug",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 5678
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/api",
|
||||
"remoteRoot": "/usr/src/app/api/"
|
||||
}
|
||||
],
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4
api/.gitignore
vendored
4
api/.gitignore
vendored
@ -157,6 +157,4 @@ cython_debug/
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
*.db
|
||||
#.idea/
|
||||
1346
api/poetry.lock
generated
1346
api/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -21,29 +21,53 @@ requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python=">=3.10,<4.0"
|
||||
python=">=3.9,<4.0"
|
||||
asyncio = "^3.4.3"
|
||||
packaging = "^24.1"
|
||||
pydantic = "^1.10.17"
|
||||
packaging = "^23.2"
|
||||
pydantic = "^1.10.14"
|
||||
python-dotenv = "^1.0.1"
|
||||
python-multipart = "^0.0.9"
|
||||
pyyaml = "^6.0"
|
||||
rfc3986 = "^2.0.0"
|
||||
sentencepiece = "^0.1.99"
|
||||
sniffio = "^1.3.0"
|
||||
sse-starlette = "^1.8.2"
|
||||
starlette = "^0.26.1"
|
||||
typing-extensions = "^4.12.2"
|
||||
urllib3 = "^2.2.2"
|
||||
toml = "^0.10.2"
|
||||
tqdm = "^4.66.2"
|
||||
typing-extensions = "^4.9.0"
|
||||
ujson = "^5.9.0"
|
||||
urllib3 = "^2.2.0"
|
||||
uvloop = "^0.19.0"
|
||||
watchfiles = "^0.21.0"
|
||||
websockets = "^12.0"
|
||||
anyio = "^4.2.0"
|
||||
certifi = "^2024.2.2"
|
||||
charset-normalizer = "^3.3.2"
|
||||
click = "^8.1.7"
|
||||
email-validator = "^2.0.0"
|
||||
fastapi = "^0.95.1"
|
||||
huggingface-hub = "^0.24.5"
|
||||
requests = "^2.32.3"
|
||||
filelock = "^3.13.1"
|
||||
h11 = "^0.14.0"
|
||||
httpcore = "^1.0.2"
|
||||
httptools = "^0.6.1"
|
||||
huggingface-hub = "^0.20.3"
|
||||
idna = "^3.6"
|
||||
itsdangerous = "^2.1.2"
|
||||
jinja2 = "^3.1.3"
|
||||
markupsafe = "^2.1.5"
|
||||
motor = "^3.3.2"
|
||||
orjson = "^3.9.13"
|
||||
dnspython = "^2.5.0"
|
||||
lazy-model = "^0.2.0"
|
||||
requests = "^2.31.0"
|
||||
numpy = "^1.25.2"
|
||||
langchain = "^0.0.180"
|
||||
loguru = "^0.7.2"
|
||||
redis = {extras = ["hiredis"], version = "^5.0.8"}
|
||||
pytest = "^8.3.2"
|
||||
hypercorn = {extras = ["trio"], version = "^0.17.3"}
|
||||
redis = {extras = ["hiredis"], version = "^5.0.1"}
|
||||
pytest = "^8.0.0"
|
||||
hypercorn = {extras = ["trio"], version = "^0.16.0"}
|
||||
|
||||
pyjwt = "^2.9.0"
|
||||
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
|
||||
aiofiles = "^24.1.0"
|
||||
python-multipart = "^0.0.9"
|
||||
debugpy = "^1.8.5"
|
||||
sqlalchemy = "^2.0.32"
|
||||
[tool.ruff]
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
@ -94,3 +118,6 @@ target-version = "py311"
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
max-complexity = 10
|
||||
|
||||
[tool.black]
|
||||
line-length = 150
|
||||
target-version = ['py311']
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
import logging
|
||||
import uuid
|
||||
from typing import List, Optional
|
||||
|
||||
from serge.schema import user as user_schema
|
||||
from serge.utils.security import get_password_hash
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from serge.models import user as user_model
|
||||
|
||||
|
||||
def get_user(db: Session, username: str) -> Optional[user_schema.User]:
|
||||
return Mappers.user_db_to_view(
|
||||
db.query(user_model.User).filter(user_model.User.username == username).first(),
|
||||
include_auth=True,
|
||||
)
|
||||
|
||||
|
||||
def get_user_by_email(db: Session, email: str) -> Optional[user_schema.User]:
|
||||
return Mappers.user_db_to_view(db.query(user_model.User).filter(user_model.User.email == email).first())
|
||||
|
||||
|
||||
def get_users(db: Session, skip: int = 0, limit: int = 100) -> List[user_schema.User]:
|
||||
return [Mappers.user_db_to_view(u) for u in db.query(user_model.User).offset(skip).limit(limit).all()]
|
||||
|
||||
|
||||
def create_user(db: Session, ua: user_schema.UserAuth) -> Optional[user_schema.User]:
|
||||
# Check already exists
|
||||
if get_user(db, ua.username):
|
||||
logging.error(f"Tried to create new user, but already exists: {ua.username}")
|
||||
return None
|
||||
|
||||
match ua.auth_type:
|
||||
case 1:
|
||||
ua.secret = get_password_hash(ua.secret)
|
||||
case _: # Todo: More auth types
|
||||
return None
|
||||
|
||||
db_user, db_user_auth = Mappers.user_view_to_db(None, ua)
|
||||
db.add(db_user_auth)
|
||||
db.add(db_user)
|
||||
db.commit()
|
||||
return Mappers.user_db_to_view(db_user)
|
||||
|
||||
|
||||
def update_user(db: Session, u: user_schema.User) -> Optional[user_schema.User]:
|
||||
user = db.query(user_model.User).filter(user_model.User.username == u.username).first()
|
||||
if not user:
|
||||
return None
|
||||
for k, v in u.dict().items():
|
||||
if k in ["auth", "chats"]:
|
||||
continue
|
||||
setattr(user, k, v)
|
||||
db.commit()
|
||||
return user
|
||||
|
||||
|
||||
def create_chat(db: Session, chat: user_schema.Chat):
|
||||
c = user_model.Chat(owner=chat.owner, chat_id=chat.chat_id)
|
||||
db.add(c)
|
||||
db.commit()
|
||||
|
||||
|
||||
def remove_chat(db: Session, chat: user_schema.Chat):
|
||||
c = db.query(user_model.Chat).filter(user_model.Chat.chat_id == chat.chat_id).one()
|
||||
db.delete(c)
|
||||
db.commit()
|
||||
|
||||
|
||||
class Mappers:
|
||||
@staticmethod
|
||||
def user_db_to_view(u: user_model.User, include_auth=False) -> user_schema.User:
|
||||
if not u:
|
||||
return None
|
||||
auths = chats = []
|
||||
if include_auth:
|
||||
auths = u.auth
|
||||
# u.auth = []
|
||||
chats = u.chats
|
||||
# u.chats = []
|
||||
app_user = user_schema.User(**{k: v for k, v in u.__dict__.items() if not k.startswith("_") and k not in ["chats", "auth"]})
|
||||
|
||||
app_user.auth = [user_schema.UserAuth(username=u.username, secret=x.secret, auth_type=x.auth_type) for x in auths]
|
||||
|
||||
app_user.chats = [user_schema.Chat(chat_id=x.chat_id, owner=x.owner) for x in chats]
|
||||
|
||||
return app_user
|
||||
|
||||
@staticmethod
|
||||
def user_view_to_db(
|
||||
u: Optional[user_schema.User] = None, ua: Optional[user_schema.UserAuth] = None
|
||||
) -> (user_model.User, Optional[user_model.UserAuth]):
|
||||
assert u or ua, "One of User or UserAuth must be passed"
|
||||
if not u: # Creating a new user
|
||||
u = user_schema.User(id=uuid.uuid4(), username=ua.username)
|
||||
auth = []
|
||||
if ua:
|
||||
auth = Mappers.user_auth_view_to_db(ua, u.id)
|
||||
user = user_model.User(**u.dict())
|
||||
if auth:
|
||||
user.auth.append(auth)
|
||||
for chat in u.chats:
|
||||
user.chats.append(user_model.Chat(chat_id=chat.chat_id))
|
||||
return (user, auth)
|
||||
|
||||
@staticmethod
|
||||
def user_auth_view_to_db(ua: user_schema.UserAuth, user_id: uuid.UUID) -> user_model.UserAuth:
|
||||
if not ua:
|
||||
return None
|
||||
return user_model.UserAuth(secret=ua.secret, auth_type=ua.auth_type, user_id=user_id)
|
||||
@ -15,22 +15,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BioMistral",
|
||||
"models": [
|
||||
{
|
||||
"name": "BioMistral-7B",
|
||||
"repo": "BioMistral/BioMistral-7B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "ggml-model-Q4_K_M.gguf",
|
||||
"disk_space": 4368439424.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Code",
|
||||
"models": [
|
||||
@ -161,23 +145,7 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Codesstral",
|
||||
"models": [
|
||||
{
|
||||
"name": "Codestral-22B-v0.1",
|
||||
"repo": "bartowski/Codestral-22B-v0.1-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Codestral-22B-v0.1-Q4_K_M.gguf",
|
||||
"disk_space": 15722553696.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Falcon",
|
||||
"models": [
|
||||
@ -227,104 +195,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gemma",
|
||||
"models": [
|
||||
{
|
||||
"name": "Gemma-2B",
|
||||
"repo": "brittlewis12/gemma-2b-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gemma-2b.Q4_K_M.gguf",
|
||||
"disk_space": 1495245728.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gemma-1_1-2B-Instruct",
|
||||
"repo": "brittlewis12/gemma-1.1-2b-it-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gemma-1.1-2b-it.Q4_K_M.gguf",
|
||||
"disk_space": 1630263200.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gemma-7B",
|
||||
"repo": "brittlewis12/gemma-7b-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gemma-7b.Q4_K_M.gguf",
|
||||
"disk_space": 5127231648.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gemma-1_1-7B-Instruct",
|
||||
"repo": "brittlewis12/gemma-1.1-7b-it-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gemma-1.1-7b-it.Q4_K_M.gguf",
|
||||
"disk_space": 5329759200.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gorilla",
|
||||
"models": [
|
||||
{
|
||||
"name": "Gorilla-7B Falcon",
|
||||
"repo": "gorilla-llm/gorilla-falcon-7b-hf-v0-gguf",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gorilla-falcon-7b-hf-v0-q4_K_M.gguf",
|
||||
"disk_space": 4975125696.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gorilla-7B",
|
||||
"repo": "gorilla-llm/gorilla-7b-hf-v1-gguf",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gorilla-7b-hf-v1-q4_K_M.gguf",
|
||||
"disk_space": 4081004288.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gorilla OpenFunctions V1",
|
||||
"repo": "gorilla-llm/gorilla-openfunctions-v1-gguf",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gorilla-openfunctions-v1-q4_K_M.gguf",
|
||||
"disk_space": 4081004288.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gorilla OpenFunctions V2",
|
||||
"repo": "gorilla-llm/gorilla-openfunctions-v2-gguf",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "gorilla-openfunctions-v2-q4_K_M.gguf",
|
||||
"disk_space": 4223770912.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LLaMA_2",
|
||||
"models": [
|
||||
@ -417,71 +287,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LLaMA_3",
|
||||
"models": [
|
||||
{
|
||||
"name": "Llama-3-11B-Instruct-v0.1",
|
||||
"repo": "MaziyarPanahi/Llama-3-11B-Instruct-v0.1-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Llama-3-11B-Instruct-v0.1.Q4_K_M.gguf",
|
||||
"disk_space": 8200021632.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Llama-3-13B-Instruct-v0.1",
|
||||
"repo": "MaziyarPanahi/Llama-3-13B-Instruct-v0.1-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Llama-3-13B-Instruct-v0.1.Q4_K_M.gguf",
|
||||
"disk_space": 8061089600.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Llama-3-16B-Instruct-v0.1",
|
||||
"repo": "MaziyarPanahi/Llama-3-16B-Instruct-v0.1-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Llama-3-16B-Instruct-v0.1.Q4_K_M.gguf",
|
||||
"disk_space": 10154318048.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LLaMA-Pro",
|
||||
"models": [
|
||||
{
|
||||
"name": "Llama-Pro-8B",
|
||||
"repo": "TheBloke/LLaMA-Pro-8B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "llama-pro-8b.Q4_K_M.gguf",
|
||||
"disk_space": 5055758336.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Llama-Pro-8B-Instruct",
|
||||
"repo": "TheBloke/LLaMA-Pro-8B-Instruct-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "llama-pro-8b-instruct.Q4_K_M.gguf",
|
||||
"disk_space": 5055758688.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Med42",
|
||||
@ -516,32 +321,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Medicine",
|
||||
"name": "medicine-LLM",
|
||||
"models": [
|
||||
{
|
||||
"name": "Medicine-Chat",
|
||||
"repo": "TheBloke/medicine-chat-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "medicine-chat.Q4_K_M.gguf",
|
||||
"disk_space": 4081010048.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Medicine-LLM",
|
||||
"repo": "TheBloke/medicine-LLM-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "medicine-llm.Q4_K_M.gguf",
|
||||
"disk_space": 4081009920.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Medicine-LLM-13B",
|
||||
"name": "Medicine LLM 13B",
|
||||
"repo": "TheBloke/medicine-LLM-13B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
@ -552,7 +335,7 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Meditron",
|
||||
"models": [
|
||||
@ -590,56 +373,7 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Meta-Llama-3",
|
||||
"models": [
|
||||
{
|
||||
"name": "Meta-Llama-3-8B",
|
||||
"repo": "QuantFactory/Meta-Llama-3-8B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Meta-Llama-3-8B.Q4_K_M.gguf",
|
||||
"disk_space": 4921246944.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Meta-Llama-3-8B-Instruct",
|
||||
"repo": "QuantFactory/Meta-Llama-3-8B-Instruct-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Meta-Llama-3-8B-Instruct.Q4_K_M.gguf",
|
||||
"disk_space": 4921246944.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Meta-Llama-3-70B",
|
||||
"repo": "NousResearch/Meta-Llama-3-70B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Meta-Llama-3-70B-Q4_K_M.gguf",
|
||||
"disk_space": 42520906176.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Meta-Llama-3-70B-Instruct",
|
||||
"repo": "QuantFactory/Meta-Llama-3-70B-Instruct-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Meta-Llama-3-70B-Instruct.Q4_K_M.gguf",
|
||||
"disk_space": 42520906208.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Mistral",
|
||||
"models": [
|
||||
@ -780,44 +514,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Nous-Hermes-2",
|
||||
"models": [
|
||||
{
|
||||
"name": "Nous-Hermes-2-Mistral-7B-DPO",
|
||||
"repo": "NousResearch/Nous-Hermes-2-Mistral-7B-DPO-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Nous-Hermes-2-Mistral-7B-DPO.Q4_K_M.gguf",
|
||||
"disk_space": 4368450560.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Nous-Hermes-2-Mistral-7B-DPO",
|
||||
"repo": "TheBloke/Nous-Hermes-2-Mixtral-8x7B-DPO-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "nous-hermes-2-mixtral-8x7b-dpo.Q4_K_M.gguf",
|
||||
"disk_space": 28446421792.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Nous-Hermes-2-Mistral-7B-SFT",
|
||||
"repo": "NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Nous-Hermes-2-Mixtral-8x7B-SFT.Q4_K_M.gguf",
|
||||
"disk_space": 28446421760.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenChat",
|
||||
"models": [
|
||||
@ -833,67 +529,7 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenCodeInterpreter",
|
||||
"models": [
|
||||
{
|
||||
"name": "OpenCodeInterpreter-DS-6.7B",
|
||||
"repo": "LoneStriker/OpenCodeInterpreter-DS-6.7B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "OpenCodeInterpreter-DS-6.7B-Q4_K_M.gguf",
|
||||
"disk_space": 4083016864.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenCodeInterpreter-DS-33B",
|
||||
"repo": "LoneStriker/OpenCodeInterpreter-DS-33B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "OpenCodeInterpreter-DS-33B-Q4_K_M.gguf",
|
||||
"disk_space": 19940659200.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenCodeInterpreter-CL-7B",
|
||||
"repo": "LoneStriker/OpenCodeInterpreter-CL-7B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "OpenCodeInterpreter-CL-7B-Q4_K_M.gguf",
|
||||
"disk_space": 4081095360.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenCodeInterpreter-CL-13B",
|
||||
"repo": "LoneStriker/OpenCodeInterpreter-CL-13B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "OpenCodeInterpreter-CL-13B-Q4_K_M.gguf",
|
||||
"disk_space": 7866070048.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenCodeInterpreter-CL-70B",
|
||||
"repo": "LoneStriker/OpenCodeInterpreter-CL-70B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "OpenCodeInterpreter-CL-70B-Q4_K_M.gguf",
|
||||
"disk_space": 41423092096.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "OpenLLaMA",
|
||||
"models": [
|
||||
@ -972,39 +608,6 @@
|
||||
"disk_space": 1789239136.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Phi-3-mini-4k-instruct-v0_3",
|
||||
"repo": "bartowski/Phi-3-mini-4k-instruct-v0.3-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Phi-3-mini-4k-instruct-v0.3-Q4_K_M.gguf",
|
||||
"disk_space": 2393231456.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Phi-3-medium-4k-instruct",
|
||||
"repo": "bartowski/Phi-3-medium-4k-instruct-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Phi-3-medium-4k-instruct-Q4_K_M.gguf",
|
||||
"disk_space": 8566820736.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Phi-3-medium-128k-instruct",
|
||||
"repo": "bartowski/Phi-3-medium-128k-instruct-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "Phi-3-medium-128k-instruct-Q4_K_M.gguf",
|
||||
"disk_space": 8566821408.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1093,49 +696,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SOLAR",
|
||||
"models": [
|
||||
{
|
||||
"name": "SOLAR-10.7B-V1_0",
|
||||
"repo": "TheBloke/SOLAR-10.7B-v1.0-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "solar-10.7b-v1.0.Q4_K_M.gguf",
|
||||
"disk_space": 6461667488.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SOLAR-10.7B-instruct-V1_0",
|
||||
"repo": "TheBloke/SOLAR-10.7B-Instruct-v1.0-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "solar-10.7b-instruct-v1.0.Q4_K_M.gguf",
|
||||
"disk_space": 6461667936.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Tinyllama",
|
||||
"models": [
|
||||
{
|
||||
"name": "Tinyllama-1.1B-Chat-v1.0",
|
||||
"repo": "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf",
|
||||
"disk_space": 668788096.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Vicuna",
|
||||
@ -1190,13 +750,13 @@
|
||||
"name": "WizardLM",
|
||||
"models": [
|
||||
{
|
||||
"name": "WizardLM-2-7B",
|
||||
"repo": "MaziyarPanahi/WizardLM-2-7B-GGUF",
|
||||
"name": "WizardLM-7B-v1_0",
|
||||
"repo": "TheBloke/wizardLM-7B-GGUF",
|
||||
"files": [
|
||||
{
|
||||
"name": "q4_K_M",
|
||||
"filename": "WizardLM-2-7B.Q4_K_M.gguf",
|
||||
"disk_space": 4368439008.0
|
||||
"filename": "wizardLM-7B.Q4_K_M.gguf",
|
||||
"disk_space": 4081009920.0
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from serge.models.settings import Settings
|
||||
from serge.models.user import User, UserAuth
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
settings = Settings()
|
||||
|
||||
engine = create_engine(settings.SERGE_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
def seed_db(db: Session):
|
||||
sys_u = db.query(User).filter(User.username == "system").first()
|
||||
if sys_u:
|
||||
return
|
||||
system_user = User(
|
||||
id=uuid.uuid4(),
|
||||
username="system",
|
||||
email="",
|
||||
full_name="Default User",
|
||||
theme_light=False,
|
||||
default_prompt="Below is an instruction that describes a task. Write a response that appropriately completes the request.",
|
||||
is_active=True,
|
||||
auth=[UserAuth(secret="", auth_type=0)],
|
||||
)
|
||||
db.add(system_user)
|
||||
db.commit()
|
||||
logging.info("System user created")
|
||||
@ -5,16 +5,12 @@ from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from loguru import logger
|
||||
from serge.database import SessionLocal, engine, seed_db
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from serge.models.settings import Settings
|
||||
from serge.routers.auth import auth_router
|
||||
from serge.routers.chat import chat_router
|
||||
from serge.routers.model import model_router
|
||||
from serge.routers.ping import ping_router
|
||||
from serge.routers.user import user_router
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from serge.models import user as user_models
|
||||
|
||||
# Configure logging settings
|
||||
|
||||
@ -45,17 +41,12 @@ origins = [
|
||||
"http://localhost:9124",
|
||||
]
|
||||
|
||||
# Seed the database
|
||||
user_models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI(title="Serge", version="0.0.1", description=description, tags_metadata=tags_metadata)
|
||||
|
||||
api_app = FastAPI(title="Serge API")
|
||||
api_app.include_router(chat_router)
|
||||
api_app.include_router(ping_router)
|
||||
api_app.include_router(model_router)
|
||||
api_app.include_router(auth_router)
|
||||
api_app.include_router(user_router)
|
||||
app.mount("/api", api_app)
|
||||
|
||||
# handle serving the frontend as static files in production
|
||||
@ -92,9 +83,6 @@ async def start_database():
|
||||
for file in files:
|
||||
os.remove(WEIGHTS + file)
|
||||
|
||||
db = SessionLocal()
|
||||
seed_db(db)
|
||||
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
|
||||
@ -32,5 +32,5 @@ class ChatParameters(BaseModel):
|
||||
class Chat(BaseModel):
|
||||
id: str = Field(default_factory=lambda: str(uuid4()))
|
||||
created: datetime = Field(default_factory=datetime.now)
|
||||
owner: str = Field("system")
|
||||
|
||||
params: ChatParameters
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
from os import getenv
|
||||
|
||||
from pydantic import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
SERGE_DATABASE_URL: str = getenv("SERGE_DATABASE_URL", "sqlite:////data/db/sql_app.db")
|
||||
NODE_ENV: str = "development"
|
||||
SERGE_JWT_SECRET: str = getenv("SERGE_JWT_SECRET", "uF7FGN5uzfGdFiPzR")
|
||||
SERGE_SESSION_EXPIRY: int = getenv("SERGE_SESSION_EXPIRY", 60)
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Uuid
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Uuid, primary_key=True)
|
||||
username = Column(String, unique=True, index=True)
|
||||
email = Column(String)
|
||||
full_name = Column(String)
|
||||
theme_light = Column(Boolean)
|
||||
default_prompt = Column(String)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
auth = relationship("UserAuth", back_populates="user", lazy="joined")
|
||||
chats = relationship("Chat", back_populates="user", lazy="joined")
|
||||
|
||||
|
||||
class Chat(Base):
|
||||
__tablename__ = "chats"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
chat_id = Column(String, index=True)
|
||||
owner = Column(String, ForeignKey("users.username"))
|
||||
user = relationship("User", back_populates="chats")
|
||||
|
||||
|
||||
class UserAuth(Base):
|
||||
__tablename__ = "auth"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
secret = Column(String)
|
||||
auth_type = Column(Integer)
|
||||
user_id = Column(Uuid, ForeignKey("users.id"))
|
||||
|
||||
user = relationship("User", back_populates="auth")
|
||||
@ -1,108 +0,0 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jose import JWTError
|
||||
from serge.crud import get_user
|
||||
from serge.database import SessionLocal
|
||||
from serge.schema.user import Token, User
|
||||
from serge.models.settings import Settings
|
||||
from serge.utils.security import create_access_token, decode_access_token, verify_password
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
settings = Settings()
|
||||
|
||||
auth_router = APIRouter(
|
||||
prefix="/auth",
|
||||
tags=["auth"],
|
||||
)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def authenticate_user(username: str, password: str, db: Session) -> Optional[User]:
|
||||
user = get_user(db, username)
|
||||
if not user:
|
||||
return None
|
||||
# Users may have multipe ways to authenticate
|
||||
auths = [a.auth_type for a in user.auth]
|
||||
if 0 in auths: # Default user, passwordless
|
||||
return user
|
||||
if 1 in auths: # Password auth
|
||||
secret = [x for x in user.auth if x.auth_type == 1][0].secret
|
||||
if verify_password(password, secret):
|
||||
return user
|
||||
if 2 in auths: # todo future auths
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
@auth_router.post("/token", response_model=Token)
|
||||
async def login_for_access_token(
|
||||
response: Response,
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
user = authenticate_user(form_data.username, form_data.password, db)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=settings.SERGE_SESSION_EXPIRY)
|
||||
access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires)
|
||||
response.set_cookie(key="token", value=access_token, httponly=True, secure=True, samesite="strict")
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@auth_router.post("/logout")
|
||||
async def logout(response: Response):
|
||||
# Clear the token cookie by setting it to expire immediately
|
||||
response.delete_cookie(key="token")
|
||||
return {"message": "Logged out successfully"}
|
||||
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> User:
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
username = decode_access_token(token)
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
except JWTError as e:
|
||||
logging.exception(e)
|
||||
raise credentials_exception
|
||||
|
||||
user = get_user(db, username)
|
||||
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_active_user(request: Request, response: Response, db: Session = Depends(get_db)) -> User:
|
||||
token = request.cookies.get("token")
|
||||
|
||||
if not token:
|
||||
return get_user(db, "system")
|
||||
|
||||
u = None
|
||||
try:
|
||||
u = await get_current_user(token, db)
|
||||
except HTTPException:
|
||||
await logout(response)
|
||||
u = get_user(db, "system")
|
||||
return u
|
||||
@ -1,60 +1,25 @@
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from langchain.memory import RedisChatMessageHistory
|
||||
from langchain.schema import AIMessage, HumanMessage, SystemMessage, messages_to_dict
|
||||
from langchain.schema import SystemMessage, messages_to_dict, AIMessage, HumanMessage
|
||||
from llama_cpp import Llama
|
||||
from loguru import logger
|
||||
from redis import Redis
|
||||
from serge.crud import create_chat, remove_chat, update_user
|
||||
from serge.database import SessionLocal
|
||||
from serge.models.chat import Chat, ChatParameters
|
||||
from serge.routers.auth import get_current_active_user
|
||||
from serge.schema.user import Chat as UserChat
|
||||
from serge.schema.user import User
|
||||
from serge.utils.stream import get_prompt
|
||||
from sqlalchemy.orm import Session
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
|
||||
from serge.models.chat import Chat, ChatParameters
|
||||
from serge.utils.stream import get_prompt
|
||||
|
||||
chat_router = APIRouter(
|
||||
prefix="/chat",
|
||||
tags=["chat"],
|
||||
)
|
||||
|
||||
unauth_error = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Unauthorized",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def _try_get_chat(client, chat_id):
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
chat_raw = client.get(f"chat:{chat_id}")
|
||||
chat = Chat.parse_raw(chat_raw)
|
||||
|
||||
# backwards compat
|
||||
if not hasattr(chat, "owner"):
|
||||
chat.owner = "system"
|
||||
|
||||
return chat
|
||||
|
||||
|
||||
@chat_router.post("/")
|
||||
async def create_new_chat(
|
||||
u: User = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db),
|
||||
model: str = "7B",
|
||||
temperature: float = 0.1,
|
||||
top_k: int = 50,
|
||||
@ -86,16 +51,11 @@ async def create_new_chat(
|
||||
init_prompt=init_prompt,
|
||||
)
|
||||
# create the chat
|
||||
chat = Chat(owner=u.username, params=params)
|
||||
chat = Chat(params=params)
|
||||
|
||||
# store the parameters
|
||||
client.set(f"chat:{chat.id}", chat.json())
|
||||
|
||||
uc = UserChat(chat_id=chat.id, owner=u.username)
|
||||
create_chat(db, uc)
|
||||
u.chats.append(uc)
|
||||
update_user(db, u)
|
||||
|
||||
# create the message history
|
||||
history = RedisChatMessageHistory(chat.id)
|
||||
history.append(SystemMessage(content=init_prompt))
|
||||
@ -107,11 +67,13 @@ async def create_new_chat(
|
||||
|
||||
|
||||
@chat_router.get("/")
|
||||
async def get_all_chats(u: User = Depends(get_current_active_user)):
|
||||
async def get_all_chats():
|
||||
res = []
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
ids = client.smembers("chats")
|
||||
|
||||
chats = sorted(
|
||||
[await get_specific_chat(x.chat_id, u) for x in u.chats],
|
||||
[await get_specific_chat(id.decode()) for id in ids],
|
||||
key=lambda x: x["created"],
|
||||
reverse=True,
|
||||
)
|
||||
@ -134,33 +96,39 @@ async def get_all_chats(u: User = Depends(get_current_active_user)):
|
||||
|
||||
|
||||
@chat_router.get("/{chat_id}")
|
||||
async def get_specific_chat(chat_id: str, u: User = Depends(get_current_active_user)):
|
||||
async def get_specific_chat(chat_id: str):
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
chat = _try_get_chat(client, chat_id)
|
||||
chat_raw = client.get(f"chat:{chat_id}")
|
||||
chat = Chat.parse_raw(chat_raw)
|
||||
|
||||
history = RedisChatMessageHistory(chat.id)
|
||||
|
||||
chat_dict = chat.dict()
|
||||
chat_dict["history"] = messages_to_dict(history.messages)
|
||||
return chat_dict
|
||||
|
||||
|
||||
@chat_router.get("/{chat_id}/history")
|
||||
async def get_chat_history(chat_id: str, u: User = Depends(get_current_active_user)):
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
async def get_chat_history(chat_id: str):
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
history = RedisChatMessageHistory(chat_id)
|
||||
return messages_to_dict(history.messages)
|
||||
|
||||
|
||||
@chat_router.delete("/{chat_id}/prompt")
|
||||
async def delete_prompt(chat_id: str, idx: int, u: User = Depends(get_current_active_user)):
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
async def delete_prompt(chat_id: str, idx: int):
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
history = RedisChatMessageHistory(chat_id)
|
||||
|
||||
@ -178,17 +146,12 @@ async def delete_prompt(chat_id: str, idx: int, u: User = Depends(get_current_ac
|
||||
|
||||
|
||||
@chat_router.delete("/{chat_id}")
|
||||
async def delete_chat(chat_id: str, u: User = Depends(get_current_active_user), db: Session = Depends(get_db)):
|
||||
async def delete_chat(chat_id: str):
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
if cid := next((x for x in u.chats if x.chat_id == chat_id), None):
|
||||
remove_chat(db, cid)
|
||||
|
||||
RedisChatMessageHistory(chat_id).clear()
|
||||
|
||||
client.delete(f"chat:{chat_id}")
|
||||
@ -198,25 +161,23 @@ async def delete_chat(chat_id: str, u: User = Depends(get_current_active_user),
|
||||
|
||||
|
||||
@chat_router.delete("/delete/all")
|
||||
async def delete_all_chats(u: User = Depends(get_current_active_user), db: Session = Depends(get_db)):
|
||||
[delete_chat(x.chat_id, u, db) for x in u.chats]
|
||||
async def delete_all_chats():
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
client.flushdb()
|
||||
return True
|
||||
|
||||
|
||||
@chat_router.get("/{chat_id}/question")
|
||||
async def stream_ask_a_question(chat_id: str, prompt: str, u: User = Depends(get_current_active_user)):
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
|
||||
def stream_ask_a_question(chat_id: str, prompt: str):
|
||||
logger.info("Starting redis client")
|
||||
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
logger.debug("creating chat")
|
||||
chat = _try_get_chat(client, chat_id)
|
||||
chat_raw = client.get(f"chat:{chat_id}")
|
||||
chat = Chat.parse_raw(chat_raw)
|
||||
|
||||
logger.debug(chat.params)
|
||||
logger.debug("creating history")
|
||||
@ -262,7 +223,7 @@ async def stream_ask_a_question(chat_id: str, prompt: str, u: User = Depends(get
|
||||
yield {"event": "message", "data": txt}
|
||||
|
||||
except Exception as e:
|
||||
if type(e) is UnicodeDecodeError:
|
||||
if type(e) == UnicodeDecodeError:
|
||||
pass
|
||||
else:
|
||||
error = e.__str__()
|
||||
@ -281,16 +242,15 @@ async def stream_ask_a_question(chat_id: str, prompt: str, u: User = Depends(get
|
||||
|
||||
|
||||
@chat_router.post("/{chat_id}/question")
|
||||
async def ask_a_question(chat_id: str, prompt: str, u: User = Depends(get_current_active_user)):
|
||||
if chat_id not in [x.chat_id for x in u.chats]:
|
||||
raise unauth_error
|
||||
|
||||
async def ask_a_question(chat_id: str, prompt: str):
|
||||
client = Redis(host="localhost", port=6379, decode_responses=False)
|
||||
|
||||
if not client.sismember("chats", chat_id):
|
||||
raise ValueError("Chat does not exist")
|
||||
|
||||
chat = _try_get_chat(client, chat_id)
|
||||
chat_raw = client.get(f"chat:{chat_id}")
|
||||
chat = Chat.parse_raw(chat_raw)
|
||||
|
||||
history = RedisChatMessageHistory(chat.id)
|
||||
|
||||
if len(prompt) > 0:
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from serge.crud import create_user, update_user
|
||||
from serge.database import SessionLocal
|
||||
from serge.routers.auth import get_current_active_user
|
||||
from serge.schema import user as user_schema
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
user_router = APIRouter(
|
||||
prefix="/user",
|
||||
tags=["user"],
|
||||
)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@user_router.get("/", response_model=user_schema.User)
|
||||
async def get_user(u: user_schema.User = Depends(get_current_active_user)):
|
||||
if not u:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
return u.to_public_dict()
|
||||
|
||||
|
||||
@user_router.post("/create", response_model=user_schema.User)
|
||||
async def create_user_with_pass(ua: user_schema.UserAuth, db: Session = Depends(get_db)):
|
||||
try:
|
||||
u = create_user(db, ua)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=f"Failed to create. {e}",
|
||||
)
|
||||
if not u:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
|
||||
detail="Could not create user",
|
||||
)
|
||||
return u.to_public_dict()
|
||||
|
||||
|
||||
@user_router.put("/", response_model=user_schema.User)
|
||||
async def self_update_user(
|
||||
new_data: user_schema.User,
|
||||
current: user_schema.User = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
current.email = new_data.email
|
||||
current.full_name = new_data.full_name
|
||||
current.default_prompt = new_data.default_prompt
|
||||
update_user(db, current)
|
||||
return current.to_public_dict()
|
||||
@ -73,35 +73,16 @@
|
||||
"Name": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fp16",
|
||||
"iq1_M",
|
||||
"iq1_S",
|
||||
"iq2_M",
|
||||
"iq2_S",
|
||||
"iq2_XS",
|
||||
"iq2_XXS",
|
||||
"iq3_M",
|
||||
"iq3_S",
|
||||
"iq3_XS",
|
||||
"iq3_XXS",
|
||||
"iq4_NL",
|
||||
"iq4_XS",
|
||||
"q2_K",
|
||||
"q3_K_L",
|
||||
"q3_K_M",
|
||||
"q3_K_S",
|
||||
"q4_0",
|
||||
"q4_1",
|
||||
"q4_K_M",
|
||||
"q4_K_S",
|
||||
"q5_0",
|
||||
"q5_1",
|
||||
"q5_K_M",
|
||||
"q5_K_S",
|
||||
"q6_K",
|
||||
"q8_0",
|
||||
"q8_1",
|
||||
"q8_K"
|
||||
"q8_0"
|
||||
],
|
||||
"title": "Name"
|
||||
}
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
username: str
|
||||
|
||||
|
||||
class UserAuth(UserBase):
|
||||
secret: str
|
||||
auth_type: int
|
||||
|
||||
|
||||
class Chat(BaseModel):
|
||||
chat_id: str
|
||||
owner: str
|
||||
|
||||
|
||||
class User(UserBase):
|
||||
id: uuid.UUID
|
||||
is_active: bool = True
|
||||
email: str = ""
|
||||
full_name: str = ""
|
||||
theme_light: bool = False
|
||||
default_prompt: str = "Below is an instruction that describes a task. Write a response that appropriately completes the request."
|
||||
auth: list[UserAuth] = []
|
||||
chats: list[Chat] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
def to_public_dict(self):
|
||||
user_dict = self.dict()
|
||||
for auth in user_dict["auth"]:
|
||||
auth["secret"] = "********"
|
||||
return user_dict
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
@ -1,56 +0,0 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from jose import JWTError, jwt
|
||||
from serge.models.settings import Settings
|
||||
|
||||
ALGORITHM = "HS256"
|
||||
settings = Settings()
|
||||
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
salt_and_hash = base64.b64decode(hashed_password.encode("utf-8"))
|
||||
salt = salt_and_hash[:16]
|
||||
stored_password = salt_and_hash[16:]
|
||||
new_hashed_password = hashlib.scrypt(plain_password.encode("utf-8"), salt=salt, n=8192, r=8, p=1, dklen=64)
|
||||
return new_hashed_password == stored_password
|
||||
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
salt = os.urandom(16)
|
||||
hashed_password = hashlib.scrypt(password.encode("utf-8"), salt=salt, n=8192, r=8, p=1, dklen=64)
|
||||
salt_and_hash = salt + hashed_password
|
||||
return base64.b64encode(salt_and_hash).decode("utf-8")
|
||||
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.SERGE_SESSION_EXPIRY)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.SERGE_JWT_SECRET, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
def decode_access_token(token: str):
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SERGE_JWT_SECRET, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
return username
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
@ -1,7 +1,7 @@
|
||||
services:
|
||||
serge:
|
||||
restart: on-failure
|
||||
build:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
volumes:
|
||||
@ -11,9 +11,9 @@ services:
|
||||
- weights:/usr/src/app/weights/
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- 8008:8008
|
||||
- 9124:9124
|
||||
- 5678:5678
|
||||
- "8008:8008"
|
||||
- "9124:9124"
|
||||
|
||||
volumes:
|
||||
datadb:
|
||||
weights:
|
||||
|
||||
@ -21,13 +21,12 @@ detect_cpu_features() {
|
||||
}
|
||||
|
||||
# Check if the CPU architecture is aarch64/arm64
|
||||
if [ "$cpu_arch" = "aarch64" ] || [ "$cpu_arch" = "arm64" ]; then
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu/"
|
||||
if [ "$cpu_arch" = "aarch64" ]; then
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://gaby.github.io/arm64-wheels/"
|
||||
else
|
||||
# Use @smartappli provided wheels
|
||||
#cpu_feature=$(detect_cpu_features)
|
||||
#pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu-$cpu_feature/"
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu/"
|
||||
cpu_feature=$(detect_cpu_features)
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://smartappli.github.io/llama-cpp-python-cuBLAS-wheels/$cpu_feature/cpu"
|
||||
fi
|
||||
|
||||
echo "Recommended install command for llama-cpp-python: $pip_command"
|
||||
@ -52,11 +51,7 @@ redis_process=$!
|
||||
# Start the API
|
||||
cd /usr/src/app/api || exit 1
|
||||
hypercorn_cmd="hypercorn src.serge.main:app --bind 0.0.0.0:8008"
|
||||
if [ "$SERGE_ENABLE_IPV6" = true ] && [ "$SERGE_ENABLE_IPV4" != true ]; then
|
||||
hypercorn_cmd="hypercorn src.serge.main:app --bind [::]:8008"
|
||||
elif [ "$SERGE_ENABLE_IPV4" = true ] && [ "$SERGE_ENABLE_IPV6" = true ]; then
|
||||
hypercorn_cmd="hypercorn src.serge.main:app --bind 0.0.0.0:8008 --bind [::]:8008"
|
||||
fi
|
||||
[ "$SERGE_ENABLE_IPV6" = true ] && hypercorn_cmd+=" --bind [::]:8008"
|
||||
|
||||
$hypercorn_cmd || {
|
||||
echo 'Failed to start main app'
|
||||
|
||||
@ -21,13 +21,12 @@ detect_cpu_features() {
|
||||
}
|
||||
|
||||
# Check if the CPU architecture is aarch64/arm64
|
||||
if [ "$cpu_arch" = "aarch64" ] || [ "$cpu_arch" = "arm64" ]; then
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu/"
|
||||
if [ "$cpu_arch" = "aarch64" ]; then
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://gaby.github.io/arm64-wheels/"
|
||||
else
|
||||
# Use @smartappli provided wheels
|
||||
#cpu_feature=$(detect_cpu_features)
|
||||
#pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu-$cpu_feature/"
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://abetlen.github.io/llama-cpp-python/whl/cpu/"
|
||||
cpu_feature=$(detect_cpu_features)
|
||||
pip_command="python -m pip install -v llama-cpp-python==$LLAMA_PYTHON_VERSION --only-binary=:all: --extra-index-url=https://smartappli.github.io/llama-cpp-python-cuBLAS-wheels/$cpu_feature/cpu"
|
||||
fi
|
||||
|
||||
echo "Recommended install command for llama-cpp-python: $pip_command"
|
||||
@ -57,16 +56,10 @@ redis-server /etc/redis/redis.conf &
|
||||
cd /usr/src/app/web || exit 1
|
||||
npm run dev -- --host 0.0.0.0 --port 8008 &
|
||||
|
||||
python -m pip install debugpy -t /tmp
|
||||
|
||||
# Start the API
|
||||
cd /usr/src/app/api || exit 1
|
||||
hypercorn_cmd="python /tmp/debugpy --listen 0.0.0.0:5678 -m hypercorn src.serge.main:api_app --reload --bind 0.0.0.0:9124"
|
||||
if [ "$SERGE_ENABLE_IPV6" = true ] && [ "$SERGE_ENABLE_IPV4" != true ]; then
|
||||
hypercorn_cmd="python /tmp/debugpy --listen 0.0.0.0:5678 -m hypercorn src.serge.main:api_app --reload --bind [::]:9124"
|
||||
elif [ "$SERGE_ENABLE_IPV4" = true ] && [ "$SERGE_ENABLE_IPV6" = true ]; then
|
||||
hypercorn_cmd="python /tmp/debugpy --listen 0.0.0.0:5678 -m hypercorn src.serge.main:api_app --reload --bind 0.0.0.0:9124 --bind [::]:9124"
|
||||
fi
|
||||
hypercorn_cmd="hypercorn src.serge.main:app --bind 0.0.0.0:8008"
|
||||
[ "$SERGE_ENABLE_IPV6" = true ] && hypercorn_cmd+=" --bind [::]:8008"
|
||||
|
||||
$hypercorn_cmd || {
|
||||
echo 'Failed to start main app'
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
LLAMA_PYTHON_VERSION=0.2.87
|
||||
SERGE_ENABLE_IPV4=true
|
||||
LLAMA_PYTHON_VERSION=0.2.39
|
||||
SERGE_ENABLE_IPV6=false
|
||||
|
||||
6
vendor/requirements.txt
vendored
6
vendor/requirements.txt
vendored
@ -1,3 +1,3 @@
|
||||
typing-extensions>=4.12.2
|
||||
numpy>=1.26.0,<2.0.0
|
||||
diskcache>=5.6.3
|
||||
typing-extensions>=4.5.0
|
||||
numpy>=1.20.0
|
||||
diskcache>=5.6.1
|
||||
|
||||
2067
web/package-lock.json
generated
2067
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,41 +12,40 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.2.2",
|
||||
"@sveltejs/adapter-node": "^5.2.0",
|
||||
"@sveltejs/adapter-static": "^3.0.2",
|
||||
"@sveltejs/kit": "^2.5.20",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
"@sveltejs/adapter-auto": "^3.1.1",
|
||||
"@sveltejs/adapter-node": "^4.0.1",
|
||||
"@sveltejs/adapter-static": "^3.0.1",
|
||||
"@sveltejs/kit": "^2.5.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||
"@types/markdown-it": "^13.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||
"@typescript-eslint/parser": "^7.0.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-svelte": "^2.43.0",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"postcss": "^8.4.40",
|
||||
"prettier": "3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.5",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"tslib": "^2.6.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.1"
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"eslint-plugin-vue": "^9.21.1",
|
||||
"postcss": "^8.4.35",
|
||||
"prettier": "3.2.5",
|
||||
"prettier-plugin-svelte": "^3.2.0",
|
||||
"svelte": "^4.2.10",
|
||||
"svelte-check": "^3.6.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.1"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iconify/svelte": "^4.0.2",
|
||||
"@iconify/svelte": "^3.1.6",
|
||||
"@sveltestack/svelte-query": "^1.6.0",
|
||||
"clipboard": "^2.0.11",
|
||||
"daisyui": "^4.12.10",
|
||||
"highlight.js": "^11.10.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-highlightjs": "^4.1.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.5"
|
||||
"daisyui": "^4.7.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"markdown-it-highlightjs": "^4.0.1",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11"
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
width: auto;
|
||||
}
|
||||
|
||||
markdown .hljs {
|
||||
markdown. .hljs {
|
||||
background: hsl(var(--b3)) !important;
|
||||
}
|
||||
|
||||
@ -90,11 +90,9 @@ markdown .hljs {
|
||||
.models-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 25px;
|
||||
padding-left: 80px;
|
||||
padding-right: 40px;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 10px;
|
||||
gap: 20px;
|
||||
padding: 30px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
/* Model Accordion Styles */
|
||||
@ -134,44 +132,9 @@ markdown .hljs {
|
||||
}
|
||||
|
||||
.search-row {
|
||||
position: fixed;
|
||||
top: 5px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding-left: 80px;
|
||||
padding-right: 40px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
div {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.5rem 1rem;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
import { fly } from "svelte/transition";
|
||||
export let data: PageData;
|
||||
|
||||
export let isSidebarOpen: boolean = true;
|
||||
|
||||
let models;
|
||||
let modelAvailable: boolean;
|
||||
const isLoading = false;
|
||||
@ -20,14 +18,6 @@
|
||||
let dataCht: Response | any = null;
|
||||
const unsubscribe = newChat.subscribe((value) => (dataCht = value));
|
||||
|
||||
function toggleSidebar(): void {
|
||||
isSidebarOpen = !isSidebarOpen;
|
||||
}
|
||||
|
||||
function hideSidebar(): void {
|
||||
isSidebarOpen = false;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
theme = localStorage.getItem("data-theme") || "dark";
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
@ -120,49 +110,25 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<button
|
||||
on:click={toggleSidebar}
|
||||
class="border-base-content/[.2] btn btn-square z-10 my-1 mx-2 fixed border"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="inline-block w-5 h-5 stroke-current"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
></path></svg
|
||||
>
|
||||
</button>
|
||||
|
||||
<aside
|
||||
class="border-base-content/[.2] fixed top-0 z-40 min-h-full border-r transition-all overflow-hidden aria-label=Sidebar"
|
||||
class:left-0={isSidebarOpen}
|
||||
class:-left-80={!isSidebarOpen}
|
||||
id="default-sidebar"
|
||||
class="border-base-content/[.2] fixed left-0 top-0 z-40 h-screen w-80 -translate-x-full border-r transition-transform overflow-hidden translate-x-0 aria-label=Sidebar"
|
||||
>
|
||||
<div
|
||||
class="bg-base-200 relative h-screen py-1 px-2 overflow-hidden flex flex-col items-center justify-between"
|
||||
>
|
||||
<div class="w-full flex items-center pb-1">
|
||||
<button
|
||||
on:click={toggleSidebar}
|
||||
class="border-base-content/[.2] btn btn-square border"
|
||||
>
|
||||
<div
|
||||
class="w-full flex items-center border-b border-base-content/[.2] pb-1"
|
||||
>
|
||||
<button class="btn btn-ghost flex-shrink-0" on:click={goToHome}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="inline-block w-5 h-5 stroke-current"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
></path></svg
|
||||
fill="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
disabled={isLoading || !modelAvailable}
|
||||
@ -184,78 +150,75 @@
|
||||
</svg>
|
||||
<span>New Chat</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-ghost flex-shrink-0" on:click={goToHome}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
|
||||
</svg>
|
||||
<span class="sr-only">Home</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul
|
||||
class="my-1 w-full flex-grow overflow-y-auto no-scrollbar firefox-no-scrollbar ie-edge-no-scrollbar"
|
||||
class="my-1 w-full h-[85%] overflow-y-auto no-scrollbar firefox-no-scrollbar ie-edge-no-scrollbar"
|
||||
>
|
||||
{#if data && Symbol.iterator in Object(data.chats)}
|
||||
{#each data.chats as chat (chat.id)}
|
||||
<li in:fly={{ x: -100, duration: 900 }}>
|
||||
<a
|
||||
href={"/chat/" + chat.id}
|
||||
class="group hover:from-base-100 hover:text-base-content flex items-center rounded-lg py-2 pl-2 text-base font-normal hover:bg-gradient-to-r hover:to-transparent"
|
||||
class:bg-base-300={id === chat.id}
|
||||
>
|
||||
<div
|
||||
class="flex w-full flex-col space-y-2 p-2 border-b border-gray-200 relative"
|
||||
>
|
||||
{#each data.chats as chat (chat.id)}
|
||||
<li in:fly={{ x: -100, duration: 900 }}>
|
||||
<a
|
||||
href={"/chat/" + chat.id}
|
||||
class="group hover:from-base-100 hover:text-base-content flex items-center rounded-lg py-2 pl-2 text-base font-normal hover:bg-gradient-to-r hover:to-transparent"
|
||||
class:bg-base-300={id === chat.id}
|
||||
>
|
||||
<div class="flex w-full flex-col">
|
||||
<div class="flex w-full flex-col items-start justify-start">
|
||||
<div
|
||||
class="flex w-full flex-col items-start justify-start space-y-1"
|
||||
class="relative flex w-full flex-row items-center justify-between"
|
||||
>
|
||||
<div
|
||||
class="flex w-full flex-row items-center justify-between"
|
||||
>
|
||||
<div class="flex flex-col space-y-1.5">
|
||||
<p class="text-sm font-light max-w-[25ch] break-words">
|
||||
{truncate(chat.subtitle, 100)}
|
||||
</p>
|
||||
<span
|
||||
class="text-xs font-semibold max-w-[25ch] break-words"
|
||||
>{chat.model}</span
|
||||
>
|
||||
<span class="text-xs"
|
||||
>{timeSince(chat.created) + " ago"}</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<p class="text-sm font-light">
|
||||
{truncate(chat.subtitle, 42)}
|
||||
</p>
|
||||
<span class="text-xs font-semibold">{chat.model}</span>
|
||||
<span class="text-xs"
|
||||
>{timeSince(chat.created) + " ago"}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="absolute bottom-1.5 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
>
|
||||
{#if deleteConfirm}
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<button
|
||||
name="confirm-delete"
|
||||
class="btn btn-sm btn"
|
||||
on:click|preventDefault={() => deleteChat(chat.id)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
<div
|
||||
class="absolute right-0 opacity-0 group-hover:opacity-100 transition"
|
||||
>
|
||||
<!-- {#if $page.params.id === chat.id} -->
|
||||
{#if deleteConfirm}
|
||||
<div class="flex flex-row items-center">
|
||||
<button
|
||||
name="confirm-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={() => deleteChat(chat.id)}
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
name="cancel-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={toggleDeleteConfirm}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M2.344 2.343h-.001a8 8 0 0 1 11.314 11.314A8.002 8.002 0 0 1 .234 10.089a8 8 0 0 1 2.11-7.746Zm1.06 10.253a6.5 6.5 0 1 0 9.108-9.275 6.5 6.5 0 0 0-9.108 9.275ZM6.03 4.97 8 6.94l1.97-1.97a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-1.97 1.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 4.97 6.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
name="cancel-delete"
|
||||
class="btn btn-sm btn"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={toggleDeleteConfirm}
|
||||
>
|
||||
<svg
|
||||
@ -266,189 +229,26 @@
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M2.344 2.343h-.001a8 8 0 0 1 11.314 11.314A8.002 8.002 0 0 1 .234 10.089a8 8 0 0 1 2.11-7.746Zm1.06 10.253a6.5 6.5 0 1 0 9.108-9.275 6.5 6.5 0 0 0-9.108 9.275ZM6.03 4.97 8 6.94l1.97-1.97a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-1.97 1.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 4.97 6.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018Z"
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm btn"
|
||||
on:click|preventDefault={toggleDeleteConfirm}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
<!-- {/if} -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="w-full border-t border-base-content/[.2] pt-1">
|
||||
{#if data.userData?.username === "system"}
|
||||
{#if deleteAllConfirm}
|
||||
<button
|
||||
name="login-btn"
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
on:click={() => goto("/login")}
|
||||
class="btn btn-ghost w-full flex flex-row justify-between items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7m1.679-4.493-1.335 2.226a.75.75 0 0 1-1.174.144l-.774-.773a.5.5 0 0 1 .708-.708l.547.548 1.17-1.951a.5.5 0 1 1 .858.514M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4"
|
||||
/>
|
||||
<path
|
||||
d="M8.256 14a4.5 4.5 0 0 1-.229-1.004H3c.001-.246.154-.986.832-1.664C4.484 10.68 5.711 10 8 10q.39 0 .74.025c.226-.341.496-.65.804-.918Q8.844 9.002 8 9c-5 0-6 3-6 4s1 1 1 1z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Login</span>
|
||||
</button>
|
||||
<button
|
||||
name="create-btn"
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
on:click={() => goto("/signup")}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7m.5-5v1h1a.5.5 0 0 1 0 1h-1v1a.5.5 0 0 1-1 0v-1h-1a.5.5 0 0 1 0-1h1v-1a.5.5 0 0 1 1 0m-2-6a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4"
|
||||
/>
|
||||
<path
|
||||
d="M8.256 14a4.5 4.5 0 0 1-.229-1.004H3c.001-.246.154-.986.832-1.664C4.484 10.68 5.711 10 8 10q.39 0 .74.025c.226-.341.496-.65.804-.918Q8.844 9.002 8 9c-5 0-6 3-6 4s1 1 1 1z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Create Account</span>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
name="logout-btn"
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
on:click={async () => {
|
||||
const response = await fetch("/api/auth/logout", {
|
||||
method: "POST",
|
||||
});
|
||||
data.userData = null;
|
||||
window.location.href = "/";
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m0 5.996V14H3s-1 0-1-1 1-4 6-4q.845.002 1.544.107a4.5 4.5 0 0 0-.803.918A11 11 0 0 0 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664zM9 13a1 1 0 0 1 1-1v-1a2 2 0 1 1 4 0v1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm3-3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1"
|
||||
/>
|
||||
</svg>
|
||||
<span>Log Out</span>
|
||||
</button>
|
||||
<a
|
||||
href="/account"
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
>
|
||||
<path
|
||||
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
{#if deleteAllConfirm}
|
||||
<button
|
||||
class="btn btn-ghost w-full flex flex-row justify-between items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<div class="h-6 flex flex-row items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
>
|
||||
<path
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
<span>Clear Chats</span>
|
||||
</div>
|
||||
<div class="h-6 flex flex-row items-center">
|
||||
<button
|
||||
name="confirm-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={() => deleteAllChat()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
name="cancel-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={toggleDeleteAllConfirm}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M2.344 2.343h-.001a8 8 0 0 1 11.314 11.314A8.002 8.002 0 0 1 .234 10.089a8 8 0 0 1 2.11-7.746Zm1.06 10.253a6.5 6.5 0 1 0 9.108-9.275 6.5 6.5 0 0 0-9.108 9.275ZM6.03 4.97 8 6.94l1.97-1.97a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-1.97 1.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 4.97 6.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
on:click|preventDefault={toggleDeleteAllConfirm}
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<div class="h-6 flex flex-row items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
@ -463,14 +263,71 @@
|
||||
</path>
|
||||
</svg>
|
||||
<span>Clear Chats</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="h-6 flex flex-row items-center">
|
||||
<button
|
||||
name="confirm-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={() => deleteAllChat()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
name="cancel-delete"
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={toggleDeleteAllConfirm}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M2.344 2.343h-.001a8 8 0 0 1 11.314 11.314A8.002 8.002 0 0 1 .234 10.089a8 8 0 0 1 2.11-7.746Zm1.06 10.253a6.5 6.5 0 1 0 9.108-9.275 6.5 6.5 0 0 0-9.108 9.275ZM6.03 4.97 8 6.94l1.97-1.97a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-1.97 1.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 4.97 6.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
on:click|preventDefault={toggleDeleteAllConfirm}
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
>
|
||||
<path
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
<span>Clear Chats</span>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
on:click={toggleTheme}
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<label class="swap swap-rotate" for="theme-toggle">
|
||||
<label class="swap swap-rotate">
|
||||
<input type="checkbox" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
@ -500,12 +357,29 @@
|
||||
</label>
|
||||
<span>{theme == "dark" ? "Light" : "Dark"} theme</span>
|
||||
</button>
|
||||
<a
|
||||
href="/"
|
||||
class="btn btn-ghost w-full flex justify-start items-center p-2.5 text-left text-sm capitalize"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
>
|
||||
<path
|
||||
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<button on:click={hideSidebar} type="button"></button>
|
||||
|
||||
<div id="main_content" class="h-full w-full">
|
||||
<div class={"relative h-full transition-all md:ml-80"}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@ -7,8 +7,6 @@ interface ChatMetadata {
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
export const ssr = false; // off for now because ssr with auth is broken
|
||||
|
||||
export interface ModelStatus {
|
||||
name: string;
|
||||
size: number;
|
||||
@ -16,32 +14,14 @@ export interface ModelStatus {
|
||||
progress?: number;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
pref_theme: "light" | "dark";
|
||||
full_name: string;
|
||||
default_prompt: string;
|
||||
}
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch }) => {
|
||||
let userData: User | null = null;
|
||||
|
||||
const api_chat = await fetch("/api/chat/");
|
||||
const chats = (await api_chat.json()) as ChatMetadata[];
|
||||
|
||||
const model_api = await fetch("/api/model/all");
|
||||
const models = (await model_api.json()) as ModelStatus[];
|
||||
|
||||
const userData_api = await fetch("/api/user/");
|
||||
if (userData_api.ok) {
|
||||
userData = (await userData_api.json()) as User;
|
||||
}
|
||||
|
||||
return {
|
||||
chats,
|
||||
models,
|
||||
userData,
|
||||
};
|
||||
};
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
let repeat_penalty = 1.3;
|
||||
|
||||
let init_prompt =
|
||||
data.userData?.default_prompt ??
|
||||
"Below is an instruction that describes a task. Write a response that appropriately completes the request.";
|
||||
|
||||
let n_threads = 4;
|
||||
@ -54,12 +53,7 @@
|
||||
An easy way to chat with LLaMA based models.
|
||||
</h1>
|
||||
|
||||
<form
|
||||
on:submit|preventDefault={onCreateChat}
|
||||
id="form-create-chat"
|
||||
class="p-5"
|
||||
aria-label="Model Settings"
|
||||
>
|
||||
<form on:submit|preventDefault={onCreateChat} id="form-create-chat" class="p-5">
|
||||
<div class="w-full pb-20">
|
||||
<div class="mx-auto w-fit pt-5 flex flex-col lg:flex-row justify-center">
|
||||
<button
|
||||
@ -74,181 +68,175 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<div class="grid grid-cols-3 gap-4 p-3 bg-base-200" id="model_settings">
|
||||
<div class="col-span-3 text-xl font-medium">Model settings</div>
|
||||
<div
|
||||
class="tooltip tooltip-bottom col-span-2"
|
||||
data-tip="Controls how random the generated text is. Higher temperatures lead to more random and creative text, while lower temperatures lead to more predictable and conservative text."
|
||||
>
|
||||
<label for="temperature" class="label-text"
|
||||
>Temperature - [{temp}]</label
|
||||
|
||||
<div tabindex="-1" class="collapse-arrow rounded-box collapse bg-base-200">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">Model settings</div>
|
||||
<div class="collapse-content">
|
||||
<div class="grid grid-cols-3 gap-4 p-3">
|
||||
<div
|
||||
class="tooltip tooltip-bottom col-span-2"
|
||||
data-tip="Controls how random the generated text is. Higher temperatures lead to more random and creative text, while lower temperatures lead to more predictable and conservative text."
|
||||
>
|
||||
<input
|
||||
id="temperature"
|
||||
name="temperature"
|
||||
type="range"
|
||||
bind:value={temp}
|
||||
min="0.05"
|
||||
max="2"
|
||||
step="0.05"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip tooltip-bottom flex flex-col"
|
||||
data-tip="Controls the number of tokens that are considered when generating the next token. Higher values of top_k lead to more predictable text, while lower values of top_k lead to more creative text."
|
||||
>
|
||||
<label for="top_k" class="label-text pb-1">top_k</label>
|
||||
<input
|
||||
id="top_k"
|
||||
class="input-bordered input w-full"
|
||||
name="top_k"
|
||||
type="number"
|
||||
bind:value={top_k}
|
||||
min="0"
|
||||
max="100"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip tooltip-bottom col-span-2"
|
||||
data-tip="The maximum number of tokens that the model will generate. This parameter can be used to control the length of the generated text."
|
||||
>
|
||||
<label for="max_length" class="label-text"
|
||||
>Maximum generated tokens - [{max_length}]</label
|
||||
<label for="temperature" class="label-text"
|
||||
>Temperature - [{temp}]</label
|
||||
>
|
||||
<input
|
||||
name="temperature"
|
||||
type="range"
|
||||
bind:value={temp}
|
||||
min="0.05"
|
||||
max="2"
|
||||
step="0.05"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip tooltip-bottom flex flex-col"
|
||||
data-tip="Controls the number of tokens that are considered when generating the next token. Higher values of top_k lead to more predictable text, while lower values of top_k lead to more creative text."
|
||||
>
|
||||
<input
|
||||
id="max_length"
|
||||
name="max_length"
|
||||
type="range"
|
||||
bind:value={max_length}
|
||||
min="32"
|
||||
max="32768"
|
||||
step="16"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Controls the diversity of the generated text. Higher values of top_p lead to more diverse text, while lower values of top_p lead to less diverse text."
|
||||
>
|
||||
<label for="top_p" class="label-text pb-1">top_p</label>
|
||||
<input
|
||||
class="input-bordered input w-full"
|
||||
id="top_p"
|
||||
name="top_p"
|
||||
type="number"
|
||||
bind:value={top_p}
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.025"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip col-span-2"
|
||||
data-tip="The number of previous tokens that are considered when generating the next token. A longer context length can help the model to generate more coherent and informative text."
|
||||
>
|
||||
<label for="context_window" class="label-text"
|
||||
>Context Length - [{context_window}]</label
|
||||
<label for="top_k" class="label-text pb-1">top_k</label>
|
||||
<input
|
||||
class="input-bordered input w-full max-w-xs"
|
||||
name="top_k"
|
||||
type="number"
|
||||
bind:value={top_k}
|
||||
min="0"
|
||||
max="100"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip tooltip-bottom col-span-2"
|
||||
data-tip="The maximum number of tokens that the model will generate. This parameter can be used to control the length of the generated text."
|
||||
>
|
||||
<input
|
||||
id="context_window"
|
||||
name="context_window"
|
||||
type="range"
|
||||
bind:value={context_window}
|
||||
min="16"
|
||||
max="2048"
|
||||
step="16"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip col-span-2"
|
||||
data-tip="Number of layers to put on the GPU. The rest will be on the CPU."
|
||||
>
|
||||
<label for="gpu_layers" class="label-text"
|
||||
>GPU Layers - [{gpu_layers}]</label
|
||||
<label for="max_length" class="label-text"
|
||||
>Maximum generated tokens - [{max_length}]</label
|
||||
>
|
||||
<input
|
||||
name="max_length"
|
||||
type="range"
|
||||
bind:value={max_length}
|
||||
min="32"
|
||||
max="32768"
|
||||
step="16"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Controls the diversity of the generated text. Higher values of top_p lead to more diverse text, while lower values of top_p lead to less diverse text."
|
||||
>
|
||||
<input
|
||||
id="gpu_layers"
|
||||
name="gpu_layers"
|
||||
type="range"
|
||||
bind:value={gpu_layers}
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Defines the penalty associated with repeating the last 'n' tokens in a generated text sequence."
|
||||
>
|
||||
<label for="repeat_last_n" class="label-text pb-1">repeat_last_n</label>
|
||||
<input
|
||||
id="repeat_last_n"
|
||||
class="input-bordered input w-full"
|
||||
name="repeat_last_n"
|
||||
type="number"
|
||||
bind:value={repeat_last_n}
|
||||
min="0"
|
||||
max="100"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="model" class="label-text pb-1"> Model choice</label>
|
||||
<select
|
||||
name="model"
|
||||
id="models"
|
||||
class="select-bordered select w-full"
|
||||
aria-haspopup="menu"
|
||||
<label for="top_p" class="label-text pb-1">top_p</label>
|
||||
<input
|
||||
class="input-bordered input w-full max-w-xs"
|
||||
name="top_p"
|
||||
type="number"
|
||||
bind:value={top_p}
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.025"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip col-span-2"
|
||||
data-tip="The number of previous tokens that are considered when generating the next token. A longer context length can help the model to generate more coherent and informative text."
|
||||
>
|
||||
{#each modelsLabels as model}
|
||||
<option id={model} value={model}>{model}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Number of threads to run LLaMA on."
|
||||
>
|
||||
<label for="n_threads" class="label-text pb-1">n_threads</label>
|
||||
<input
|
||||
id="n_threads"
|
||||
class="input-bordered input w-full"
|
||||
name="n_threads"
|
||||
type="number"
|
||||
bind:value={n_threads}
|
||||
min="0"
|
||||
max="64"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Defines the penalty assigned to the model when it repeats certain tokens or patterns in the generated text."
|
||||
>
|
||||
<label for="repeat_penalty" class="label-text pb-1">
|
||||
repeat_penalty
|
||||
</label>
|
||||
<input
|
||||
id="repeat_penalty"
|
||||
class="input-bordered input w-full"
|
||||
name="repeat_penalty"
|
||||
type="number"
|
||||
bind:value={repeat_penalty}
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.05"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-3 flex flex-col">
|
||||
<label for="init_prompt" class="label-text pb-1">Prompt Template</label>
|
||||
<textarea
|
||||
class="textarea-bordered textarea h-24 w-full"
|
||||
name="init_prompt"
|
||||
bind:value={init_prompt}
|
||||
placeholder="Enter your prompt here"
|
||||
/>
|
||||
<label for="context_window" class="label-text"
|
||||
>Context Length - [{context_window}]</label
|
||||
>
|
||||
<input
|
||||
name="context_window"
|
||||
type="range"
|
||||
bind:value={context_window}
|
||||
min="16"
|
||||
max="2048"
|
||||
step="16"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip col-span-2"
|
||||
data-tip="Number of layers to put on the GPU. The rest will be on the CPU."
|
||||
>
|
||||
<label for="gpu_layers" class="label-text"
|
||||
>GPU Layers - [{gpu_layers}]</label
|
||||
>
|
||||
<input
|
||||
name="gpu_layers"
|
||||
type="range"
|
||||
bind:value={gpu_layers}
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
class="range range-sm mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Defines the penalty associated with repeating the last 'n' tokens in a generated text sequence."
|
||||
>
|
||||
<label for="repeat_last_n" class="label-text pb-1"
|
||||
>repeat_last_n</label
|
||||
>
|
||||
<input
|
||||
class="input-bordered input w-full max-w-xs"
|
||||
name="repeat_last_n"
|
||||
type="number"
|
||||
bind:value={repeat_last_n}
|
||||
min="0"
|
||||
max="100"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="model" class="label-text pb-1"> Model choice</label>
|
||||
<select name="model" class="select-bordered select w-full max-w-xs">
|
||||
{#each modelsLabels as model}
|
||||
<option value={model}>{model}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Number of threads to run LLaMA on."
|
||||
>
|
||||
<label for="n_threads" class="label-text pb-1">n_threads</label>
|
||||
<input
|
||||
class="input-bordered input w-full max-w-xs"
|
||||
name="n_threads"
|
||||
type="number"
|
||||
bind:value={n_threads}
|
||||
min="0"
|
||||
max="64"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip flex flex-col"
|
||||
data-tip="Defines the penalty assigned to the model when it repeats certain tokens or patterns in the generated text."
|
||||
>
|
||||
<label for="repeat_penalty" class="label-text pb-1">
|
||||
repeat_penalty
|
||||
</label>
|
||||
<input
|
||||
class="input-bordered input w-full max-w-xs"
|
||||
name="repeat_penalty"
|
||||
type="number"
|
||||
bind:value={repeat_penalty}
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.05"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-3 flex flex-col">
|
||||
<label for="init_prompt" class="label-text pb-1"
|
||||
>Prompt Template</label
|
||||
>
|
||||
<textarea
|
||||
class="textarea-bordered textarea h-24 w-full"
|
||||
name="init_prompt"
|
||||
bind:value={init_prompt}
|
||||
placeholder="Enter your prompt here"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,106 +0,0 @@
|
||||
<script context="module" lang="ts">
|
||||
export { load } from "./+page";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { writable } from "svelte/store";
|
||||
import { goto } from "$app/navigation";
|
||||
export let data: {
|
||||
user: {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
full_name: string;
|
||||
pref_theme: "light" | "dark";
|
||||
default_prompt: string;
|
||||
} | null;
|
||||
};
|
||||
let user = data.user;
|
||||
let id: string = user?.id ?? "";
|
||||
let username: string = user?.username ?? "";
|
||||
let email: string = user?.email ?? "";
|
||||
let full_name: string = user?.full_name ?? "";
|
||||
let pref_theme: "light" | "dark" = user?.pref_theme ?? "light";
|
||||
let default_prompt: string = user?.default_prompt ?? "";
|
||||
let status = writable<string | null>(null);
|
||||
|
||||
async function handleSubmit(event: Event) {
|
||||
event.preventDefault();
|
||||
// Implement the update logic here, e.g., sending a PUT request to update user preferences
|
||||
try {
|
||||
await fetch("/api/user/", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id,
|
||||
username,
|
||||
email,
|
||||
full_name,
|
||||
pref_theme,
|
||||
default_prompt,
|
||||
}),
|
||||
});
|
||||
|
||||
status.set("Preferences updated successfully");
|
||||
goto("/", { invalidateAll: true });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
status.set(error.message);
|
||||
} else {
|
||||
status.set("Failed to update preferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="card-group">
|
||||
<div class="card">
|
||||
<div class="card-title p-3 text-3xl justify-center font-bold">
|
||||
User Preferences
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{#if user}
|
||||
<form on:submit={handleSubmit}>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Username</span>
|
||||
</div>
|
||||
<input type="text" bind:value={username} disabled />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Full Name</span>
|
||||
</div>
|
||||
<input id="full_name" type="text" bind:value={full_name} />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Email</span>
|
||||
</div>
|
||||
<input id="email" type="email" bind:value={email} />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Default Prompt</span>
|
||||
</div>
|
||||
<textarea
|
||||
id="default_prompt"
|
||||
bind:value={default_prompt}
|
||||
style="resize:both; width:100%;"
|
||||
/>
|
||||
</div>
|
||||
{#if $status}
|
||||
<p>{$status}</p>
|
||||
{/if}
|
||||
<button class="btn" type="submit">Save Preferences</button>
|
||||
</form>
|
||||
{:else}
|
||||
<p>Loading...</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@ -1,27 +0,0 @@
|
||||
import type { Load } from "@sveltejs/kit";
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
pref_theme: "light" | "dark";
|
||||
full_name: string;
|
||||
default_prompt: string;
|
||||
}
|
||||
|
||||
export const load: Load = async () => {
|
||||
const user = await fetch("/api/user/", {
|
||||
method: "GET",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status == 401) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
window.location.href = "/";
|
||||
});
|
||||
return { user };
|
||||
};
|
||||
@ -127,19 +127,7 @@
|
||||
accept: "application/json",
|
||||
},
|
||||
},
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status == 401) {
|
||||
console.log("Not authorized");
|
||||
window.location.href = "/";
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
window.location.href = "/";
|
||||
});
|
||||
).then((response) => response.json());
|
||||
await invalidate("/api/chat/");
|
||||
await goto("/chat/" + newData);
|
||||
}
|
||||
@ -154,8 +142,6 @@
|
||||
await invalidate("/api/chat/" + $page.params.id);
|
||||
} else if (response.status === 202) {
|
||||
showToast("Chat in progress!");
|
||||
} else if (response.status === 401) {
|
||||
window.location.href = "/";
|
||||
} else {
|
||||
showToast("An error occurred: " + response.statusText);
|
||||
}
|
||||
@ -290,12 +276,12 @@
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="relative h-full max-h-screen overflow-hidden"
|
||||
class="relative mx-auto h-full max-h-screen w-full overflow-hidden"
|
||||
on:keydown={handleKeyDown}
|
||||
>
|
||||
<div class="mx-20">
|
||||
<div class="h-8 justify-content border-b border-base-content/[.2]">
|
||||
<div class="h-full relative flex items-center justify-center">
|
||||
<div class="w-full border-b border-base-content/[.2]">
|
||||
<div class="h-8 px-2 md:container md:mx-auto md:px-0">
|
||||
<div class="w-full h-full relative flex items-center justify-center">
|
||||
<div
|
||||
class="flex flex-row items-center justify-center color-base-300"
|
||||
title="Model"
|
||||
@ -444,46 +430,49 @@
|
||||
<div class="h-max pb-4">
|
||||
{#each history as question, i}
|
||||
{#if question.type === "human"}
|
||||
<div class="w-10/12 mx-auto sm:w-10/12 chat chat-end py-4">
|
||||
<div class="chat-image self-start pl-1 pt-1">
|
||||
<div
|
||||
class="mask mask-squircle online flex aspect-square w-8 items-center justify-center overflow-hidden bg-gradient-to-b from-primary to-primary-focus"
|
||||
>
|
||||
<span class="text-xs text-neutral-content">I</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="chat-bubble whitespace-normal break-words bg-base-300 text-base font-light text-base-content"
|
||||
>
|
||||
<!-- {question.data.content} -->
|
||||
<div class="w-full overflow-hidden break-words">
|
||||
{@html renderMarkdown(question.data.content)}
|
||||
</div>
|
||||
</div>
|
||||
{#if i === history.length - 1 && !isLoading}
|
||||
<div style="width: 100%; text-align: right;">
|
||||
<button
|
||||
disabled={isLoading}
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={() => deletePrompt(data.chat.id, i)}
|
||||
<div class="w-full border-y border-base-content/[.2] bg-base-300">
|
||||
<div class="w-11/12 mx-auto sm:w-10/12 chat chat-start py-4">
|
||||
<div class="chat-image self-start pl-1 pt-1">
|
||||
<div
|
||||
class="mask mask-squircle online flex aspect-square w-8 items-center justify-center overflow-hidden bg-gradient-to-b from-primary to-primary-focus"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="text-xs text-neutral-content">I</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="chat-bubble whitespace-normal break-words bg-base-300 text-base font-light text-base-content"
|
||||
>
|
||||
<!-- {question.data.content} -->
|
||||
<div class="w-full overflow-hidden break-words">
|
||||
{@html renderMarkdown(question.data.content)}
|
||||
</div>
|
||||
</div>
|
||||
{#if i === history.length - 1 && !isLoading}
|
||||
<div style="width: 100%; text-align: right;">
|
||||
<button
|
||||
disabled={isLoading}
|
||||
class="btn-ghost btn-sm btn"
|
||||
on:click|preventDefault={() =>
|
||||
deletePrompt(data.chat.id, i)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
class="fill-base-content"
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if question.type === "ai"}
|
||||
<div class="w-10/12 mx-auto sm:w-10/12 chat chat-start py-4">
|
||||
<div class="w-11/12 mx-auto sm:w-10/12 chat chat-start py-4">
|
||||
<div class="chat-image self-start pl-1 pt-1">
|
||||
<div
|
||||
class="mask mask-squircle online flex aspect-square w-8 items-center justify-center overflow-hidden bg-gradient-to-b from-primary to-primary-focus"
|
||||
@ -524,7 +513,6 @@
|
||||
d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Delete</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
@ -564,7 +552,7 @@
|
||||
class="btn btn-ghost h-10 w-14 rounded-l-none rounded-r-lg border-0 text-lg"
|
||||
class:loading={isLoading}
|
||||
on:click|preventDefault={askQuestion}
|
||||
><span class="sr-only">Send</span>
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
|
||||
@ -28,22 +28,12 @@ interface Response {
|
||||
id: string;
|
||||
created: string;
|
||||
params: Params;
|
||||
owner: string;
|
||||
history: Message[];
|
||||
}
|
||||
|
||||
export const load: PageLoad = async ({ fetch, params }) => {
|
||||
const data = await fetch("/api/chat/" + params.id)
|
||||
.then((response) => {
|
||||
if (response.status == 401) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
window.location.href = "/";
|
||||
});
|
||||
const r = await fetch("/api/chat/" + params.id);
|
||||
const data = (await r.json()) as Response;
|
||||
|
||||
return {
|
||||
chat: data,
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from "$app/navigation";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
let username = "";
|
||||
let password = "";
|
||||
let error = writable<string | null>(null);
|
||||
|
||||
async function handleSubmit(event: Event) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const response = await fetch("/api/auth/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
username,
|
||||
password,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
localStorage.setItem("token", data.access_token);
|
||||
goto("/", { invalidateAll: true });
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
error.set(errorData.detail || "Login failed");
|
||||
}
|
||||
} catch (err) {
|
||||
error.set("An error occurred");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="card-group">
|
||||
<div class="card">
|
||||
<div class="card-title p-3 text-3xl justify-center font-bold">
|
||||
Sign In
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form on:submit={handleSubmit}>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
bind:value={username}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
bind:value={password}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if $error}
|
||||
<p style="color: red;">{$error}</p>
|
||||
{/if}
|
||||
<button class="btn" type="submit">Authenticate</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@ -264,7 +264,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ml-12 pt-1">
|
||||
<div class="top-section">
|
||||
<div class="search-row">
|
||||
<input
|
||||
type="text"
|
||||
@ -327,7 +327,6 @@
|
||||
<div class="model-details">
|
||||
{#if models.length > 1}
|
||||
<select
|
||||
class="select-bordered select w-full"
|
||||
bind:value={selectedVariant[prefix]}
|
||||
on:change={(event) => handleVariantChange(prefix, event)}
|
||||
>
|
||||
|
||||
@ -1,165 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
let username = "";
|
||||
let secret = "";
|
||||
let full_name = "";
|
||||
let email = "";
|
||||
let auth_type = 1;
|
||||
let error = "";
|
||||
let success = "";
|
||||
|
||||
async function handleSubmit(event: Event) {
|
||||
event.preventDefault();
|
||||
error = "";
|
||||
success = "";
|
||||
const response = await fetch("/api/user/create", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
secret,
|
||||
full_name,
|
||||
email,
|
||||
auth_type,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
success = "User created successfully!";
|
||||
await authAfterCreate(event);
|
||||
goto("/account");
|
||||
} else {
|
||||
const data = await response.json();
|
||||
error = data.detail || "An error occurred";
|
||||
}
|
||||
}
|
||||
|
||||
async function authAfterCreate(event: Event) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const response = await fetch("/api/auth/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
username: username,
|
||||
password: secret,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
goto("/", { invalidateAll: true });
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
error = errorData.detail || "Login failed";
|
||||
}
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : "An unknown error occurred";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="card-group">
|
||||
<div class="card">
|
||||
<div class="card-title p-3 text-3xl justify-center font-bold">
|
||||
Register a new user
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form on:submit={handleSubmit}>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
bind:value={username}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
bind:value={secret}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<p class="error-message">{error}</p>
|
||||
{/if}
|
||||
{#if success}
|
||||
<p class="success-message">{success}</p>
|
||||
{/if}
|
||||
<button class="btn" type="submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title p-3 text-3xl justify-center font-bold">
|
||||
Or link an account (comming soon)
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button name="google-btn" class="btn" disabled={true}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M15.545 6.558a9.4 9.4 0 0 1 .139 1.626c0 2.434-.87 4.492-2.384 5.885h.002C11.978 15.292 10.158 16 8 16A8 8 0 1 1 8 0a7.7 7.7 0 0 1 5.352 2.082l-2.284 2.284A4.35 4.35 0 0 0 8 3.166c-2.087 0-3.86 1.408-4.492 3.304a4.8 4.8 0 0 0 0 3.063h.003c.635 1.893 2.405 3.301 4.492 3.301 1.078 0 2.004-.276 2.722-.764h-.003a3.7 3.7 0 0 0 1.599-2.431H8v-3.08z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Link Google Account</span>
|
||||
</button>
|
||||
<button name="reddit-btn" class="btn" disabled={true}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M6.167 8a.83.83 0 0 0-.83.83c0 .459.372.84.83.831a.831.831 0 0 0 0-1.661m1.843 3.647c.315 0 1.403-.038 1.976-.611a.23.23 0 0 0 0-.306.213.213 0 0 0-.306 0c-.353.363-1.126.487-1.67.487-.545 0-1.308-.124-1.671-.487a.213.213 0 0 0-.306 0 .213.213 0 0 0 0 .306c.564.563 1.652.61 1.977.61zm.992-2.807c0 .458.373.83.831.83s.83-.381.83-.83a.831.831 0 0 0-1.66 0z"
|
||||
/>
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.828-1.165c-.315 0-.602.124-.812.325-.801-.573-1.9-.945-3.121-.993l.534-2.501 1.738.372a.83.83 0 1 0 .83-.869.83.83 0 0 0-.744.468l-1.938-.41a.2.2 0 0 0-.153.028.2.2 0 0 0-.086.134l-.592 2.788c-1.24.038-2.358.41-3.17.992-.21-.2-.496-.324-.81-.324a1.163 1.163 0 0 0-.478 2.224q-.03.17-.029.353c0 1.795 2.091 3.256 4.669 3.256s4.668-1.451 4.668-3.256c0-.114-.01-.238-.029-.353.401-.181.688-.592.688-1.069 0-.65-.525-1.165-1.165-1.165"
|
||||
/>
|
||||
</svg>
|
||||
<span>Link Reddit Account</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title pt-3 text-3xl justify-center font-bold">
|
||||
Already have an account?
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button name="login-btn" class="btn" on:click={() => goto("/login")}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="currentColor"
|
||||
class="mr-3"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7m1.679-4.493-1.335 2.226a.75.75 0 0 1-1.174.144l-.774-.773a.5.5 0 0 1 .708-.708l.547.548 1.17-1.951a.5.5 0 1 1 .858.514M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4"
|
||||
/>
|
||||
<path
|
||||
d="M8.256 14a4.5 4.5 0 0 1-.229-1.004H3c.001-.246.154-.986.832-1.664C4.484 10.68 5.711 10 8 10q.39 0 .74.025c.226-.341.496-.65.804-.918Q8.844 9.002 8 9c-5 0-6 3-6 4s1 1 1 1z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Login Instead</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
Loading…
x
Reference in New Issue
Block a user