Proper dev & deployment images. Converts front-end to SPA. (#84)

* begin work on dev environment

* more work on dev image

* working dev + prod images with SPA front-end

* reworked dockerfile

* make CI point to the right action

* Improvements to github actions (#79)

* Improvements to github actions

* Change username to repo owner username

* Add fix for login into ghcr (#81)

* Update bug_report.yml

* added dev instructions to readme

* reduced number of steps in dockerfile

---------

Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
This commit is contained in:
Nathan Sarrazin
2023-03-27 16:44:20 +02:00
committed by GitHub
parent 75de7faece
commit 293a23e487
20 changed files with 427 additions and 127 deletions

View File

@@ -49,6 +49,7 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
target: release
push: ${{ github.event_name != 'pull_request' }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -1,4 +1,5 @@
FROM gcc:11 as llama_builder
# Compile llama
FROM gcc:11 as llama_builder
WORKDIR /tmp
@@ -8,54 +9,73 @@ RUN cd llama.cpp && \
make && \
mv main llama
# Copy over rest of the project files
# Base image for node
FROM node:19 as node_base
FROM ubuntu:22.04 as deployment
WORKDIR /usr/src/app
# Install pip and requirements
# Base image for runtime
FROM ubuntu:22.04 as base
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Amsterdam
WORKDIR /usr/src/app
# install pip
RUN apt update
RUN apt-get install -y python3-pip curl wget
RUN pip install --upgrade pip
# Install MongoDB and necessary tools
RUN apt update && \
apt install -y curl wget gnupg python3-pip && \
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add - && \
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list && \
apt-get update && \
apt-get install -y mongodb-org
# install nodejs
RUN curl -sL https://deb.nodesource.com/setup_19.x | bash
RUN apt-get install nodejs
# MongoDB
RUN wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add -
RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
RUN apt-get update
RUN apt-get install -y mongodb-org
# install requirements
# copy & install python reqs
COPY ./api/requirements.txt api/requirements.txt
RUN pip install -r ./api/requirements.txt
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r ./api/requirements.txt
# copy llama binary from llama_builder
# Copy files
COPY --from=llama_builder /tmp/llama.cpp/llama /usr/bin/llama
# copy built webserver from web_builder
COPY ./web/package.json web/
COPY ./web/package-lock.json web/
# Install Node modules
RUN cd web && npm install
# Dev environment
FROM base as dev
ENV NODE_ENV='development'
COPY ./web /usr/src/app/web/
# Install Node.js and npm packages
COPY --from=node_base /usr/local /usr/local
COPY ./web/package*.json ./
RUN npm ci
#copy api folder
# Copy the rest of the project files
COPY web /usr/src/app/web
COPY ./api /usr/src/app/api
COPY dev.sh /usr/src/app/dev.sh
RUN chmod +x /usr/src/app/dev.sh
CMD ./dev.sh
# Build frontend
FROM node_base as frontend_builder
COPY ./web/package*.json ./
RUN npm ci
COPY ./web /usr/src/app/web/
WORKDIR /usr/src/app/web/
RUN npm run build
# Runtime environment
FROM base as release
ENV NODE_ENV='production'
WORKDIR /usr/src/app
COPY --from=frontend_builder /usr/src/app/web/build /usr/src/app/api/static/
COPY ./api /usr/src/app/api
# get the deploy script with the right permissions
COPY deploy.sh /usr/src/app/deploy.sh
RUN chmod +x /usr/src/app/deploy.sh
CMD /usr/src/app/deploy.sh
CMD ./deploy.sh

View File

@@ -14,21 +14,21 @@ A chat interface based on `llama.cpp` for running Alpaca models. Entirely self-h
## Getting started
Setting up Serge is very easy. TLDR for running it with Alpaca 7B:
```
git clone https://github.com/nsarrazin/serge.git
cd serge
docker compose up -d
docker compose up --build -d
docker compose exec serge python3 /usr/src/app/api/utils/download.py tokenizer 7B
```
#### Windows
:warning: For cloning on windows, use `git clone https://github.com/nsarrazin/serge.git --config core.autocrlf=input`.
:warning: For cloning on windows, use `git clone https://github.com/nsarrazin/serge.git --config core.autocrlf=input`.
Make sure you have docker desktop installed, WSL2 configured and enough free RAM to run models. (see below)
#### Kubernetes
Setting up Serge on Kubernetes can be found in the wiki: https://github.com/nsarrazin/serge/wiki/Integrating-Serge-in-your-orchestration#kubernetes-example
@@ -59,6 +59,16 @@ llama will just crash if you don't have enough available memory for your model.
Feel free to join the discord if you need help with the setup: https://discord.gg/62Hc6FEYQH
## Contributing
Serge is always open for contributions! If you catch a bug or have a feature idea, feel free to open an issue or a PR.
If you want to run Serge in development mode (with hot-module reloading for svelte & autoreload for FastAPI) you can do so like this:
```
docker compose -f docker-compose.dev.yml up -d --build
```
## What's next
- [x] Front-end to interface with the API

View File

@@ -1,21 +0,0 @@
FROM gcc:10.2 as builder
WORKDIR /tmp
RUN git clone https://github.com/ggerganov/llama.cpp.git --branch master-d5850c5
RUN cd llama.cpp && \
make && \
mv main llama
FROM ubuntu:latest
WORKDIR /usr/src/app
RUN apt update
RUN apt-get install -y python3-pip
RUN pip install --upgrade pip
COPY ./requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY --from=builder /tmp/llama.cpp/llama /usr/bin/llama

View File

@@ -9,10 +9,12 @@ from fastapi.middleware.cors import CORSMiddleware
from beanie.odm.enums import SortDirection
from sse_starlette.sse import EventSourceResponse
from utils.initiate_database import initiate_database
from utils.initiate_database import initiate_database, Settings
from utils.generate import generate, get_full_prompt_from_chat
from utils.convert import convert_all
from models import Question, Chat, ChatParameters
from fastapi.staticfiles import StaticFiles
from starlette.responses import FileResponse
# Configure logging settings
@@ -27,6 +29,8 @@ logging.basicConfig(
# Define a logger for the current module
logger = logging.getLogger(__name__)
settings = Settings()
tags_metadata = [
{
"name": "misc.",
@@ -46,6 +50,32 @@ app = FastAPI(
title="Serge", version="0.0.1", description=description, tags_metadata=tags_metadata
)
api_app = FastAPI(title="Serge API")
app.mount('/api', api_app)
if settings.NODE_ENV == "production":
@app.middleware("http")
async def add_custom_header(request, call_next):
response = await call_next(request)
if response.status_code == 404:
return FileResponse('static/200.html')
return response
@app.exception_handler(404)
def not_found(request, exc):
return FileResponse('static/200.html')
async def homepage(request):
return FileResponse('static/200.html')
app.route('/', homepage)
app.mount('/', StaticFiles(directory='static'))
if settings.NODE_ENV == "development":
start_app = api_app
else:
start_app = app
origins = [
"http://localhost",
"http://api:9124",
@@ -90,7 +120,7 @@ async def convert_model_files():
logger.info("models are ready")
@app.on_event("startup")
@start_app.on_event("startup")
async def start_database():
logger.info("initializing database connection")
await initiate_database()
@@ -99,14 +129,14 @@ async def start_database():
asyncio.create_task(convert_model_files())
@app.get("/models", tags=["misc."])
@api_app.get("/models", tags=["misc."])
def list_of_installed_models(
models: Annotated[list[str], Depends(dep_models_ready)]
):
return models
@app.post("/chat", tags=["chats"])
@api_app.post("/chat", tags=["chats"])
async def create_new_chat(
model: str = "ggml-alpaca-7B-q4_0.bin",
temperature: float = 0.1,
@@ -136,14 +166,14 @@ async def create_new_chat(
return chat.id
@app.get("/chat/{chat_id}", tags=["chats"])
@api_app.get("/chat/{chat_id}", tags=["chats"])
async def get_specific_chat(chat_id: str):
chat = await Chat.get(chat_id)
await chat.fetch_all_links()
return chat
@app.delete("/chat/{chat_id}", tags=["chats"])
@api_app.delete("/chat/{chat_id}", tags=["chats"])
async def delete_chat(chat_id: str):
chat = await Chat.get(chat_id)
deleted_chat = await chat.delete()
@@ -175,7 +205,7 @@ def remove_matching_end(a, b):
return b
@app.get("/chat/{chat_id}/question", dependencies=[Depends(dep_models_ready)])
@api_app.get("/chat/{chat_id}/question", dependencies=[Depends(dep_models_ready)])
async def stream_ask_a_question(chat_id: str, prompt: str):
chat = await Chat.get(chat_id)
@@ -216,7 +246,7 @@ async def stream_ask_a_question(chat_id: str, prompt: str):
return EventSourceResponse(event_generator())
@app.post("/chat/{chat_id}/question", dependencies=[Depends(dep_models_ready)])
@api_app.post("/chat/{chat_id}/question", dependencies=[Depends(dep_models_ready)])
async def ask_a_question(chat_id: str, prompt: str):
chat = await Chat.get(chat_id)
await chat.fetch_link(Chat.parameters)
@@ -240,7 +270,7 @@ async def ask_a_question(chat_id: str, prompt: str):
return {"question" : prompt, "answer" : answer[len(full_prompt)+1:]}
@app.get("/chats", tags=["chats"])
@api_app.get("/chats", tags=["chats"])
async def get_all_chats():
res = []

View File

@@ -53,7 +53,7 @@ async def generate(
if return_code != 0:
error_output = await procLlama.stderr.read()
logger.error(error_output.decode("utf-8"))
raise ValueError(error_output.decode("utf-8"))
raise ValueError(f"RETURN CODE {return_code}\n\n"+error_output.decode("utf-8"))
else:
return

View File

@@ -8,6 +8,7 @@ from models import Question, Chat, ChatParameters
class Settings(BaseSettings):
NODE_ENV: str = "development"
class Config:
orm_mode = True

View File

@@ -2,14 +2,11 @@
mongod &
# Start the web server
cd web && npm run dev -- --host 0.0.0.0 --port 8008 &
# Start the API
cd api && uvicorn main:app --host 0.0.0.0 --port 9124 --root-path /api/ &
cd api && uvicorn main:app --host 0.0.0.0 --port 8008 &
# Wait for any process to exit
wait -n
# Exit with status of process that exited first
exit $?
exit $?

14
dev.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
mongod &
# Start the web server
cd web && npm run dev -- --host 0.0.0.0 --port 8008 &
# Start the API
cd api && uvicorn main:api_app --reload --host 0.0.0.0 --port 9124 --root-path /api/ &
# Wait for any process to exit
wait -n
# Exit with status of process that exited first
exit $?

20
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,20 @@
version: "3.9"
services:
serge:
restart: on-failure
build:
context: .
dockerfile: Dockerfile
target: dev
volumes:
- ./web:/usr/src/app/web/
- ./api:/usr/src/app/api/
- datadb:/data/db
- weights:/usr/src/app/weights/
- /etc/localtime:/etc/localtime:ro
ports:
- "8008:8008"
volumes:
datadb:
weights:

View File

@@ -5,6 +5,7 @@ services:
build:
context: .
dockerfile: Dockerfile
target: release
volumes:
- datadb:/data/db
- weights:/usr/src/app/weights/

View File

@@ -1,20 +0,0 @@
FROM node:19.8.1
# Set the Node environment to development to ensure all packages are installed
ENV NODE_ENV development
# Change our current working directory
WORKDIR /usr/src/app
# Copy over `package.json` and lock files to optimize the build process
COPY package.json .
COPY package-lock.json .
# Install Node modules
RUN npm install
# Copy over rest of the project files
COPY . .
# Expose port 24678 for Vite's HMR
EXPOSE 24678

236
web/package-lock.json generated
View File

@@ -15,6 +15,8 @@
"devDependencies": {
"@feltjs/eslint-config": "^0.2.1",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.2.3",
"@sveltejs/adapter-static": "^2.0.1",
"@sveltejs/kit": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
@@ -569,6 +571,150 @@
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
"dev": true
},
"node_modules/@rollup/plugin-commonjs": {
"version": "24.0.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
"integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"glob": "^8.0.3",
"is-reference": "1.2.1",
"magic-string": "^0.27.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/magic-string": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@rollup/plugin-json": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz",
"integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
"integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-builtin-module": "^3.2.0",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
"integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@sveltejs/adapter-auto": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.0.0.tgz",
@@ -581,6 +727,30 @@
"@sveltejs/kit": "^1.0.0"
}
},
"node_modules/@sveltejs/adapter-node": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-1.2.3.tgz",
"integrity": "sha512-Fv6NyVpVWYA63KRaV6dDjcU8ytcWFiUr0siJOoHl+oWy5WHNEuRiJOUdiZzYbZo8MmvFaCoxHkTgPrVQhpqaRA==",
"dev": true,
"dependencies": {
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"rollup": "^3.7.0"
},
"peerDependencies": {
"@sveltejs/kit": "^1.0.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-2.0.1.tgz",
"integrity": "sha512-o5/q3YwD/ErxYCFlK1v3ydvldyNKk1lh3oeyxn4mhz+Pkbx/uuxhzmbOpytTlp5aVqNHDVsb04xadUzOFCDDzw==",
"dev": true,
"peerDependencies": {
"@sveltejs/kit": "^1.5.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.12.0.tgz",
@@ -665,6 +835,12 @@
"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==",
"dev": true
},
"node_modules/@types/estree": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -677,6 +853,12 @@
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
"dev": true
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true
},
"node_modules/@types/semver": {
"version": "7.3.13",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
@@ -1084,6 +1266,18 @@
"node": "*"
}
},
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1207,6 +1401,12 @@
"simple-swizzle": "^0.2.2"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1657,6 +1857,12 @@
"node": ">=4.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -2007,6 +2213,21 @@
"node": ">=8"
}
},
"node_modules/is-builtin-module": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
"dev": true,
"dependencies": {
"builtin-modules": "^3.3.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
@@ -2037,6 +2258,12 @@
"node": ">=0.10.0"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -2054,6 +2281,15 @@
"node": ">=8"
}
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",

View File

@@ -12,6 +12,8 @@
"devDependencies": {
"@feltjs/eslint-config": "^0.2.1",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.2.3",
"@sveltejs/adapter-static": "^2.0.1",
"@sveltejs/kit": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",

View File

@@ -8,12 +8,13 @@
$: deleteConfirm = false;
async function deleteChat(chatID: string) {
var response = await fetch("/api/chat/" + chatID, { method: "DELETE" });
const response = await fetch("/api/chat/" + chatID, { method: "DELETE" });
if (response.status == 200) {
toggleDeleteConfirm();
await goto("/");
await invalidate("/api/chats");
} else {
alert("Error " + response.status + ": " + response.statusText);
console.error("Error " + response.status + ": " + response.statusText);
}
}
@@ -23,9 +24,9 @@
function timeSince(datestring: string) {
const date = new Date(datestring);
var seconds = Math.floor((Date.now() - date.getTime()) / 1000);
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
var interval = seconds / 31536000;
let interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
@@ -74,7 +75,7 @@
<div>
<span class="font-semibold">{chat.model}</span>
<span class="ml-3">{timeSince(chat.created) + " ago"}</span>
{#if $page.params.id == chat.id}
{#if $page.params.id === chat.id}
{#if deleteConfirm}
<button
name="confirm-delete"

View File

@@ -11,6 +11,6 @@ export const load: LayoutLoad = async ({ fetch }) => {
const r = await fetch("/api/chats");
const chats = (await r.json()) as t[];
return {
chats: chats,
chats,
};
};

View File

@@ -1,24 +0,0 @@
import { redirect } from "@sveltejs/kit";
import type { Actions } from "./$types";
export const actions = {
default: async ({ fetch, request }) => {
const formData = await request.formData();
const convertedFormEntries = Array.from(formData, ([key, value]) => [
key,
typeof value === "string" ? value : value.name,
]);
const searchParams = new URLSearchParams(convertedFormEntries);
const r = await fetch("/api/chat?" + searchParams.toString(), {
method: "POST",
});
if (r.ok) {
const data = await r.json();
throw redirect(303, "/chat/" + data);
} else {
console.log(r.statusText);
}
},
} satisfies Actions;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import type { PageData } from "./$types";
import { goto, invalidate } from "$app/navigation";
export let data: PageData;
const modelAvailable = data.models.length > 0;
@@ -18,14 +18,39 @@
let n_threads = 4;
let ctx_length = 512;
async function onCreateChat(event: Event) {
const form = document.getElementById("form-create-chat") as HTMLFormElement;
const formData = new FormData(form);
const convertedFormEntries = Array.from(formData, ([key, value]) => [
key,
typeof value === "string" ? value : value.name,
]);
const searchParams = new URLSearchParams(convertedFormEntries);
const r = await fetch("/api/chat?" + searchParams.toString(), {
method: "POST",
});
console.log(r);
if (r.ok) {
const data = await r.json();
await goto("/chat/" + data);
await invalidate("/api/chats");
} else {
console.log(r.statusText);
}
}
</script>
<h1 class="text-3xl font-bold text-center pt-5">Say Hi to Serge!</h1>
<h1 class="text-xl text-center pt-2 pb-5">
<h1 class="text-xl font-light text-center pt-2 pb-5">
An easy way to chat with Alpaca & other LLaMa based models.
</h1>
<form method="POST" class="p-5">
<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">
<button class=" mx-auto btn btn-primary ml-5" disabled={!modelAvailable}

View File

@@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
@@ -11,7 +11,10 @@ const config = {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
adapter: adapter({
fallback: '200.html' // may differ from host to host
}
),
csrf: {
checkOrigin: false,
}

View File

@@ -4,6 +4,10 @@ import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
server: {
fs: {
// Allow serving files from one level up to the project root
allow: [".."],
},
proxy: {
"/api": {
target: "http://serge:9124/",