mirror of
https://github.com/mudler/LocalAI.git
synced 2026-05-24 08:38:02 -04:00
Compare commits
233 Commits
v2.24.0
...
feat/realt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f272605b95 | ||
|
|
9a0982066f | ||
|
|
30e3c47598 | ||
|
|
01aace3017 | ||
|
|
5f2c83700c | ||
|
|
90206830c1 | ||
|
|
7592984b64 | ||
|
|
c526f05de5 | ||
|
|
06e438d68b | ||
|
|
3dd1b300e9 | ||
|
|
ebfe8dd119 | ||
|
|
136fbd25f5 | ||
|
|
59531562a6 | ||
|
|
9273395e38 | ||
|
|
0318434b17 | ||
|
|
9614422713 | ||
|
|
a3fd8caaa6 | ||
|
|
1796a1713d | ||
|
|
4f69170273 | ||
|
|
60c99ddc50 | ||
|
|
b4fea58076 | ||
|
|
9e965033bb | ||
|
|
05225c93e4 | ||
|
|
65f4c12d1e | ||
|
|
f45d11c734 | ||
|
|
9b6826d5ff | ||
|
|
4ca7689f31 | ||
|
|
dcb13a7e6f | ||
|
|
8f507c39c0 | ||
|
|
d7dee3a5ec | ||
|
|
b8d74e52b1 | ||
|
|
62abe0d2c9 | ||
|
|
5414c294c4 | ||
|
|
1b3e89c89c | ||
|
|
69c6e5b192 | ||
|
|
0c02512f15 | ||
|
|
b0ead0bf12 | ||
|
|
ab5adf40af | ||
|
|
8d82afb595 | ||
|
|
aea71dd2c6 | ||
|
|
9fdb44323d | ||
|
|
6a299c04a7 | ||
|
|
9ce71fe427 | ||
|
|
e8de7b52da | ||
|
|
1780ccadbc | ||
|
|
f8cffd05e5 | ||
|
|
b898cd49b5 | ||
|
|
7cd33d10c9 | ||
|
|
cd480dbe5c | ||
|
|
cb8bf79ada | ||
|
|
b206eab80f | ||
|
|
80dc23fab9 | ||
|
|
844c0c422d | ||
|
|
07655c0c2e | ||
|
|
bebfd19b45 | ||
|
|
6e34430d99 | ||
|
|
0d08aaa29b | ||
|
|
66f9c06e7d | ||
|
|
775adf871f | ||
|
|
a0fc19a3d6 | ||
|
|
7bd18662a7 | ||
|
|
95b0739906 | ||
|
|
cad7e9a1cd | ||
|
|
4426efab05 | ||
|
|
6765b17acd | ||
|
|
ae1340d59b | ||
|
|
fc52f179fe | ||
|
|
4f43a9a162 | ||
|
|
20edd44463 | ||
|
|
1a4f9d8453 | ||
|
|
f2dd33b8f4 | ||
|
|
25e988868c | ||
|
|
ab344e4f47 | ||
|
|
fac7893dd6 | ||
|
|
9be338cfe4 | ||
|
|
b4d4f96919 | ||
|
|
8cc2d01caa | ||
|
|
bf37eebecb | ||
|
|
3f0850b58b | ||
|
|
2ffa89b8b9 | ||
|
|
d43adc0205 | ||
|
|
78b34505ab | ||
|
|
e55a1bed59 | ||
|
|
0d7550ad54 | ||
|
|
b5992255ac | ||
|
|
e845cc0401 | ||
|
|
a10033e8a4 | ||
|
|
6c6d840e6b | ||
|
|
a8b3b3d6f4 | ||
|
|
ec66f7e3b1 | ||
|
|
05841c2435 | ||
|
|
c553d73748 | ||
|
|
1006e8a2ed | ||
|
|
9bcfda171b | ||
|
|
baee4f7bd5 | ||
|
|
286dc32fe0 | ||
|
|
36e4c0fcf0 | ||
|
|
3c21c8789a | ||
|
|
d9facbcee9 | ||
|
|
930280ecac | ||
|
|
3415e6ae74 | ||
|
|
f1082f3c6d | ||
|
|
f345f7a795 | ||
|
|
1a2a7a57b3 | ||
|
|
ae80a2bd24 | ||
|
|
c30ecdd535 | ||
|
|
f16c7cef92 | ||
|
|
e1dd78bcea | ||
|
|
25acb0cbbc | ||
|
|
7674c80bb6 | ||
|
|
e044970a5b | ||
|
|
639526d207 | ||
|
|
998ff9fa22 | ||
|
|
7122c7472e | ||
|
|
671381267a | ||
|
|
d1762e098e | ||
|
|
270d33504b | ||
|
|
9b0983d027 | ||
|
|
afd0af987d | ||
|
|
58524d40c9 | ||
|
|
2a7222c6aa | ||
|
|
0093985e7c | ||
|
|
7f51e2dddf | ||
|
|
f3bbdef77d | ||
|
|
9cbf168dc0 | ||
|
|
9572f0577b | ||
|
|
1a14c7d45a | ||
|
|
5c29e0cd4d | ||
|
|
1a74af1492 | ||
|
|
8f6332ab23 | ||
|
|
816ae7a53a | ||
|
|
1d630e4185 | ||
|
|
bc8dd3ad14 | ||
|
|
b969053701 | ||
|
|
60bf7c9dd7 | ||
|
|
d65c10cee7 | ||
|
|
6c71698299 | ||
|
|
c7c275c7c8 | ||
|
|
d0adbee75d | ||
|
|
159a7f6df2 | ||
|
|
0eb2911aad | ||
|
|
cab9f88ca4 | ||
|
|
a3b675b09e | ||
|
|
6477913e8f | ||
|
|
138cd97ce7 | ||
|
|
4dd9ac39b0 | ||
|
|
23499ddc8a | ||
|
|
8864156300 | ||
|
|
478014ca18 | ||
|
|
d45477b003 | ||
|
|
396fb88e33 | ||
|
|
a429ec1b3f | ||
|
|
5b5fb9c22a | ||
|
|
801a87c3a6 | ||
|
|
badbd212f7 | ||
|
|
c4bbecc4d6 | ||
|
|
8a08e9ec67 | ||
|
|
61e486dbf5 | ||
|
|
f2f387e1dd | ||
|
|
3be9a08fc9 | ||
|
|
b325807c60 | ||
|
|
ae9855a39e | ||
|
|
9ac62b589f | ||
|
|
d12660a286 | ||
|
|
3d3bd2d10f | ||
|
|
b656d10556 | ||
|
|
8c67f38ef6 | ||
|
|
4623728cd7 | ||
|
|
5f804aa6e8 | ||
|
|
f52c6e3a31 | ||
|
|
0b4bb7a562 | ||
|
|
2bc4b56a79 | ||
|
|
fc920cc58a | ||
|
|
fdb560b8e5 | ||
|
|
708cba0c1b | ||
|
|
24abf568cb | ||
|
|
7ca0e2d925 | ||
|
|
037e8030bf | ||
|
|
472d11f884 | ||
|
|
b40d5d12b7 | ||
|
|
6938618e30 | ||
|
|
5d9c530eaa | ||
|
|
9429a53db7 | ||
|
|
1d6d301370 | ||
|
|
8f2be82667 | ||
|
|
cca911f3e5 | ||
|
|
e37bbbaacc | ||
|
|
59cbf38b4b | ||
|
|
432c31d904 | ||
|
|
af33483687 | ||
|
|
5051074845 | ||
|
|
fc4a714992 | ||
|
|
0429e00746 | ||
|
|
73f1f25b9a | ||
|
|
044570fa85 | ||
|
|
37527420de | ||
|
|
1854b8c612 | ||
|
|
b8824f2ad9 | ||
|
|
3ab83e91df | ||
|
|
f2cb261797 | ||
|
|
c85f46a71d | ||
|
|
75b283d83c | ||
|
|
1918efdfdd | ||
|
|
ec239a0cd0 | ||
|
|
b74a936178 | ||
|
|
de1ddb8ba6 | ||
|
|
272763f625 | ||
|
|
3aff87a5cf | ||
|
|
885118e863 | ||
|
|
a03a9b9e51 | ||
|
|
f45d6c746a | ||
|
|
5eceb5f67c | ||
|
|
a9c0dd3a1e | ||
|
|
fb17e737f0 | ||
|
|
b5a21202ed | ||
|
|
e147f1bd3e | ||
|
|
61839efed2 | ||
|
|
a0fe050055 | ||
|
|
f943c4b803 | ||
|
|
cea5a0ea42 | ||
|
|
f5e1527a5a | ||
|
|
7184ca546f | ||
|
|
5592f5e820 | ||
|
|
d4c1746c7d | ||
|
|
88737e1d76 | ||
|
|
ba225f660b | ||
|
|
3127cd1352 | ||
|
|
b90d78d9f6 | ||
|
|
b86a3e4fa6 | ||
|
|
be907d993f | ||
|
|
ab0f8648a3 | ||
|
|
c226149503 | ||
|
|
4a079f893c |
9
.env
9
.env
@@ -82,6 +82,15 @@
|
|||||||
# Enable to allow p2p mode
|
# Enable to allow p2p mode
|
||||||
# LOCALAI_P2P=true
|
# LOCALAI_P2P=true
|
||||||
|
|
||||||
|
# Enable to use federated mode
|
||||||
|
# LOCALAI_FEDERATED=true
|
||||||
|
|
||||||
|
# Enable to start federation server
|
||||||
|
# FEDERATED_SERVER=true
|
||||||
|
|
||||||
|
# Define to use federation token
|
||||||
|
# TOKEN=""
|
||||||
|
|
||||||
### Watchdog settings
|
### Watchdog settings
|
||||||
###
|
###
|
||||||
# Enables watchdog to kill backends that are inactive for too much time
|
# Enables watchdog to kill backends that are inactive for too much time
|
||||||
|
|||||||
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
@@ -5,6 +5,10 @@ dependencies:
|
|||||||
- any:
|
- any:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'Makefile'
|
- any-glob-to-any-file: 'Makefile'
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: '*.mod'
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: '*.sum'
|
||||||
|
|
||||||
kind/documentation:
|
kind/documentation:
|
||||||
- any:
|
- any:
|
||||||
|
|||||||
47
.github/workflows/image.yml
vendored
47
.github/workflows/image.yml
vendored
@@ -280,6 +280,7 @@ jobs:
|
|||||||
makeflags: ${{ matrix.makeflags }}
|
makeflags: ${{ matrix.makeflags }}
|
||||||
latest-image: ${{ matrix.latest-image }}
|
latest-image: ${{ matrix.latest-image }}
|
||||||
latest-image-aio: ${{ matrix.latest-image-aio }}
|
latest-image-aio: ${{ matrix.latest-image-aio }}
|
||||||
|
skip-drivers: ${{ matrix.skip-drivers }}
|
||||||
secrets:
|
secrets:
|
||||||
dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }}
|
dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }}
|
dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
@@ -301,6 +302,7 @@ jobs:
|
|||||||
latest-image: 'latest-cpu'
|
latest-image: 'latest-cpu'
|
||||||
latest-image-aio: 'latest-aio-cpu'
|
latest-image-aio: 'latest-aio-cpu'
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
skip-drivers: 'false'
|
||||||
- build-type: 'cublas'
|
- build-type: 'cublas'
|
||||||
cuda-major-version: "11"
|
cuda-major-version: "11"
|
||||||
cuda-minor-version: "7"
|
cuda-minor-version: "7"
|
||||||
@@ -312,6 +314,7 @@ jobs:
|
|||||||
base-image: "ubuntu:22.04"
|
base-image: "ubuntu:22.04"
|
||||||
runs-on: 'arc-runner-set'
|
runs-on: 'arc-runner-set'
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
skip-drivers: 'false'
|
||||||
- build-type: 'cublas'
|
- build-type: 'cublas'
|
||||||
cuda-major-version: "12"
|
cuda-major-version: "12"
|
||||||
cuda-minor-version: "0"
|
cuda-minor-version: "0"
|
||||||
@@ -323,6 +326,7 @@ jobs:
|
|||||||
base-image: "ubuntu:22.04"
|
base-image: "ubuntu:22.04"
|
||||||
runs-on: 'arc-runner-set'
|
runs-on: 'arc-runner-set'
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
skip-drivers: 'false'
|
||||||
- build-type: 'cublas'
|
- build-type: 'cublas'
|
||||||
cuda-major-version: "11"
|
cuda-major-version: "11"
|
||||||
cuda-minor-version: "7"
|
cuda-minor-version: "7"
|
||||||
@@ -334,6 +338,7 @@ jobs:
|
|||||||
runs-on: 'arc-runner-set'
|
runs-on: 'arc-runner-set'
|
||||||
base-image: "ubuntu:22.04"
|
base-image: "ubuntu:22.04"
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
skip-drivers: 'false'
|
||||||
- build-type: 'cublas'
|
- build-type: 'cublas'
|
||||||
cuda-major-version: "12"
|
cuda-major-version: "12"
|
||||||
cuda-minor-version: "0"
|
cuda-minor-version: "0"
|
||||||
@@ -344,6 +349,7 @@ jobs:
|
|||||||
image-type: 'core'
|
image-type: 'core'
|
||||||
runs-on: 'arc-runner-set'
|
runs-on: 'arc-runner-set'
|
||||||
base-image: "ubuntu:22.04"
|
base-image: "ubuntu:22.04"
|
||||||
|
skip-drivers: 'false'
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
- build-type: 'vulkan'
|
- build-type: 'vulkan'
|
||||||
platforms: 'linux/amd64'
|
platforms: 'linux/amd64'
|
||||||
@@ -354,4 +360,45 @@ jobs:
|
|||||||
image-type: 'core'
|
image-type: 'core'
|
||||||
runs-on: 'arc-runner-set'
|
runs-on: 'arc-runner-set'
|
||||||
base-image: "ubuntu:22.04"
|
base-image: "ubuntu:22.04"
|
||||||
|
skip-drivers: 'false'
|
||||||
makeflags: "--jobs=4 --output-sync=target"
|
makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
# parallel-builds:
|
||||||
|
# uses: ./.github/workflows/image_build.yml
|
||||||
|
# with:
|
||||||
|
# tag-latest: ${{ matrix.tag-latest }}
|
||||||
|
# tag-suffix: ${{ matrix.tag-suffix }}
|
||||||
|
# ffmpeg: ${{ matrix.ffmpeg }}
|
||||||
|
# image-type: ${{ matrix.image-type }}
|
||||||
|
# build-type: ${{ matrix.build-type }}
|
||||||
|
# cuda-major-version: ${{ matrix.cuda-major-version }}
|
||||||
|
# cuda-minor-version: ${{ matrix.cuda-minor-version }}
|
||||||
|
# platforms: ${{ matrix.platforms }}
|
||||||
|
# runs-on: ${{ matrix.runs-on }}
|
||||||
|
# aio: ${{ matrix.aio }}
|
||||||
|
# base-image: ${{ matrix.base-image }}
|
||||||
|
# grpc-base-image: ${{ matrix.grpc-base-image }}
|
||||||
|
# makeflags: ${{ matrix.makeflags }}
|
||||||
|
# latest-image: ${{ matrix.latest-image }}
|
||||||
|
# latest-image-aio: ${{ matrix.latest-image-aio }}
|
||||||
|
# skip-drivers: ${{ matrix.skip-drivers }}
|
||||||
|
# secrets:
|
||||||
|
# dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
# dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
# quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }}
|
||||||
|
# quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }}
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# include:
|
||||||
|
# - build-type: 'cublas'
|
||||||
|
# cuda-major-version: "12"
|
||||||
|
# cuda-minor-version: "0"
|
||||||
|
# platforms: 'linux/arm64'
|
||||||
|
# tag-latest: 'false'
|
||||||
|
# tag-suffix: '-nvidia-l4t-arm64-core'
|
||||||
|
# latest-image: 'latest-nvidia-l4t-arm64-core'
|
||||||
|
# ffmpeg: 'true'
|
||||||
|
# image-type: 'core'
|
||||||
|
# base-image: "nvcr.io/nvidia/l4t-jetpack:r36.4.0"
|
||||||
|
# runs-on: 'self-hosted'
|
||||||
|
# makeflags: "--jobs=4 --output-sync=target"
|
||||||
|
# skip-drivers: 'true'
|
||||||
|
|||||||
6
.github/workflows/image_build.yml
vendored
6
.github/workflows/image_build.yml
vendored
@@ -49,6 +49,10 @@ on:
|
|||||||
description: 'FFMPEG'
|
description: 'FFMPEG'
|
||||||
default: ''
|
default: ''
|
||||||
type: string
|
type: string
|
||||||
|
skip-drivers:
|
||||||
|
description: 'Skip drivers by default'
|
||||||
|
default: 'false'
|
||||||
|
type: string
|
||||||
image-type:
|
image-type:
|
||||||
description: 'Image type'
|
description: 'Image type'
|
||||||
default: ''
|
default: ''
|
||||||
@@ -234,6 +238,7 @@ jobs:
|
|||||||
GRPC_MAKEFLAGS=--jobs=4 --output-sync=target
|
GRPC_MAKEFLAGS=--jobs=4 --output-sync=target
|
||||||
GRPC_VERSION=v1.65.0
|
GRPC_VERSION=v1.65.0
|
||||||
MAKEFLAGS=${{ inputs.makeflags }}
|
MAKEFLAGS=${{ inputs.makeflags }}
|
||||||
|
SKIP_DRIVERS=${{ inputs.skip-drivers }}
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
@@ -262,6 +267,7 @@ jobs:
|
|||||||
GRPC_MAKEFLAGS=--jobs=4 --output-sync=target
|
GRPC_MAKEFLAGS=--jobs=4 --output-sync=target
|
||||||
GRPC_VERSION=v1.65.0
|
GRPC_VERSION=v1.65.0
|
||||||
MAKEFLAGS=${{ inputs.makeflags }}
|
MAKEFLAGS=${{ inputs.makeflags }}
|
||||||
|
SKIP_DRIVERS=${{ inputs.skip-drivers }}
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
|
|||||||
2
.github/workflows/secscan.yaml
vendored
2
.github/workflows/secscan.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||||
- name: Run Gosec Security Scanner
|
- name: Run Gosec Security Scanner
|
||||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||||
uses: securego/gosec@v2.21.4
|
uses: securego/gosec@v2.22.0
|
||||||
with:
|
with:
|
||||||
# we let the report trigger content trigger a failure using the GitHub Security features.
|
# we let the report trigger content trigger a failure using the GitHub Security features.
|
||||||
args: '-no-fail -fmt sarif -out results.sarif ./...'
|
args: '-no-fail -fmt sarif -out results.sarif ./...'
|
||||||
|
|||||||
@@ -115,12 +115,13 @@ FROM requirements-${IMAGE_TYPE} AS requirements-drivers
|
|||||||
ARG BUILD_TYPE
|
ARG BUILD_TYPE
|
||||||
ARG CUDA_MAJOR_VERSION=12
|
ARG CUDA_MAJOR_VERSION=12
|
||||||
ARG CUDA_MINOR_VERSION=0
|
ARG CUDA_MINOR_VERSION=0
|
||||||
|
ARG SKIP_DRIVERS=false
|
||||||
|
|
||||||
ENV BUILD_TYPE=${BUILD_TYPE}
|
ENV BUILD_TYPE=${BUILD_TYPE}
|
||||||
|
|
||||||
# Vulkan requirements
|
# Vulkan requirements
|
||||||
RUN <<EOT bash
|
RUN <<EOT bash
|
||||||
if [ "${BUILD_TYPE}" = "vulkan" ]; then
|
if [ "${BUILD_TYPE}" = "vulkan" ] && [ "${SKIP_DRIVERS}" = "false" ]; then
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
software-properties-common pciutils wget gpg-agent && \
|
software-properties-common pciutils wget gpg-agent && \
|
||||||
@@ -136,7 +137,7 @@ EOT
|
|||||||
|
|
||||||
# CuBLAS requirements
|
# CuBLAS requirements
|
||||||
RUN <<EOT bash
|
RUN <<EOT bash
|
||||||
if [ "${BUILD_TYPE}" = "cublas" ]; then
|
if [ "${BUILD_TYPE}" = "cublas" ] && [ "${SKIP_DRIVERS}" = "false" ]; then
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
software-properties-common pciutils
|
software-properties-common pciutils
|
||||||
@@ -162,7 +163,7 @@ RUN <<EOT bash
|
|||||||
EOT
|
EOT
|
||||||
|
|
||||||
# If we are building with clblas support, we need the libraries for the builds
|
# If we are building with clblas support, we need the libraries for the builds
|
||||||
RUN if [ "${BUILD_TYPE}" = "clblas" ]; then \
|
RUN if [ "${BUILD_TYPE}" = "clblas" ] && [ "${SKIP_DRIVERS}" = "false" ]; then \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
libclblast-dev && \
|
libclblast-dev && \
|
||||||
@@ -170,7 +171,7 @@ RUN if [ "${BUILD_TYPE}" = "clblas" ]; then \
|
|||||||
rm -rf /var/lib/apt/lists/* \
|
rm -rf /var/lib/apt/lists/* \
|
||||||
; fi
|
; fi
|
||||||
|
|
||||||
RUN if [ "${BUILD_TYPE}" = "hipblas" ]; then \
|
RUN if [ "${BUILD_TYPE}" = "hipblas" ] && [ "${SKIP_DRIVERS}" = "false" ]; then \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
hipblas-dev \
|
hipblas-dev \
|
||||||
|
|||||||
14
Makefile
14
Makefile
@@ -8,7 +8,7 @@ DETECT_LIBS?=true
|
|||||||
# llama.cpp versions
|
# llama.cpp versions
|
||||||
GOLLAMA_REPO?=https://github.com/go-skynet/go-llama.cpp
|
GOLLAMA_REPO?=https://github.com/go-skynet/go-llama.cpp
|
||||||
GOLLAMA_VERSION?=2b57a8ae43e4699d3dc5d1496a1ccd42922993be
|
GOLLAMA_VERSION?=2b57a8ae43e4699d3dc5d1496a1ccd42922993be
|
||||||
CPPLLAMA_VERSION?=cc98896db858df7aa40d0e16a505883ef196a482
|
CPPLLAMA_VERSION?=504af20ee4eae72080a56d59d744f6774f7901ce
|
||||||
|
|
||||||
# whisper.cpp version
|
# whisper.cpp version
|
||||||
WHISPER_REPO?=https://github.com/ggerganov/whisper.cpp
|
WHISPER_REPO?=https://github.com/ggerganov/whisper.cpp
|
||||||
@@ -32,7 +32,7 @@ BARKCPP_VERSION?=v1.0.0
|
|||||||
|
|
||||||
# stablediffusion.cpp (ggml)
|
# stablediffusion.cpp (ggml)
|
||||||
STABLEDIFFUSION_GGML_REPO?=https://github.com/leejet/stable-diffusion.cpp
|
STABLEDIFFUSION_GGML_REPO?=https://github.com/leejet/stable-diffusion.cpp
|
||||||
STABLEDIFFUSION_GGML_VERSION?=4570715727f35e5a07a76796d823824c8f42206c
|
STABLEDIFFUSION_GGML_VERSION?=dcf91f9e0f2cbf9da472ee2a556751ed4bab2d2a
|
||||||
|
|
||||||
ONNX_VERSION?=1.20.0
|
ONNX_VERSION?=1.20.0
|
||||||
ONNX_ARCH?=x64
|
ONNX_ARCH?=x64
|
||||||
@@ -302,14 +302,8 @@ sources/stablediffusion-ggml.cpp:
|
|||||||
git checkout $(STABLEDIFFUSION_GGML_VERSION) && \
|
git checkout $(STABLEDIFFUSION_GGML_VERSION) && \
|
||||||
git submodule update --init --recursive --depth 1 --single-branch
|
git submodule update --init --recursive --depth 1 --single-branch
|
||||||
|
|
||||||
sources/stablediffusion-ggml.cpp/build/libstable-diffusion.a: sources/stablediffusion-ggml.cpp
|
backend/go/image/stablediffusion-ggml/libsd.a: sources/stablediffusion-ggml.cpp
|
||||||
cd sources/stablediffusion-ggml.cpp && \
|
$(MAKE) -C backend/go/image/stablediffusion-ggml build/libstable-diffusion.a
|
||||||
mkdir -p build && \
|
|
||||||
cd build && \
|
|
||||||
cmake $(CMAKE_ARGS) .. && \
|
|
||||||
cmake --build . --config Release
|
|
||||||
|
|
||||||
backend/go/image/stablediffusion-ggml/libsd.a: sources/stablediffusion-ggml.cpp/build/libstable-diffusion.a
|
|
||||||
$(MAKE) -C backend/go/image/stablediffusion-ggml libsd.a
|
$(MAKE) -C backend/go/image/stablediffusion-ggml libsd.a
|
||||||
|
|
||||||
backend-assets/grpc/stablediffusion-ggml: backend/go/image/stablediffusion-ggml/libsd.a backend-assets/grpc
|
backend-assets/grpc/stablediffusion-ggml: backend/go/image/stablediffusion-ggml/libsd.a backend-assets/grpc
|
||||||
|
|||||||
@@ -126,10 +126,10 @@ If you want to help and contribute, issues up for grabs: https://github.com/mudl
|
|||||||
|
|
||||||
## 🚀 [Features](https://localai.io/features/)
|
## 🚀 [Features](https://localai.io/features/)
|
||||||
|
|
||||||
- 📖 [Text generation with GPTs](https://localai.io/features/text-generation/) (`llama.cpp`, `gpt4all.cpp`, ... [:book: and more](https://localai.io/model-compatibility/index.html#model-compatibility-table))
|
- 📖 [Text generation with GPTs](https://localai.io/features/text-generation/) (`llama.cpp`, `transformers`, `vllm` ... [:book: and more](https://localai.io/model-compatibility/index.html#model-compatibility-table))
|
||||||
- 🗣 [Text to Audio](https://localai.io/features/text-to-audio/)
|
- 🗣 [Text to Audio](https://localai.io/features/text-to-audio/)
|
||||||
- 🔈 [Audio to Text](https://localai.io/features/audio-to-text/) (Audio transcription with `whisper.cpp`)
|
- 🔈 [Audio to Text](https://localai.io/features/audio-to-text/) (Audio transcription with `whisper.cpp`)
|
||||||
- 🎨 [Image generation with stable diffusion](https://localai.io/features/image-generation)
|
- 🎨 [Image generation](https://localai.io/features/image-generation)
|
||||||
- 🔥 [OpenAI-alike tools API](https://localai.io/features/openai-functions/)
|
- 🔥 [OpenAI-alike tools API](https://localai.io/features/openai-functions/)
|
||||||
- 🧠 [Embeddings generation for vector databases](https://localai.io/features/embeddings/)
|
- 🧠 [Embeddings generation for vector databases](https://localai.io/features/embeddings/)
|
||||||
- ✍️ [Constrained grammars](https://localai.io/features/constrained_grammars/)
|
- ✍️ [Constrained grammars](https://localai.io/features/constrained_grammars/)
|
||||||
@@ -137,6 +137,7 @@ If you want to help and contribute, issues up for grabs: https://github.com/mudl
|
|||||||
- 🥽 [Vision API](https://localai.io/features/gpt-vision/)
|
- 🥽 [Vision API](https://localai.io/features/gpt-vision/)
|
||||||
- 📈 [Reranker API](https://localai.io/features/reranker/)
|
- 📈 [Reranker API](https://localai.io/features/reranker/)
|
||||||
- 🆕🖧 [P2P Inferencing](https://localai.io/features/distribute/)
|
- 🆕🖧 [P2P Inferencing](https://localai.io/features/distribute/)
|
||||||
|
- 🔊 Voice activity detection (Silero-VAD support)
|
||||||
- 🌍 Integrated WebUI!
|
- 🌍 Integrated WebUI!
|
||||||
|
|
||||||
## 💻 Usage
|
## 💻 Usage
|
||||||
@@ -159,6 +160,7 @@ Model galleries
|
|||||||
Other:
|
Other:
|
||||||
- Helm chart https://github.com/go-skynet/helm-charts
|
- Helm chart https://github.com/go-skynet/helm-charts
|
||||||
- VSCode extension https://github.com/badgooooor/localai-vscode-plugin
|
- VSCode extension https://github.com/badgooooor/localai-vscode-plugin
|
||||||
|
- Langchain: https://python.langchain.com/docs/integrations/providers/localai/
|
||||||
- Terminal utility https://github.com/djcopley/ShellOracle
|
- Terminal utility https://github.com/djcopley/ShellOracle
|
||||||
- Local Smart assistant https://github.com/mudler/LocalAGI
|
- Local Smart assistant https://github.com/mudler/LocalAGI
|
||||||
- Home Assistant https://github.com/sammcj/homeassistant-localai / https://github.com/drndos/hass-openai-custom-conversation / https://github.com/valentinfrlch/ha-gpt4vision
|
- Home Assistant https://github.com/sammcj/homeassistant-localai / https://github.com/drndos/hass-openai-custom-conversation / https://github.com/valentinfrlch/ha-gpt4vision
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ message Reply {
|
|||||||
bytes message = 1;
|
bytes message = 1;
|
||||||
int32 tokens = 2;
|
int32 tokens = 2;
|
||||||
int32 prompt_tokens = 3;
|
int32 prompt_tokens = 3;
|
||||||
|
bytes audio = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ModelOptions {
|
message ModelOptions {
|
||||||
@@ -242,6 +243,9 @@ message ModelOptions {
|
|||||||
repeated float LoraScales = 61;
|
repeated float LoraScales = 61;
|
||||||
|
|
||||||
repeated string Options = 62;
|
repeated string Options = 62;
|
||||||
|
|
||||||
|
string CacheTypeKey = 63;
|
||||||
|
string CacheTypeValue = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Result {
|
message Result {
|
||||||
|
|||||||
@@ -428,6 +428,7 @@ struct llama_server_context
|
|||||||
{
|
{
|
||||||
llama_model *model = nullptr;
|
llama_model *model = nullptr;
|
||||||
llama_context *ctx = nullptr;
|
llama_context *ctx = nullptr;
|
||||||
|
const llama_vocab * vocab = nullptr;
|
||||||
|
|
||||||
clip_ctx *clp_ctx = nullptr;
|
clip_ctx *clp_ctx = nullptr;
|
||||||
|
|
||||||
@@ -439,6 +440,7 @@ struct llama_server_context
|
|||||||
bool clean_kv_cache = true;
|
bool clean_kv_cache = true;
|
||||||
bool all_slots_are_idle = false;
|
bool all_slots_are_idle = false;
|
||||||
bool add_bos_token = true;
|
bool add_bos_token = true;
|
||||||
|
bool has_eos_token = true;
|
||||||
|
|
||||||
int32_t n_ctx; // total context for all clients / slots
|
int32_t n_ctx; // total context for all clients / slots
|
||||||
|
|
||||||
@@ -492,8 +494,8 @@ struct llama_server_context
|
|||||||
}
|
}
|
||||||
|
|
||||||
common_init_result common_init = common_init_from_params(params);
|
common_init_result common_init = common_init_from_params(params);
|
||||||
model = common_init.model;
|
model = common_init.model.release();
|
||||||
ctx = common_init.context;
|
ctx = common_init.context.release();
|
||||||
if (model == nullptr)
|
if (model == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERR("unable to load model: %s", params.model.c_str());
|
LOG_ERR("unable to load model: %s", params.model.c_str());
|
||||||
@@ -502,7 +504,7 @@ struct llama_server_context
|
|||||||
|
|
||||||
if (multimodal) {
|
if (multimodal) {
|
||||||
const int n_embd_clip = clip_n_mmproj_embd(clp_ctx);
|
const int n_embd_clip = clip_n_mmproj_embd(clp_ctx);
|
||||||
const int n_embd_llm = llama_n_embd(model);
|
const int n_embd_llm = llama_model_n_embd(model);
|
||||||
if (n_embd_clip != n_embd_llm) {
|
if (n_embd_clip != n_embd_llm) {
|
||||||
LOG("%s: embedding dim of the multimodal projector (%d) is not equal to that of LLaMA (%d). Make sure that you use the correct mmproj file.\n", __func__, n_embd_clip, n_embd_llm);
|
LOG("%s: embedding dim of the multimodal projector (%d) is not equal to that of LLaMA (%d). Make sure that you use the correct mmproj file.\n", __func__, n_embd_clip, n_embd_llm);
|
||||||
llama_free(ctx);
|
llama_free(ctx);
|
||||||
@@ -511,23 +513,15 @@ struct llama_server_context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vocab = llama_model_get_vocab(model);
|
||||||
n_ctx = llama_n_ctx(ctx);
|
n_ctx = llama_n_ctx(ctx);
|
||||||
|
|
||||||
add_bos_token = llama_add_bos_token(model);
|
add_bos_token = llama_vocab_get_add_bos(vocab);
|
||||||
|
has_eos_token = llama_vocab_eos(vocab) != LLAMA_TOKEN_NULL;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_model_chat_template(server_params & sparams) {
|
|
||||||
llama_chat_message chat[] = {{"user", "test"}};
|
|
||||||
std::vector<char> buf(1);
|
|
||||||
int res = llama_chat_apply_template(model, nullptr, chat, 1, true, buf.data(), buf.size());
|
|
||||||
if (res < 0) {
|
|
||||||
LOG_ERR("The chat template comes with this model is not yet supported, falling back to chatml. This may cause the model to output suboptimal responses", __func__);
|
|
||||||
sparams.chat_template = "<|im_start|>"; // llama_chat_apply_template only checks if <|im_start|> exist in the template
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llama_client_slot* get_active_slot() {
|
llama_client_slot* get_active_slot() {
|
||||||
for (llama_client_slot& slot : slots) {
|
for (llama_client_slot& slot : slots) {
|
||||||
// Check if the slot is currently processing
|
// Check if the slot is currently processing
|
||||||
@@ -681,7 +675,6 @@ struct llama_server_context
|
|||||||
slot->sparams.mirostat = json_value(data, "mirostat", default_sparams.mirostat);
|
slot->sparams.mirostat = json_value(data, "mirostat", default_sparams.mirostat);
|
||||||
slot->sparams.mirostat_tau = json_value(data, "mirostat_tau", default_sparams.mirostat_tau);
|
slot->sparams.mirostat_tau = json_value(data, "mirostat_tau", default_sparams.mirostat_tau);
|
||||||
slot->sparams.mirostat_eta = json_value(data, "mirostat_eta", default_sparams.mirostat_eta);
|
slot->sparams.mirostat_eta = json_value(data, "mirostat_eta", default_sparams.mirostat_eta);
|
||||||
slot->sparams.penalize_nl = json_value(data, "penalize_nl", default_sparams.penalize_nl);
|
|
||||||
slot->params.n_keep = json_value(data, "n_keep", slot->params.n_keep);
|
slot->params.n_keep = json_value(data, "n_keep", slot->params.n_keep);
|
||||||
slot->sparams.seed = json_value(data, "seed", default_sparams.seed);
|
slot->sparams.seed = json_value(data, "seed", default_sparams.seed);
|
||||||
slot->sparams.grammar = json_value(data, "grammar", default_sparams.grammar);
|
slot->sparams.grammar = json_value(data, "grammar", default_sparams.grammar);
|
||||||
@@ -726,8 +719,8 @@ struct llama_server_context
|
|||||||
slot->prompt = "";
|
slot->prompt = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json_value(data, "ignore_eos", false)) {
|
if (json_value(data, "ignore_eos", false) && has_eos_token) {
|
||||||
slot->sparams.logit_bias.push_back({llama_token_eos(model), -INFINITY});
|
slot->sparams.logit_bias.push_back({llama_vocab_eos(vocab), -INFINITY});
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
slot->sparams.penalty_prompt_tokens.clear();
|
slot->sparams.penalty_prompt_tokens.clear();
|
||||||
@@ -766,13 +759,13 @@ struct llama_server_context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
slot->sparams.logit_bias.clear();
|
slot->sparams.logit_bias.clear();
|
||||||
|
|
||||||
const auto &logit_bias = data.find("logit_bias");
|
const auto &logit_bias = data.find("logit_bias");
|
||||||
if (logit_bias != data.end() && logit_bias->is_array())
|
if (logit_bias != data.end() && logit_bias->is_array())
|
||||||
{
|
{
|
||||||
const int n_vocab = llama_n_vocab(model);
|
const llama_vocab * vocab = llama_model_get_vocab(model);
|
||||||
|
const int n_vocab = llama_vocab_n_tokens(vocab);
|
||||||
for (const auto &el : *logit_bias)
|
for (const auto &el : *logit_bias)
|
||||||
{
|
{
|
||||||
if (el.is_array() && el.size() == 2)
|
if (el.is_array() && el.size() == 2)
|
||||||
@@ -801,7 +794,7 @@ struct llama_server_context
|
|||||||
}
|
}
|
||||||
else if (el[0].is_string())
|
else if (el[0].is_string())
|
||||||
{
|
{
|
||||||
auto toks = common_tokenize(model, el[0].get<std::string>(), false);
|
auto toks = common_tokenize(vocab, el[0].get<std::string>(), false);
|
||||||
for (auto tok : toks)
|
for (auto tok : toks)
|
||||||
{
|
{
|
||||||
slot->sparams.logit_bias.push_back({tok, bias});
|
slot->sparams.logit_bias.push_back({tok, bias});
|
||||||
@@ -1131,7 +1124,7 @@ struct llama_server_context
|
|||||||
slot.has_next_token = false;
|
slot.has_next_token = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.tok == llama_token_eos(model))
|
if (result.tok == llama_vocab_eos(vocab) || llama_vocab_is_eog(vocab, result.tok))
|
||||||
{
|
{
|
||||||
slot.stopped_eos = true;
|
slot.stopped_eos = true;
|
||||||
slot.has_next_token = false;
|
slot.has_next_token = false;
|
||||||
@@ -1213,13 +1206,12 @@ struct llama_server_context
|
|||||||
{"mirostat", slot.sparams.mirostat},
|
{"mirostat", slot.sparams.mirostat},
|
||||||
{"mirostat_tau", slot.sparams.mirostat_tau},
|
{"mirostat_tau", slot.sparams.mirostat_tau},
|
||||||
{"mirostat_eta", slot.sparams.mirostat_eta},
|
{"mirostat_eta", slot.sparams.mirostat_eta},
|
||||||
{"penalize_nl", slot.sparams.penalize_nl},
|
|
||||||
{"stop", slot.params.antiprompt},
|
{"stop", slot.params.antiprompt},
|
||||||
{"n_predict", slot.params.n_predict},
|
{"n_predict", slot.params.n_predict},
|
||||||
{"n_keep", params.n_keep},
|
{"n_keep", params.n_keep},
|
||||||
{"ignore_eos", slot.sparams.ignore_eos},
|
{"ignore_eos", slot.sparams.ignore_eos},
|
||||||
{"stream", slot.params.stream},
|
{"stream", slot.params.stream},
|
||||||
// {"logit_bias", slot.sparams.logit_bias},
|
// {"logit_bias", slot.sparams.logit_bias},
|
||||||
{"n_probs", slot.sparams.n_probs},
|
{"n_probs", slot.sparams.n_probs},
|
||||||
{"min_keep", slot.sparams.min_keep},
|
{"min_keep", slot.sparams.min_keep},
|
||||||
{"grammar", slot.sparams.grammar},
|
{"grammar", slot.sparams.grammar},
|
||||||
@@ -1327,7 +1319,7 @@ struct llama_server_context
|
|||||||
res.error = false;
|
res.error = false;
|
||||||
res.stop = true;
|
res.stop = true;
|
||||||
|
|
||||||
const int n_embd = llama_n_embd(model);
|
const int n_embd = llama_model_n_embd(model);
|
||||||
if (!params.embedding)
|
if (!params.embedding)
|
||||||
{
|
{
|
||||||
LOG_WARNING("embedding disabled", {
|
LOG_WARNING("embedding disabled", {
|
||||||
@@ -1426,7 +1418,7 @@ struct llama_server_context
|
|||||||
n_eval = n_batch;
|
n_eval = n_batch;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int n_embd = llama_n_embd(model);
|
const int n_embd = llama_model_n_embd(model);
|
||||||
float * embd = img.image_embedding + i * n_embd;
|
float * embd = img.image_embedding + i * n_embd;
|
||||||
llava_embd_batch llava_batch = llava_embd_batch(embd, n_eval, slot.n_past, 0);
|
llava_embd_batch llava_batch = llava_embd_batch(embd, n_eval, slot.n_past, 0);
|
||||||
if (llama_decode(ctx, llava_batch.batch))
|
if (llama_decode(ctx, llava_batch.batch))
|
||||||
@@ -1707,11 +1699,11 @@ struct llama_server_context
|
|||||||
suffix_tokens.erase(suffix_tokens.begin());
|
suffix_tokens.erase(suffix_tokens.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix_tokens.insert(prefix_tokens.begin(), llama_token_prefix(model));
|
prefix_tokens.insert(prefix_tokens.begin(), llama_vocab_fim_pre(vocab));
|
||||||
prefix_tokens.insert(prefix_tokens.begin(), llama_token_bos(model)); // always add BOS
|
prefix_tokens.insert(prefix_tokens.begin(), llama_vocab_bos(vocab)); // always add BOS
|
||||||
prefix_tokens.insert(prefix_tokens.end(), llama_token_suffix(model));
|
prefix_tokens.insert(prefix_tokens.end(), llama_vocab_fim_suf(vocab));
|
||||||
prefix_tokens.insert(prefix_tokens.end(), suffix_tokens.begin(), suffix_tokens.end());
|
prefix_tokens.insert(prefix_tokens.end(), suffix_tokens.begin(), suffix_tokens.end());
|
||||||
prefix_tokens.push_back(llama_token_middle(model));
|
prefix_tokens.push_back(llama_vocab_fim_mid(vocab));
|
||||||
prompt_tokens = prefix_tokens;
|
prompt_tokens = prefix_tokens;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2112,7 +2104,6 @@ json parse_options(bool streaming, const backend::PredictOptions* predict, llama
|
|||||||
// slot->sparams.mirostat = json_value(data, "mirostat", default_sparams.mirostat);
|
// slot->sparams.mirostat = json_value(data, "mirostat", default_sparams.mirostat);
|
||||||
// slot->sparams.mirostat_tau = json_value(data, "mirostat_tau", default_sparams.mirostat_tau);
|
// slot->sparams.mirostat_tau = json_value(data, "mirostat_tau", default_sparams.mirostat_tau);
|
||||||
// slot->sparams.mirostat_eta = json_value(data, "mirostat_eta", default_sparams.mirostat_eta);
|
// slot->sparams.mirostat_eta = json_value(data, "mirostat_eta", default_sparams.mirostat_eta);
|
||||||
// slot->sparams.penalize_nl = json_value(data, "penalize_nl", default_sparams.penalize_nl);
|
|
||||||
// slot->params.n_keep = json_value(data, "n_keep", slot->params.n_keep);
|
// slot->params.n_keep = json_value(data, "n_keep", slot->params.n_keep);
|
||||||
// slot->params.seed = json_value(data, "seed", default_params.seed);
|
// slot->params.seed = json_value(data, "seed", default_params.seed);
|
||||||
// slot->sparams.grammar = json_value(data, "grammar", default_sparams.grammar);
|
// slot->sparams.grammar = json_value(data, "grammar", default_sparams.grammar);
|
||||||
@@ -2135,7 +2126,6 @@ json parse_options(bool streaming, const backend::PredictOptions* predict, llama
|
|||||||
data["mirostat"] = predict->mirostat();
|
data["mirostat"] = predict->mirostat();
|
||||||
data["mirostat_tau"] = predict->mirostattau();
|
data["mirostat_tau"] = predict->mirostattau();
|
||||||
data["mirostat_eta"] = predict->mirostateta();
|
data["mirostat_eta"] = predict->mirostateta();
|
||||||
data["penalize_nl"] = predict->penalizenl();
|
|
||||||
data["n_keep"] = predict->nkeep();
|
data["n_keep"] = predict->nkeep();
|
||||||
data["seed"] = predict->seed();
|
data["seed"] = predict->seed();
|
||||||
data["grammar"] = predict->grammar();
|
data["grammar"] = predict->grammar();
|
||||||
@@ -2181,7 +2171,6 @@ json parse_options(bool streaming, const backend::PredictOptions* predict, llama
|
|||||||
// llama.params.sparams.mirostat = predict->mirostat();
|
// llama.params.sparams.mirostat = predict->mirostat();
|
||||||
// llama.params.sparams.mirostat_tau = predict->mirostattau();
|
// llama.params.sparams.mirostat_tau = predict->mirostattau();
|
||||||
// llama.params.sparams.mirostat_eta = predict->mirostateta();
|
// llama.params.sparams.mirostat_eta = predict->mirostateta();
|
||||||
// llama.params.sparams.penalize_nl = predict->penalizenl();
|
|
||||||
// llama.params.n_keep = predict->nkeep();
|
// llama.params.n_keep = predict->nkeep();
|
||||||
// llama.params.seed = predict->seed();
|
// llama.params.seed = predict->seed();
|
||||||
// llama.params.sparams.grammar = predict->grammar();
|
// llama.params.sparams.grammar = predict->grammar();
|
||||||
@@ -2228,6 +2217,35 @@ json parse_options(bool streaming, const backend::PredictOptions* predict, llama
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const std::vector<ggml_type> kv_cache_types = {
|
||||||
|
GGML_TYPE_F32,
|
||||||
|
GGML_TYPE_F16,
|
||||||
|
GGML_TYPE_BF16,
|
||||||
|
GGML_TYPE_Q8_0,
|
||||||
|
GGML_TYPE_Q4_0,
|
||||||
|
GGML_TYPE_Q4_1,
|
||||||
|
GGML_TYPE_IQ4_NL,
|
||||||
|
GGML_TYPE_Q5_0,
|
||||||
|
GGML_TYPE_Q5_1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ggml_type kv_cache_type_from_str(const std::string & s) {
|
||||||
|
for (const auto & type : kv_cache_types) {
|
||||||
|
if (ggml_type_name(type) == s) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unsupported cache type: " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_all_kv_cache_types() {
|
||||||
|
std::ostringstream msg;
|
||||||
|
for (const auto & type : kv_cache_types) {
|
||||||
|
msg << ggml_type_name(type) << (&type == &kv_cache_types.back() ? "" : ", ");
|
||||||
|
}
|
||||||
|
return msg.str();
|
||||||
|
}
|
||||||
|
|
||||||
static void params_parse(const backend::ModelOptions* request,
|
static void params_parse(const backend::ModelOptions* request,
|
||||||
common_params & params) {
|
common_params & params) {
|
||||||
|
|
||||||
@@ -2241,6 +2259,12 @@ static void params_parse(const backend::ModelOptions* request,
|
|||||||
}
|
}
|
||||||
// params.model_alias ??
|
// params.model_alias ??
|
||||||
params.model_alias = request->modelfile();
|
params.model_alias = request->modelfile();
|
||||||
|
if (!request->cachetypekey().empty()) {
|
||||||
|
params.cache_type_k = kv_cache_type_from_str(request->cachetypekey());
|
||||||
|
}
|
||||||
|
if (!request->cachetypevalue().empty()) {
|
||||||
|
params.cache_type_v = kv_cache_type_from_str(request->cachetypevalue());
|
||||||
|
}
|
||||||
params.n_ctx = request->contextsize();
|
params.n_ctx = request->contextsize();
|
||||||
//params.memory_f16 = request->f16memory();
|
//params.memory_f16 = request->f16memory();
|
||||||
params.cpuparams.n_threads = request->threads();
|
params.cpuparams.n_threads = request->threads();
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
diff --git a/examples/llava/clip.cpp b/examples/llava/clip.cpp
|
diff --git a/examples/llava/clip.cpp b/examples/llava/clip.cpp
|
||||||
index 342042ff..224db9b5 100644
|
index 3cd0d2fa..6c5e811a 100644
|
||||||
--- a/examples/llava/clip.cpp
|
--- a/examples/llava/clip.cpp
|
||||||
+++ b/examples/llava/clip.cpp
|
+++ b/examples/llava/clip.cpp
|
||||||
@@ -2419,7 +2419,7 @@ bool clip_image_batch_encode(clip_ctx * ctx, const int n_threads, const clip_ima
|
@@ -2608,7 +2608,7 @@ bool clip_image_batch_encode(clip_ctx * ctx, const int n_threads, const clip_ima
|
||||||
struct ggml_tensor * patches = ggml_graph_get_tensor(gf, "patches");
|
struct ggml_tensor * patches = ggml_graph_get_tensor(gf, "patches");
|
||||||
int* patches_data = (int*)malloc(ggml_nbytes(patches));
|
int* patches_data = (int*)malloc(ggml_nbytes(patches));
|
||||||
for (int i = 0; i < num_patches; i++) {
|
for (int i = 0; i < num_patches; i++) {
|
||||||
- patches_data[i] = i + 1;
|
- patches_data[i] = i + 1;
|
||||||
+ patches_data[i] = i;
|
+ patches_data[i] = i;
|
||||||
}
|
}
|
||||||
ggml_backend_tensor_set(patches, patches_data, 0, ggml_nbytes(patches));
|
ggml_backend_tensor_set(patches, patches_data, 0, ggml_nbytes(patches));
|
||||||
free(patches_data);
|
free(patches_data);
|
||||||
@@ -2,20 +2,95 @@ INCLUDE_PATH := $(abspath ./)
|
|||||||
LIBRARY_PATH := $(abspath ./)
|
LIBRARY_PATH := $(abspath ./)
|
||||||
|
|
||||||
AR?=ar
|
AR?=ar
|
||||||
|
CMAKE_ARGS?=
|
||||||
BUILD_TYPE?=
|
BUILD_TYPE?=
|
||||||
|
ONEAPI_VARS?=/opt/intel/oneapi/setvars.sh
|
||||||
# keep standard at C11 and C++11
|
# keep standard at C11 and C++11
|
||||||
CXXFLAGS = -I. -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp/thirdparty -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp/ggml/include -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp -O3 -DNDEBUG -std=c++17 -fPIC
|
CXXFLAGS = -I. -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp/thirdparty -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp/ggml/include -I$(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp -O3 -DNDEBUG -std=c++17 -fPIC
|
||||||
|
|
||||||
|
# Disable Shared libs as we are linking on static gRPC and we can't mix shared and static
|
||||||
|
CMAKE_ARGS+=-DBUILD_SHARED_LIBS=OFF
|
||||||
|
|
||||||
|
# If build type is cublas, then we set -DGGML_CUDA=ON to CMAKE_ARGS automatically
|
||||||
|
ifeq ($(BUILD_TYPE),cublas)
|
||||||
|
CMAKE_ARGS+=-DGGML_CUDA=ON
|
||||||
|
# If build type is openblas then we set -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS
|
||||||
|
# to CMAKE_ARGS automatically
|
||||||
|
else ifeq ($(BUILD_TYPE),openblas)
|
||||||
|
CMAKE_ARGS+=-DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS
|
||||||
|
# If build type is clblas (openCL) we set -DGGML_CLBLAST=ON -DCLBlast_DIR=/some/path
|
||||||
|
else ifeq ($(BUILD_TYPE),clblas)
|
||||||
|
CMAKE_ARGS+=-DGGML_CLBLAST=ON -DCLBlast_DIR=/some/path
|
||||||
|
# If it's hipblas we do have also to set CC=/opt/rocm/llvm/bin/clang CXX=/opt/rocm/llvm/bin/clang++
|
||||||
|
else ifeq ($(BUILD_TYPE),hipblas)
|
||||||
|
CMAKE_ARGS+=-DGGML_HIP=ON
|
||||||
|
# If it's OSX, DO NOT embed the metal library - -DGGML_METAL_EMBED_LIBRARY=ON requires further investigation
|
||||||
|
# But if it's OSX without metal, disable it here
|
||||||
|
else ifeq ($(OS),Darwin)
|
||||||
|
ifneq ($(BUILD_TYPE),metal)
|
||||||
|
CMAKE_ARGS+=-DGGML_METAL=OFF
|
||||||
|
else
|
||||||
|
CMAKE_ARGS+=-DGGML_METAL=ON
|
||||||
|
CMAKE_ARGS+=-DGGML_METAL_EMBED_LIBRARY=ON
|
||||||
|
TARGET+=--target ggml-metal
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ifeq ($(BUILD_TYPE),sycl_f16)
|
||||||
|
# CMAKE_ARGS+=-DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL_F16=ON -DSD_SYCL=ON -DGGML_SYCL_F16=ON
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifeq ($(BUILD_TYPE),sycl_f32)
|
||||||
|
# CMAKE_ARGS+=-DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DSD_SYCL=ON
|
||||||
|
# endif
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
CXXFLAGS += -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function
|
CXXFLAGS += -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function
|
||||||
|
|
||||||
|
# Find all .a archives in ARCHIVE_DIR
|
||||||
|
# (ggml can have different backends cpu, cuda, etc., each backend generates a .a archive)
|
||||||
|
GGML_ARCHIVE_DIR := build/ggml/src/
|
||||||
|
ALL_ARCHIVES := $(shell find $(GGML_ARCHIVE_DIR) -type f -name '*.a')
|
||||||
|
|
||||||
|
# Name of the single merged library
|
||||||
|
COMBINED_LIB := libggmlall.a
|
||||||
|
|
||||||
|
# Rule to merge all the .a files into one
|
||||||
|
$(COMBINED_LIB): $(ALL_ARCHIVES)
|
||||||
|
@echo "Merging all .a into $(COMBINED_LIB)"
|
||||||
|
rm -f $@
|
||||||
|
mkdir -p merge-tmp
|
||||||
|
for a in $(ALL_ARCHIVES); do \
|
||||||
|
( cd merge-tmp && ar x ../$$a ); \
|
||||||
|
done
|
||||||
|
( cd merge-tmp && ar rcs ../$@ *.o )
|
||||||
|
# Ensure we have a proper index
|
||||||
|
ranlib $@
|
||||||
|
# Clean up
|
||||||
|
rm -rf merge-tmp
|
||||||
|
|
||||||
|
build/libstable-diffusion.a:
|
||||||
|
@echo "Building SD with $(BUILD_TYPE) build type and $(CMAKE_ARGS)"
|
||||||
|
ifneq (,$(findstring sycl,$(BUILD_TYPE)))
|
||||||
|
+bash -c "source $(ONEAPI_VARS); \
|
||||||
|
mkdir -p build && \
|
||||||
|
cd build && \
|
||||||
|
cmake $(CMAKE_ARGS) ../../../../../sources/stablediffusion-ggml.cpp && \
|
||||||
|
cmake --build . --config Release"
|
||||||
|
else
|
||||||
|
mkdir -p build && \
|
||||||
|
cd build && \
|
||||||
|
cmake $(CMAKE_ARGS) ../../../../../sources/stablediffusion-ggml.cpp && \
|
||||||
|
cmake --build . --config Release
|
||||||
|
endif
|
||||||
|
$(MAKE) $(COMBINED_LIB)
|
||||||
|
|
||||||
gosd.o:
|
gosd.o:
|
||||||
$(CXX) $(CXXFLAGS) gosd.cpp -o gosd.o -c
|
$(CXX) $(CXXFLAGS) gosd.cpp -o gosd.o -c
|
||||||
|
|
||||||
libsd.a: gosd.o
|
libsd.a: gosd.o
|
||||||
cp $(INCLUDE_PATH)/../../../../sources/stablediffusion-ggml.cpp/build/libstable-diffusion.a ./libsd.a
|
cp $(INCLUDE_PATH)/build/libstable-diffusion.a ./libsd.a
|
||||||
$(AR) rcs libsd.a gosd.o
|
$(AR) rcs libsd.a gosd.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f gosd.o libsd.a
|
rm -rf gosd.o libsd.a build $(COMBINED_LIB)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// #cgo CXXFLAGS: -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/thirdparty -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/ggml/include
|
// #cgo CXXFLAGS: -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/thirdparty -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp -I${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/ggml/include
|
||||||
// #cgo LDFLAGS: -L${SRCDIR}/ -L${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/build/ggml/src/ggml-cpu -L${SRCDIR}/../../../../sources/stablediffusion-ggml.cpp/build/ggml/src -lsd -lstdc++ -lm -lggml -lggml-base -lggml-cpu -lgomp
|
// #cgo LDFLAGS: -L${SRCDIR}/ -lsd -lstdc++ -lm -lggmlall -lgomp
|
||||||
// #include <gosd.h>
|
// #include <gosd.h>
|
||||||
// #include <stdlib.h>
|
// #include <stdlib.h>
|
||||||
import "C"
|
import "C"
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ func (vad *VAD) Load(opts *pb.ModelOptions) error {
|
|||||||
SampleRate: 16000,
|
SampleRate: 16000,
|
||||||
//WindowSize: 1024,
|
//WindowSize: 1024,
|
||||||
Threshold: 0.5,
|
Threshold: 0.5,
|
||||||
MinSilenceDurationMs: 0,
|
MinSilenceDurationMs: 100,
|
||||||
SpeechPadMs: 0,
|
SpeechPadMs: 30,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create silero detector: %w", err)
|
return fmt.Errorf("create silero detector: %w", err)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
accelerate
|
accelerate
|
||||||
auto-gptq==0.7.1
|
auto-gptq==0.7.1
|
||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
transformers
|
transformers
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
torchaudio
|
torchaudio==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
transformers
|
transformers
|
||||||
accelerate
|
accelerate
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
bark==0.1.5
|
bark==0.1.5
|
||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
# LIMIT_TARGETS="cublas12"
|
# LIMIT_TARGETS="cublas12"
|
||||||
# source $(dirname $0)/../common/libbackend.sh
|
# source $(dirname $0)/../common/libbackend.sh
|
||||||
#
|
#
|
||||||
|
|
||||||
|
PYTHON_VERSION="3.10"
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
# Name of the backend (directory name)
|
# Name of the backend (directory name)
|
||||||
BACKEND_NAME=${PWD##*/}
|
BACKEND_NAME=${PWD##*/}
|
||||||
@@ -88,7 +91,7 @@ function getBuildProfile() {
|
|||||||
# always result in an activated virtual environment
|
# always result in an activated virtual environment
|
||||||
function ensureVenv() {
|
function ensureVenv() {
|
||||||
if [ ! -d "${EDIR}/venv" ]; then
|
if [ ! -d "${EDIR}/venv" ]; then
|
||||||
uv venv ${EDIR}/venv
|
uv venv --python ${PYTHON_VERSION} ${EDIR}/venv
|
||||||
echo "virtualenv created"
|
echo "virtualenv created"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
grpcio-tools
|
grpcio-tools
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
torchaudio
|
torchaudio==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
transformers
|
transformers
|
||||||
accelerate
|
accelerate
|
||||||
coqui-tts
|
coqui-tts
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
packaging==24.1
|
packaging==24.1
|
||||||
@@ -17,7 +17,7 @@ import backend_pb2_grpc
|
|||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from diffusers import StableDiffusion3Pipeline, StableDiffusionXLPipeline, StableDiffusionDepth2ImgPipeline, DPMSolverMultistepScheduler, StableDiffusionPipeline, DiffusionPipeline, \
|
from diffusers import SanaPipeline, StableDiffusion3Pipeline, StableDiffusionXLPipeline, StableDiffusionDepth2ImgPipeline, DPMSolverMultistepScheduler, StableDiffusionPipeline, DiffusionPipeline, \
|
||||||
EulerAncestralDiscreteScheduler, FluxPipeline, FluxTransformer2DModel
|
EulerAncestralDiscreteScheduler, FluxPipeline, FluxTransformer2DModel
|
||||||
from diffusers import StableDiffusionImg2ImgPipeline, AutoPipelineForText2Image, ControlNetModel, StableVideoDiffusionPipeline
|
from diffusers import StableDiffusionImg2ImgPipeline, AutoPipelineForText2Image, ControlNetModel, StableVideoDiffusionPipeline
|
||||||
from diffusers.pipelines.stable_diffusion import safety_checker
|
from diffusers.pipelines.stable_diffusion import safety_checker
|
||||||
@@ -275,6 +275,13 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
|
|||||||
|
|
||||||
if request.LowVRAM:
|
if request.LowVRAM:
|
||||||
self.pipe.enable_model_cpu_offload()
|
self.pipe.enable_model_cpu_offload()
|
||||||
|
elif request.PipelineType == "SanaPipeline":
|
||||||
|
self.pipe = SanaPipeline.from_pretrained(
|
||||||
|
request.Model,
|
||||||
|
variant="bf16",
|
||||||
|
torch_dtype=torch.bfloat16)
|
||||||
|
self.pipe.vae.to(torch.bfloat16)
|
||||||
|
self.pipe.text_encoder.to(torch.bfloat16)
|
||||||
|
|
||||||
if CLIPSKIP and request.CLIPSkip != 0:
|
if CLIPSKIP and request.CLIPSkip != 0:
|
||||||
self.clip_skip = request.CLIPSkip
|
self.clip_skip = request.CLIPSkip
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
torchvision
|
torchvision==0.18.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
diffusers
|
diffusers
|
||||||
opencv-python
|
opencv-python
|
||||||
transformers
|
transformers
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
setuptools
|
setuptools
|
||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
pillow
|
pillow
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
wheel
|
wheel
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
torch==2.4.1
|
torch==2.4.1
|
||||||
git+https://github.com/myshell-ai/MeloTTS.git
|
git+https://github.com/myshell-ai/MeloTTS.git
|
||||||
git+https://github.com/myshell-ai/OpenVoice.git
|
git+https://github.com/myshell-ai/OpenVoice.git
|
||||||
|
whisper-timestamped
|
||||||
|
pydub==0.25.1
|
||||||
|
wavmark==0.0.3
|
||||||
|
eng_to_ipa==0.0.2
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
--extra-index-url https://download.pytorch.org/whl/cu118
|
--extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
torch==2.4.1+cu118
|
torch==2.4.1+cu118
|
||||||
git+https://github.com/myshell-ai/MeloTTS.git
|
git+https://github.com/myshell-ai/MeloTTS.git
|
||||||
git+https://github.com/myshell-ai/OpenVoice.git
|
git+https://github.com/myshell-ai/OpenVoice.git
|
||||||
|
whisper-timestamped
|
||||||
|
pydub==0.25.1
|
||||||
|
wavmark==0.0.3
|
||||||
|
eng_to_ipa==0.0.2
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
torch==2.4.1
|
torch==2.4.1
|
||||||
git+https://github.com/myshell-ai/MeloTTS.git
|
git+https://github.com/myshell-ai/MeloTTS.git
|
||||||
git+https://github.com/myshell-ai/OpenVoice.git
|
git+https://github.com/myshell-ai/OpenVoice.git
|
||||||
|
whisper-timestamped
|
||||||
|
pydub==0.25.1
|
||||||
|
wavmark==0.0.3
|
||||||
|
eng_to_ipa==0.0.2
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
--extra-index-url https://download.pytorch.org/whl/rocm6.0
|
--extra-index-url https://download.pytorch.org/whl/rocm6.0
|
||||||
torch==2.4.1+rocm6.0
|
torch==2.4.1+rocm6.0
|
||||||
git+https://github.com/myshell-ai/MeloTTS.git
|
git+https://github.com/myshell-ai/MeloTTS.git
|
||||||
git+https://github.com/myshell-ai/OpenVoice.git
|
git+https://github.com/myshell-ai/OpenVoice.git
|
||||||
|
whisper-timestamped
|
||||||
|
pydub==0.25.1
|
||||||
|
wavmark==0.0.3
|
||||||
|
eng_to_ipa==0.0.2
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
torchaudio==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
librosa==0.9.1
|
librosa==0.9.1
|
||||||
faster-whisper==0.9.0
|
faster-whisper==0.9.0
|
||||||
pydub==0.25.1
|
pydub==0.25.1
|
||||||
wavmark==0.0.3
|
wavmark==0.0.3
|
||||||
numpy==1.22.0
|
|
||||||
eng_to_ipa==0.0.2
|
eng_to_ipa==0.0.2
|
||||||
inflect==7.0.0
|
inflect==7.0.0
|
||||||
unidecode==1.3.7
|
unidecode==1.3.7
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
librosa
|
librosa
|
||||||
faster-whisper
|
faster-whisper
|
||||||
pydub==0.25.1
|
|
||||||
wavmark==0.0.3
|
|
||||||
numpy==1.22.0
|
|
||||||
eng_to_ipa==0.0.2
|
|
||||||
inflect
|
inflect
|
||||||
unidecode
|
unidecode
|
||||||
whisper-timestamped
|
|
||||||
openai
|
openai
|
||||||
python-dotenv
|
python-dotenv
|
||||||
pypinyin
|
pypinyin
|
||||||
cn2an==0.5.22
|
cn2an==0.5.22
|
||||||
|
numpy==1.22.0
|
||||||
networkx==2.8.8
|
networkx==2.8.8
|
||||||
jieba==0.42.1
|
jieba==0.42.1
|
||||||
gradio==3.48.0
|
gradio==5.9.1
|
||||||
langid==1.1.6
|
langid==1.1.6
|
||||||
llvmlite==0.43.0
|
llvmlite==0.43.0
|
||||||
|
setuptools
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
torchaudio
|
torchaudio==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
|
||||||
transformers
|
transformers
|
||||||
accelerate
|
accelerate
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
certifi
|
certifi
|
||||||
llvmlite==0.43.0
|
llvmlite==0.43.0
|
||||||
|
setuptools
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
transformers
|
transformers
|
||||||
accelerate
|
accelerate
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
rerankers[transformers]
|
rerankers[transformers]
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==69.5.1 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
accelerate
|
accelerate
|
||||||
sentence-transformers==3.3.1
|
sentence-transformers==3.3.1
|
||||||
transformers
|
transformers
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
datasets
|
datasets
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
transformers
|
transformers
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
accelerate
|
accelerate
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
scipy==1.14.0
|
scipy==1.14.0
|
||||||
certifi
|
certifi
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
intel-extension-for-transformers
|
intel-extension-for-transformers
|
||||||
bitsandbytes
|
bitsandbytes
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
setuptools==69.5.1 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
accelerate
|
accelerate
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
torchaudio
|
torchaudio==2.3.1+cxx11.abi
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
|
setuptools
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/
|
||||||
intel-extension-for-pytorch
|
intel-extension-for-pytorch==2.3.110+xpu
|
||||||
accelerate
|
accelerate
|
||||||
torch
|
torch==2.3.1+cxx11.abi
|
||||||
transformers
|
transformers
|
||||||
optimum[openvino]
|
optimum[openvino]
|
||||||
setuptools==75.1.0 # https://github.com/mudler/LocalAI/issues/2406
|
setuptools
|
||||||
bitsandbytes
|
bitsandbytes
|
||||||
|
oneccl_bind_pt==2.3.100+xpu
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
grpcio==1.68.1
|
grpcio==1.69.0
|
||||||
protobuf
|
protobuf
|
||||||
certifi
|
certifi
|
||||||
setuptools
|
setuptools
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/mudler/LocalAI/core/config"
|
|
||||||
"github.com/mudler/LocalAI/core/services"
|
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The purpose of this structure is to hold pointers to all initialized services, to make plumbing easy
|
|
||||||
// Perhaps a proper DI system is worth it in the future, but for now keep things simple.
|
|
||||||
type Application struct {
|
|
||||||
|
|
||||||
// Application-Level Config
|
|
||||||
ApplicationConfig *config.ApplicationConfig
|
|
||||||
// ApplicationState *ApplicationState
|
|
||||||
|
|
||||||
// Core Low-Level Services
|
|
||||||
BackendConfigLoader *config.BackendConfigLoader
|
|
||||||
ModelLoader *model.ModelLoader
|
|
||||||
|
|
||||||
// Backend Services
|
|
||||||
// EmbeddingsBackendService *backend.EmbeddingsBackendService
|
|
||||||
// ImageGenerationBackendService *backend.ImageGenerationBackendService
|
|
||||||
// LLMBackendService *backend.LLMBackendService
|
|
||||||
// TranscriptionBackendService *backend.TranscriptionBackendService
|
|
||||||
// TextToSpeechBackendService *backend.TextToSpeechBackendService
|
|
||||||
|
|
||||||
// LocalAI System Services
|
|
||||||
BackendMonitorService *services.BackendMonitorService
|
|
||||||
GalleryService *services.GalleryService
|
|
||||||
LocalAIMetricsService *services.LocalAIMetricsService
|
|
||||||
// OpenAIService *services.OpenAIService
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO [NEXT PR?]: Break up ApplicationConfig.
|
|
||||||
// Migrate over stuff that is not set via config at all - especially runtime stuff
|
|
||||||
type ApplicationState struct {
|
|
||||||
}
|
|
||||||
39
core/application/application.go
Normal file
39
core/application/application.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mudler/LocalAI/core/config"
|
||||||
|
"github.com/mudler/LocalAI/pkg/model"
|
||||||
|
"github.com/mudler/LocalAI/pkg/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
backendLoader *config.BackendConfigLoader
|
||||||
|
modelLoader *model.ModelLoader
|
||||||
|
applicationConfig *config.ApplicationConfig
|
||||||
|
templatesEvaluator *templates.Evaluator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApplication(appConfig *config.ApplicationConfig) *Application {
|
||||||
|
return &Application{
|
||||||
|
backendLoader: config.NewBackendConfigLoader(appConfig.ModelPath),
|
||||||
|
modelLoader: model.NewModelLoader(appConfig.ModelPath),
|
||||||
|
applicationConfig: appConfig,
|
||||||
|
templatesEvaluator: templates.NewEvaluator(appConfig.ModelPath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) BackendLoader() *config.BackendConfigLoader {
|
||||||
|
return a.backendLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) ModelLoader() *model.ModelLoader {
|
||||||
|
return a.modelLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) ApplicationConfig() *config.ApplicationConfig {
|
||||||
|
return a.applicationConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) TemplatesEvaluator() *templates.Evaluator {
|
||||||
|
return a.templatesEvaluator
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package startup
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"dario.cat/mergo"
|
"dario.cat/mergo"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package startup
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/mudler/LocalAI/core"
|
|
||||||
"github.com/mudler/LocalAI/core/backend"
|
"github.com/mudler/LocalAI/core/backend"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/services"
|
"github.com/mudler/LocalAI/core/services"
|
||||||
"github.com/mudler/LocalAI/internal"
|
"github.com/mudler/LocalAI/internal"
|
||||||
"github.com/mudler/LocalAI/pkg/assets"
|
"github.com/mudler/LocalAI/pkg/assets"
|
||||||
|
|
||||||
"github.com/mudler/LocalAI/pkg/library"
|
"github.com/mudler/LocalAI/pkg/library"
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
"github.com/mudler/LocalAI/pkg/model"
|
||||||
pkgStartup "github.com/mudler/LocalAI/pkg/startup"
|
pkgStartup "github.com/mudler/LocalAI/pkg/startup"
|
||||||
@@ -17,8 +17,9 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.ModelLoader, *config.ApplicationConfig, error) {
|
func New(opts ...config.AppOption) (*Application, error) {
|
||||||
options := config.NewApplicationConfig(opts...)
|
options := config.NewApplicationConfig(opts...)
|
||||||
|
application := newApplication(options)
|
||||||
|
|
||||||
log.Info().Msgf("Starting LocalAI using %d threads, with models path: %s", options.Threads, options.ModelPath)
|
log.Info().Msgf("Starting LocalAI using %d threads, with models path: %s", options.Threads, options.ModelPath)
|
||||||
log.Info().Msgf("LocalAI version: %s", internal.PrintableVersion())
|
log.Info().Msgf("LocalAI version: %s", internal.PrintableVersion())
|
||||||
@@ -36,28 +37,28 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
|
|
||||||
// Make sure directories exists
|
// Make sure directories exists
|
||||||
if options.ModelPath == "" {
|
if options.ModelPath == "" {
|
||||||
return nil, nil, nil, fmt.Errorf("options.ModelPath cannot be empty")
|
return nil, fmt.Errorf("options.ModelPath cannot be empty")
|
||||||
}
|
}
|
||||||
err = os.MkdirAll(options.ModelPath, 0750)
|
err = os.MkdirAll(options.ModelPath, 0750)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("unable to create ModelPath: %q", err)
|
return nil, fmt.Errorf("unable to create ModelPath: %q", err)
|
||||||
}
|
}
|
||||||
if options.ImageDir != "" {
|
if options.ImageDir != "" {
|
||||||
err := os.MkdirAll(options.ImageDir, 0750)
|
err := os.MkdirAll(options.ImageDir, 0750)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("unable to create ImageDir: %q", err)
|
return nil, fmt.Errorf("unable to create ImageDir: %q", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.AudioDir != "" {
|
if options.AudioDir != "" {
|
||||||
err := os.MkdirAll(options.AudioDir, 0750)
|
err := os.MkdirAll(options.AudioDir, 0750)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("unable to create AudioDir: %q", err)
|
return nil, fmt.Errorf("unable to create AudioDir: %q", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.UploadDir != "" {
|
if options.UploadDir != "" {
|
||||||
err := os.MkdirAll(options.UploadDir, 0750)
|
err := os.MkdirAll(options.UploadDir, 0750)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("unable to create UploadDir: %q", err)
|
return nil, fmt.Errorf("unable to create UploadDir: %q", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,39 +66,36 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
log.Error().Err(err).Msg("error installing models")
|
log.Error().Err(err).Msg("error installing models")
|
||||||
}
|
}
|
||||||
|
|
||||||
cl := config.NewBackendConfigLoader(options.ModelPath)
|
|
||||||
ml := model.NewModelLoader(options.ModelPath)
|
|
||||||
|
|
||||||
configLoaderOpts := options.ToConfigLoaderOptions()
|
configLoaderOpts := options.ToConfigLoaderOptions()
|
||||||
|
|
||||||
if err := cl.LoadBackendConfigsFromPath(options.ModelPath, configLoaderOpts...); err != nil {
|
if err := application.BackendLoader().LoadBackendConfigsFromPath(options.ModelPath, configLoaderOpts...); err != nil {
|
||||||
log.Error().Err(err).Msg("error loading config files")
|
log.Error().Err(err).Msg("error loading config files")
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.ConfigFile != "" {
|
if options.ConfigFile != "" {
|
||||||
if err := cl.LoadMultipleBackendConfigsSingleFile(options.ConfigFile, configLoaderOpts...); err != nil {
|
if err := application.BackendLoader().LoadMultipleBackendConfigsSingleFile(options.ConfigFile, configLoaderOpts...); err != nil {
|
||||||
log.Error().Err(err).Msg("error loading config file")
|
log.Error().Err(err).Msg("error loading config file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.Preload(options.ModelPath); err != nil {
|
if err := application.BackendLoader().Preload(options.ModelPath); err != nil {
|
||||||
log.Error().Err(err).Msg("error downloading models")
|
log.Error().Err(err).Msg("error downloading models")
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.PreloadJSONModels != "" {
|
if options.PreloadJSONModels != "" {
|
||||||
if err := services.ApplyGalleryFromString(options.ModelPath, options.PreloadJSONModels, options.EnforcePredownloadScans, options.Galleries); err != nil {
|
if err := services.ApplyGalleryFromString(options.ModelPath, options.PreloadJSONModels, options.EnforcePredownloadScans, options.Galleries); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.PreloadModelsFromPath != "" {
|
if options.PreloadModelsFromPath != "" {
|
||||||
if err := services.ApplyGalleryFromFile(options.ModelPath, options.PreloadModelsFromPath, options.EnforcePredownloadScans, options.Galleries); err != nil {
|
if err := services.ApplyGalleryFromFile(options.ModelPath, options.PreloadModelsFromPath, options.EnforcePredownloadScans, options.Galleries); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Debug {
|
if options.Debug {
|
||||||
for _, v := range cl.GetAllBackendConfigs() {
|
for _, v := range application.BackendLoader().GetAllBackendConfigs() {
|
||||||
log.Debug().Msgf("Model: %s (config: %+v)", v.Name, v)
|
log.Debug().Msgf("Model: %s (config: %+v)", v.Name, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +121,7 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
go func() {
|
go func() {
|
||||||
<-options.Context.Done()
|
<-options.Context.Done()
|
||||||
log.Debug().Msgf("Context canceled, shutting down")
|
log.Debug().Msgf("Context canceled, shutting down")
|
||||||
err := ml.StopAllGRPC()
|
err := application.ModelLoader().StopAllGRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error while stopping all grpc backends")
|
log.Error().Err(err).Msg("error while stopping all grpc backends")
|
||||||
}
|
}
|
||||||
@@ -131,12 +129,12 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
|
|
||||||
if options.WatchDog {
|
if options.WatchDog {
|
||||||
wd := model.NewWatchDog(
|
wd := model.NewWatchDog(
|
||||||
ml,
|
application.ModelLoader(),
|
||||||
options.WatchDogBusyTimeout,
|
options.WatchDogBusyTimeout,
|
||||||
options.WatchDogIdleTimeout,
|
options.WatchDogIdleTimeout,
|
||||||
options.WatchDogBusy,
|
options.WatchDogBusy,
|
||||||
options.WatchDogIdle)
|
options.WatchDogIdle)
|
||||||
ml.SetWatchDog(wd)
|
application.ModelLoader().SetWatchDog(wd)
|
||||||
go wd.Run()
|
go wd.Run()
|
||||||
go func() {
|
go func() {
|
||||||
<-options.Context.Done()
|
<-options.Context.Done()
|
||||||
@@ -147,7 +145,7 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
|
|
||||||
if options.LoadToMemory != nil {
|
if options.LoadToMemory != nil {
|
||||||
for _, m := range options.LoadToMemory {
|
for _, m := range options.LoadToMemory {
|
||||||
cfg, err := cl.LoadBackendConfigFileByName(m, options.ModelPath,
|
cfg, err := application.BackendLoader().LoadBackendConfigFileByName(m, options.ModelPath,
|
||||||
config.LoadOptionDebug(options.Debug),
|
config.LoadOptionDebug(options.Debug),
|
||||||
config.LoadOptionThreads(options.Threads),
|
config.LoadOptionThreads(options.Threads),
|
||||||
config.LoadOptionContextSize(options.ContextSize),
|
config.LoadOptionContextSize(options.ContextSize),
|
||||||
@@ -155,7 +153,7 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
config.ModelPath(options.ModelPath),
|
config.ModelPath(options.ModelPath),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("Auto loading model %s into memory from file: %s", m, cfg.Model)
|
log.Debug().Msgf("Auto loading model %s into memory from file: %s", m, cfg.Model)
|
||||||
@@ -163,9 +161,9 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
o := backend.ModelOptions(*cfg, options)
|
o := backend.ModelOptions(*cfg, options)
|
||||||
|
|
||||||
var backendErr error
|
var backendErr error
|
||||||
_, backendErr = ml.Load(o...)
|
_, backendErr = application.ModelLoader().Load(o...)
|
||||||
if backendErr != nil {
|
if backendErr != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +172,7 @@ func Startup(opts ...config.AppOption) (*config.BackendConfigLoader, *model.Mode
|
|||||||
startWatcher(options)
|
startWatcher(options)
|
||||||
|
|
||||||
log.Info().Msg("core/startup process completed!")
|
log.Info().Msg("core/startup process completed!")
|
||||||
return cl, ml, options, nil
|
return application, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startWatcher(options *config.ApplicationConfig) {
|
func startWatcher(options *config.ApplicationConfig) {
|
||||||
@@ -201,32 +199,3 @@ func startWatcher(options *config.ApplicationConfig) {
|
|||||||
log.Error().Err(err).Msg("failed creating watcher")
|
log.Error().Err(err).Msg("failed creating watcher")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In Lieu of a proper DI framework, this function wires up the Application manually.
|
|
||||||
// This is in core/startup rather than core/state.go to keep package references clean!
|
|
||||||
func createApplication(appConfig *config.ApplicationConfig) *core.Application {
|
|
||||||
app := &core.Application{
|
|
||||||
ApplicationConfig: appConfig,
|
|
||||||
BackendConfigLoader: config.NewBackendConfigLoader(appConfig.ModelPath),
|
|
||||||
ModelLoader: model.NewModelLoader(appConfig.ModelPath),
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// app.EmbeddingsBackendService = backend.NewEmbeddingsBackendService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
// app.ImageGenerationBackendService = backend.NewImageGenerationBackendService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
// app.LLMBackendService = backend.NewLLMBackendService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
// app.TranscriptionBackendService = backend.NewTranscriptionBackendService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
// app.TextToSpeechBackendService = backend.NewTextToSpeechBackendService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
|
|
||||||
app.BackendMonitorService = services.NewBackendMonitorService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig)
|
|
||||||
app.GalleryService = services.NewGalleryService(app.ApplicationConfig)
|
|
||||||
// app.OpenAIService = services.NewOpenAIService(app.ModelLoader, app.BackendConfigLoader, app.ApplicationConfig, app.LLMBackendService)
|
|
||||||
|
|
||||||
app.LocalAIMetricsService, err = services.NewLocalAIMetricsService()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("encountered an error initializing metrics service, startup will continue but metrics will not be tracked.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
@@ -22,8 +22,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LLMResponse struct {
|
type LLMResponse struct {
|
||||||
Response string // should this be []byte?
|
Response string // should this be []byte?
|
||||||
Usage TokenUsage
|
Usage TokenUsage
|
||||||
|
AudioOutput string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenUsage struct {
|
type TokenUsage struct {
|
||||||
@@ -118,7 +119,7 @@ func ModelInference(ctx context.Context, s string, messages []schema.Message, im
|
|||||||
|
|
||||||
var partialRune []byte
|
var partialRune []byte
|
||||||
err := inferenceModel.PredictStream(ctx, opts, func(reply *proto.Reply) {
|
err := inferenceModel.PredictStream(ctx, opts, func(reply *proto.Reply) {
|
||||||
msg := reply.GetMessage()
|
msg := reply.Message
|
||||||
partialRune = append(partialRune, msg...)
|
partialRune = append(partialRune, msg...)
|
||||||
|
|
||||||
tokenUsage.Prompt = int(reply.PromptTokens)
|
tokenUsage.Prompt = int(reply.PromptTokens)
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ func grpcModelOpts(c config.BackendConfig) *pb.ModelOptions {
|
|||||||
TensorParallelSize: int32(c.TensorParallelSize),
|
TensorParallelSize: int32(c.TensorParallelSize),
|
||||||
MMProj: c.MMProj,
|
MMProj: c.MMProj,
|
||||||
FlashAttention: c.FlashAttention,
|
FlashAttention: c.FlashAttention,
|
||||||
|
CacheTypeKey: c.CacheTypeK,
|
||||||
|
CacheTypeValue: c.CacheTypeV,
|
||||||
NoKVOffload: c.NoKVOffloading,
|
NoKVOffload: c.NoKVOffloading,
|
||||||
YarnExtFactor: c.YarnExtFactor,
|
YarnExtFactor: c.YarnExtFactor,
|
||||||
YarnAttnFactor: c.YarnAttnFactor,
|
YarnAttnFactor: c.YarnAttnFactor,
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mudler/LocalAI/core/application"
|
||||||
cli_api "github.com/mudler/LocalAI/core/cli/api"
|
cli_api "github.com/mudler/LocalAI/core/cli/api"
|
||||||
cliContext "github.com/mudler/LocalAI/core/cli/context"
|
cliContext "github.com/mudler/LocalAI/core/cli/context"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/http"
|
"github.com/mudler/LocalAI/core/http"
|
||||||
"github.com/mudler/LocalAI/core/p2p"
|
"github.com/mudler/LocalAI/core/p2p"
|
||||||
"github.com/mudler/LocalAI/core/startup"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -186,16 +186,16 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.PreloadBackendOnly {
|
if r.PreloadBackendOnly {
|
||||||
_, _, _, err := startup.Startup(opts...)
|
_, err := application.New(opts...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, ml, options, err := startup.Startup(opts...)
|
app, err := application.New(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed basic startup tasks with error %s", err.Error())
|
return fmt.Errorf("failed basic startup tasks with error %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
appHTTP, err := http.App(cl, ml, options)
|
appHTTP, err := http.API(app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error during HTTP App construction")
|
log.Error().Err(err).Msg("error during HTTP App construction")
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type BackendConfig struct {
|
|||||||
TemplateConfig TemplateConfig `yaml:"template"`
|
TemplateConfig TemplateConfig `yaml:"template"`
|
||||||
KnownUsecaseStrings []string `yaml:"known_usecases"`
|
KnownUsecaseStrings []string `yaml:"known_usecases"`
|
||||||
KnownUsecases *BackendConfigUsecases `yaml:"-"`
|
KnownUsecases *BackendConfigUsecases `yaml:"-"`
|
||||||
|
Pipeline Pipeline `yaml:"pipeline"`
|
||||||
|
|
||||||
PromptStrings, InputStrings []string `yaml:"-"`
|
PromptStrings, InputStrings []string `yaml:"-"`
|
||||||
InputToken [][]int `yaml:"-"`
|
InputToken [][]int `yaml:"-"`
|
||||||
@@ -76,6 +77,18 @@ type BackendConfig struct {
|
|||||||
Options []string `yaml:"options"`
|
Options []string `yaml:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pipeline defines other models to use for audio-to-audio
|
||||||
|
type Pipeline struct {
|
||||||
|
TTS string `yaml:"tts"`
|
||||||
|
LLM string `yaml:"llm"`
|
||||||
|
Transcription string `yaml:"transcription"`
|
||||||
|
VAD string `yaml:"vad"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pipeline) IsNotConfigured() bool {
|
||||||
|
return p.LLM == "" || p.TTS == "" || p.Transcription == ""
|
||||||
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Filename string `yaml:"filename" json:"filename"`
|
Filename string `yaml:"filename" json:"filename"`
|
||||||
SHA256 string `yaml:"sha256" json:"sha256"`
|
SHA256 string `yaml:"sha256" json:"sha256"`
|
||||||
@@ -155,8 +168,10 @@ type LLMConfig struct {
|
|||||||
TensorParallelSize int `yaml:"tensor_parallel_size"` // vLLM
|
TensorParallelSize int `yaml:"tensor_parallel_size"` // vLLM
|
||||||
MMProj string `yaml:"mmproj"`
|
MMProj string `yaml:"mmproj"`
|
||||||
|
|
||||||
FlashAttention bool `yaml:"flash_attention"`
|
FlashAttention bool `yaml:"flash_attention"`
|
||||||
NoKVOffloading bool `yaml:"no_kv_offloading"`
|
NoKVOffloading bool `yaml:"no_kv_offloading"`
|
||||||
|
CacheTypeK string `yaml:"cache_type_k"`
|
||||||
|
CacheTypeV string `yaml:"cache_type_v"`
|
||||||
|
|
||||||
RopeScaling string `yaml:"rope_scaling"`
|
RopeScaling string `yaml:"rope_scaling"`
|
||||||
ModelType string `yaml:"type"`
|
ModelType string `yaml:"type"`
|
||||||
@@ -204,6 +219,8 @@ type TemplateConfig struct {
|
|||||||
JoinChatMessagesByCharacter *string `yaml:"join_chat_messages_by_character"`
|
JoinChatMessagesByCharacter *string `yaml:"join_chat_messages_by_character"`
|
||||||
|
|
||||||
Multimodal string `yaml:"multimodal"`
|
Multimodal string `yaml:"multimodal"`
|
||||||
|
|
||||||
|
JinjaTemplate bool `yaml:"jinja_template"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BackendConfig) UnmarshalYAML(value *yaml.Node) error {
|
func (c *BackendConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ const (
|
|||||||
type settingsConfig struct {
|
type settingsConfig struct {
|
||||||
StopWords []string
|
StopWords []string
|
||||||
TemplateConfig TemplateConfig
|
TemplateConfig TemplateConfig
|
||||||
RepeatPenalty float64
|
RepeatPenalty float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// default settings to adopt with a given model family
|
// default settings to adopt with a given model family
|
||||||
var defaultsSettings map[familyType]settingsConfig = map[familyType]settingsConfig{
|
var defaultsSettings map[familyType]settingsConfig = map[familyType]settingsConfig{
|
||||||
Gemma: {
|
Gemma: {
|
||||||
RepeatPenalty: 1.0,
|
RepeatPenalty: 1.0,
|
||||||
StopWords: []string{"<|im_end|>", "<end_of_turn>", "<start_of_turn>"},
|
StopWords: []string{"<|im_end|>", "<end_of_turn>", "<start_of_turn>"},
|
||||||
TemplateConfig: TemplateConfig{
|
TemplateConfig: TemplateConfig{
|
||||||
Chat: "{{.Input }}\n<start_of_turn>model\n",
|
Chat: "{{.Input }}\n<start_of_turn>model\n",
|
||||||
ChatMessage: "<start_of_turn>{{if eq .RoleName \"assistant\" }}model{{else}}{{ .RoleName }}{{end}}\n{{ if .Content -}}\n{{.Content -}}\n{{ end -}}<end_of_turn>",
|
ChatMessage: "<start_of_turn>{{if eq .RoleName \"assistant\" }}model{{else}}{{ .RoleName }}{{end}}\n{{ if .Content -}}\n{{.Content -}}\n{{ end -}}<end_of_turn>",
|
||||||
@@ -200,6 +200,18 @@ func guessDefaultsFromFile(cfg *BackendConfig, modelPath string) {
|
|||||||
} else {
|
} else {
|
||||||
log.Debug().Any("family", family).Msgf("guessDefaultsFromFile: no template found for family")
|
log.Debug().Any("family", family).Msgf("guessDefaultsFromFile: no template found for family")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.HasTemplate() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify from well known templates first, otherwise use the raw jinja template
|
||||||
|
chatTemplate, found := f.Header.MetadataKV.Get("tokenizer.chat_template")
|
||||||
|
if found {
|
||||||
|
// try to use the jinja template
|
||||||
|
cfg.TemplateConfig.JinjaTemplate = true
|
||||||
|
cfg.TemplateConfig.ChatMessage = chatTemplate.ValueString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func identifyFamily(f *gguf.GGUFFile) familyType {
|
func identifyFamily(f *gguf.GGUFFile) familyType {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/dave-gray101/v2keyauth"
|
"github.com/dave-gray101/v2keyauth"
|
||||||
|
"github.com/gofiber/websocket/v2"
|
||||||
"github.com/mudler/LocalAI/pkg/utils"
|
"github.com/mudler/LocalAI/pkg/utils"
|
||||||
|
|
||||||
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
||||||
@@ -14,10 +15,9 @@ import (
|
|||||||
"github.com/mudler/LocalAI/core/http/middleware"
|
"github.com/mudler/LocalAI/core/http/middleware"
|
||||||
"github.com/mudler/LocalAI/core/http/routes"
|
"github.com/mudler/LocalAI/core/http/routes"
|
||||||
|
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/application"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/mudler/LocalAI/core/services"
|
"github.com/mudler/LocalAI/core/services"
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
|
||||||
|
|
||||||
"github.com/gofiber/contrib/fiberzerolog"
|
"github.com/gofiber/contrib/fiberzerolog"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -49,18 +49,18 @@ var embedDirStatic embed.FS
|
|||||||
// @in header
|
// @in header
|
||||||
// @name Authorization
|
// @name Authorization
|
||||||
|
|
||||||
func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig) (*fiber.App, error) {
|
func API(application *application.Application) (*fiber.App, error) {
|
||||||
|
|
||||||
fiberCfg := fiber.Config{
|
fiberCfg := fiber.Config{
|
||||||
Views: renderEngine(),
|
Views: renderEngine(),
|
||||||
BodyLimit: appConfig.UploadLimitMB * 1024 * 1024, // this is the default limit of 4MB
|
BodyLimit: application.ApplicationConfig().UploadLimitMB * 1024 * 1024, // this is the default limit of 4MB
|
||||||
// We disable the Fiber startup message as it does not conform to structured logging.
|
// We disable the Fiber startup message as it does not conform to structured logging.
|
||||||
// We register a startup log line with connection information in the OnListen hook to keep things user friendly though
|
// We register a startup log line with connection information in the OnListen hook to keep things user friendly though
|
||||||
DisableStartupMessage: true,
|
DisableStartupMessage: true,
|
||||||
// Override default error handler
|
// Override default error handler
|
||||||
}
|
}
|
||||||
|
|
||||||
if !appConfig.OpaqueErrors {
|
if !application.ApplicationConfig().OpaqueErrors {
|
||||||
// Normally, return errors as JSON responses
|
// Normally, return errors as JSON responses
|
||||||
fiberCfg.ErrorHandler = func(ctx *fiber.Ctx, err error) error {
|
fiberCfg.ErrorHandler = func(ctx *fiber.Ctx, err error) error {
|
||||||
// Status code defaults to 500
|
// Status code defaults to 500
|
||||||
@@ -86,9 +86,20 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app := fiber.New(fiberCfg)
|
router := fiber.New(fiberCfg)
|
||||||
|
|
||||||
app.Hooks().OnListen(func(listenData fiber.ListenData) error {
|
router.Use(middleware.StripPathPrefix())
|
||||||
|
|
||||||
|
router.Use("/v1/realtime", func(c *fiber.Ctx) error {
|
||||||
|
if websocket.IsWebSocketUpgrade(c) {
|
||||||
|
// Returns true if the client requested upgrade to the WebSocket protocol
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Hooks().OnListen(func(listenData fiber.ListenData) error {
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if listenData.TLS {
|
if listenData.TLS {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
@@ -99,82 +110,82 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi
|
|||||||
|
|
||||||
// Have Fiber use zerolog like the rest of the application rather than it's built-in logger
|
// Have Fiber use zerolog like the rest of the application rather than it's built-in logger
|
||||||
logger := log.Logger
|
logger := log.Logger
|
||||||
app.Use(fiberzerolog.New(fiberzerolog.Config{
|
router.Use(fiberzerolog.New(fiberzerolog.Config{
|
||||||
Logger: &logger,
|
Logger: &logger,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Default middleware config
|
// Default middleware config
|
||||||
|
|
||||||
if !appConfig.Debug {
|
if !application.ApplicationConfig().Debug {
|
||||||
app.Use(recover.New())
|
router.Use(recover.New())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !appConfig.DisableMetrics {
|
if !application.ApplicationConfig().DisableMetrics {
|
||||||
metricsService, err := services.NewLocalAIMetricsService()
|
metricsService, err := services.NewLocalAIMetricsService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if metricsService != nil {
|
if metricsService != nil {
|
||||||
app.Use(localai.LocalAIMetricsAPIMiddleware(metricsService))
|
router.Use(localai.LocalAIMetricsAPIMiddleware(metricsService))
|
||||||
app.Hooks().OnShutdown(func() error {
|
router.Hooks().OnShutdown(func() error {
|
||||||
return metricsService.Shutdown()
|
return metricsService.Shutdown()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// Health Checks should always be exempt from auth, so register these first
|
// Health Checks should always be exempt from auth, so register these first
|
||||||
routes.HealthRoutes(app)
|
routes.HealthRoutes(router)
|
||||||
|
|
||||||
kaConfig, err := middleware.GetKeyAuthConfig(appConfig)
|
kaConfig, err := middleware.GetKeyAuthConfig(application.ApplicationConfig())
|
||||||
if err != nil || kaConfig == nil {
|
if err != nil || kaConfig == nil {
|
||||||
return nil, fmt.Errorf("failed to create key auth config: %w", err)
|
return nil, fmt.Errorf("failed to create key auth config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth is applied to _all_ endpoints. No exceptions. Filtering out endpoints to bypass is the role of the Filter property of the KeyAuth Configuration
|
// Auth is applied to _all_ endpoints. No exceptions. Filtering out endpoints to bypass is the role of the Filter property of the KeyAuth Configuration
|
||||||
app.Use(v2keyauth.New(*kaConfig))
|
router.Use(v2keyauth.New(*kaConfig))
|
||||||
|
|
||||||
if appConfig.CORS {
|
if application.ApplicationConfig().CORS {
|
||||||
var c func(ctx *fiber.Ctx) error
|
var c func(ctx *fiber.Ctx) error
|
||||||
if appConfig.CORSAllowOrigins == "" {
|
if application.ApplicationConfig().CORSAllowOrigins == "" {
|
||||||
c = cors.New()
|
c = cors.New()
|
||||||
} else {
|
} else {
|
||||||
c = cors.New(cors.Config{AllowOrigins: appConfig.CORSAllowOrigins})
|
c = cors.New(cors.Config{AllowOrigins: application.ApplicationConfig().CORSAllowOrigins})
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Use(c)
|
router.Use(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if appConfig.CSRF {
|
if application.ApplicationConfig().CSRF {
|
||||||
log.Debug().Msg("Enabling CSRF middleware. Tokens are now required for state-modifying requests")
|
log.Debug().Msg("Enabling CSRF middleware. Tokens are now required for state-modifying requests")
|
||||||
app.Use(csrf.New())
|
router.Use(csrf.New())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config jsons
|
// Load config jsons
|
||||||
utils.LoadConfig(appConfig.UploadDir, openai.UploadedFilesFile, &openai.UploadedFiles)
|
utils.LoadConfig(application.ApplicationConfig().UploadDir, openai.UploadedFilesFile, &openai.UploadedFiles)
|
||||||
utils.LoadConfig(appConfig.ConfigsDir, openai.AssistantsConfigFile, &openai.Assistants)
|
utils.LoadConfig(application.ApplicationConfig().ConfigsDir, openai.AssistantsConfigFile, &openai.Assistants)
|
||||||
utils.LoadConfig(appConfig.ConfigsDir, openai.AssistantsFileConfigFile, &openai.AssistantFiles)
|
utils.LoadConfig(application.ApplicationConfig().ConfigsDir, openai.AssistantsFileConfigFile, &openai.AssistantFiles)
|
||||||
|
|
||||||
galleryService := services.NewGalleryService(appConfig)
|
galleryService := services.NewGalleryService(application.ApplicationConfig())
|
||||||
galleryService.Start(appConfig.Context, cl)
|
galleryService.Start(application.ApplicationConfig().Context, application.BackendLoader())
|
||||||
|
|
||||||
routes.RegisterElevenLabsRoutes(app, cl, ml, appConfig)
|
routes.RegisterElevenLabsRoutes(router, application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig())
|
||||||
routes.RegisterLocalAIRoutes(app, cl, ml, appConfig, galleryService)
|
routes.RegisterLocalAIRoutes(router, application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig(), galleryService)
|
||||||
routes.RegisterOpenAIRoutes(app, cl, ml, appConfig)
|
routes.RegisterOpenAIRoutes(router, application)
|
||||||
if !appConfig.DisableWebUI {
|
if !application.ApplicationConfig().DisableWebUI {
|
||||||
routes.RegisterUIRoutes(app, cl, ml, appConfig, galleryService)
|
routes.RegisterUIRoutes(router, application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig(), galleryService)
|
||||||
}
|
}
|
||||||
routes.RegisterJINARoutes(app, cl, ml, appConfig)
|
routes.RegisterJINARoutes(router, application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig())
|
||||||
|
|
||||||
httpFS := http.FS(embedDirStatic)
|
httpFS := http.FS(embedDirStatic)
|
||||||
|
|
||||||
app.Use(favicon.New(favicon.Config{
|
router.Use(favicon.New(favicon.Config{
|
||||||
URL: "/favicon.ico",
|
URL: "/favicon.ico",
|
||||||
FileSystem: httpFS,
|
FileSystem: httpFS,
|
||||||
File: "static/favicon.ico",
|
File: "static/favicon.ico",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.Use("/static", filesystem.New(filesystem.Config{
|
router.Use("/static", filesystem.New(filesystem.Config{
|
||||||
Root: httpFS,
|
Root: httpFS,
|
||||||
PathPrefix: "static",
|
PathPrefix: "static",
|
||||||
Browse: true,
|
Browse: true,
|
||||||
@@ -182,7 +193,7 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi
|
|||||||
|
|
||||||
// Define a custom 404 handler
|
// Define a custom 404 handler
|
||||||
// Note: keep this at the bottom!
|
// Note: keep this at the bottom!
|
||||||
app.Use(notFoundHandler)
|
router.Use(notFoundHandler)
|
||||||
|
|
||||||
return app, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/mudler/LocalAI/core/application"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
. "github.com/mudler/LocalAI/core/http"
|
. "github.com/mudler/LocalAI/core/http"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/mudler/LocalAI/core/startup"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/mudler/LocalAI/core/gallery"
|
"github.com/mudler/LocalAI/core/gallery"
|
||||||
"github.com/mudler/LocalAI/pkg/downloader"
|
"github.com/mudler/LocalAI/pkg/downloader"
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@@ -238,6 +237,31 @@ func postInvalidRequest(url string) (error, int) {
|
|||||||
return nil, resp.StatusCode
|
return nil, resp.StatusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequest(url string, header http.Header) (error, int, []byte) {
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err, -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header = header
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err, -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err, -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, resp.StatusCode, body
|
||||||
|
}
|
||||||
|
|
||||||
const bertEmbeddingsURL = `https://gist.githubusercontent.com/mudler/0a080b166b87640e8644b09c2aee6e3b/raw/f0e8c26bb72edc16d9fbafbfd6638072126ff225/bert-embeddings-gallery.yaml`
|
const bertEmbeddingsURL = `https://gist.githubusercontent.com/mudler/0a080b166b87640e8644b09c2aee6e3b/raw/f0e8c26bb72edc16d9fbafbfd6638072126ff225/bert-embeddings-gallery.yaml`
|
||||||
|
|
||||||
//go:embed backend-assets/*
|
//go:embed backend-assets/*
|
||||||
@@ -252,9 +276,6 @@ var _ = Describe("API test", func() {
|
|||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
var tmpdir string
|
var tmpdir string
|
||||||
var modelDir string
|
var modelDir string
|
||||||
var bcl *config.BackendConfigLoader
|
|
||||||
var ml *model.ModelLoader
|
|
||||||
var applicationConfig *config.ApplicationConfig
|
|
||||||
|
|
||||||
commonOpts := []config.AppOption{
|
commonOpts := []config.AppOption{
|
||||||
config.WithDebug(true),
|
config.WithDebug(true),
|
||||||
@@ -300,7 +321,7 @@ var _ = Describe("API test", func() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
bcl, ml, applicationConfig, err = startup.Startup(
|
application, err := application.New(
|
||||||
append(commonOpts,
|
append(commonOpts,
|
||||||
config.WithContext(c),
|
config.WithContext(c),
|
||||||
config.WithGalleries(galleries),
|
config.WithGalleries(galleries),
|
||||||
@@ -310,7 +331,7 @@ var _ = Describe("API test", func() {
|
|||||||
config.WithBackendAssetsOutput(backendAssetsDir))...)
|
config.WithBackendAssetsOutput(backendAssetsDir))...)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
app, err = App(bcl, ml, applicationConfig)
|
app, err = API(application)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
go app.Listen("127.0.0.1:9090")
|
go app.Listen("127.0.0.1:9090")
|
||||||
@@ -349,6 +370,33 @@ var _ = Describe("API test", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("URL routing Tests", func() {
|
||||||
|
It("Should support reverse-proxy when unauthenticated", func() {
|
||||||
|
|
||||||
|
err, sc, body := getRequest("http://127.0.0.1:9090/myprefix/", http.Header{
|
||||||
|
"X-Forwarded-Proto": {"https"},
|
||||||
|
"X-Forwarded-Host": {"example.org"},
|
||||||
|
"X-Forwarded-Prefix": {"/myprefix/"},
|
||||||
|
})
|
||||||
|
Expect(err).To(BeNil(), "error")
|
||||||
|
Expect(sc).To(Equal(401), "status code")
|
||||||
|
Expect(string(body)).To(ContainSubstring(`<base href="https://example.org/myprefix/" />`), "body")
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Should support reverse-proxy when authenticated", func() {
|
||||||
|
|
||||||
|
err, sc, body := getRequest("http://127.0.0.1:9090/myprefix/", http.Header{
|
||||||
|
"Authorization": {bearerKey},
|
||||||
|
"X-Forwarded-Proto": {"https"},
|
||||||
|
"X-Forwarded-Host": {"example.org"},
|
||||||
|
"X-Forwarded-Prefix": {"/myprefix/"},
|
||||||
|
})
|
||||||
|
Expect(err).To(BeNil(), "error")
|
||||||
|
Expect(sc).To(Equal(200), "status code")
|
||||||
|
Expect(string(body)).To(ContainSubstring(`<base href="https://example.org/myprefix/" />`), "body")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Context("Applying models", func() {
|
Context("Applying models", func() {
|
||||||
|
|
||||||
It("applies models from a gallery", func() {
|
It("applies models from a gallery", func() {
|
||||||
@@ -539,7 +587,7 @@ var _ = Describe("API test", func() {
|
|||||||
var res map[string]string
|
var res map[string]string
|
||||||
err = json.Unmarshal([]byte(resp2.Choices[0].Message.FunctionCall.Arguments), &res)
|
err = json.Unmarshal([]byte(resp2.Choices[0].Message.FunctionCall.Arguments), &res)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res["location"]).To(Equal("San Francisco"), fmt.Sprint(res))
|
Expect(res["location"]).To(ContainSubstring("San Francisco"), fmt.Sprint(res))
|
||||||
Expect(res["unit"]).To(Equal("celcius"), fmt.Sprint(res))
|
Expect(res["unit"]).To(Equal("celcius"), fmt.Sprint(res))
|
||||||
Expect(string(resp2.Choices[0].FinishReason)).To(Equal("function_call"), fmt.Sprint(resp2.Choices[0].FinishReason))
|
Expect(string(resp2.Choices[0].FinishReason)).To(Equal("function_call"), fmt.Sprint(resp2.Choices[0].FinishReason))
|
||||||
|
|
||||||
@@ -641,7 +689,7 @@ var _ = Describe("API test", func() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
bcl, ml, applicationConfig, err = startup.Startup(
|
application, err := application.New(
|
||||||
append(commonOpts,
|
append(commonOpts,
|
||||||
config.WithContext(c),
|
config.WithContext(c),
|
||||||
config.WithAudioDir(tmpdir),
|
config.WithAudioDir(tmpdir),
|
||||||
@@ -652,7 +700,7 @@ var _ = Describe("API test", func() {
|
|||||||
config.WithBackendAssetsOutput(tmpdir))...,
|
config.WithBackendAssetsOutput(tmpdir))...,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
app, err = App(bcl, ml, applicationConfig)
|
app, err = API(application)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
go app.Listen("127.0.0.1:9090")
|
go app.Listen("127.0.0.1:9090")
|
||||||
@@ -708,7 +756,7 @@ var _ = Describe("API test", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred(), fmt.Sprint(resp))
|
Expect(err).ToNot(HaveOccurred(), fmt.Sprint(resp))
|
||||||
|
|
||||||
Expect(resp.StatusCode).To(Equal(200), fmt.Sprint(string(dat)))
|
Expect(resp.StatusCode).To(Equal(200), fmt.Sprint(string(dat)))
|
||||||
Expect(resp.Header.Get("Content-Type")).To(Equal("audio/x-wav"))
|
Expect(resp.Header.Get("Content-Type")).To(Or(Equal("audio/x-wav"), Equal("audio/vnd.wave")))
|
||||||
})
|
})
|
||||||
It("installs and is capable to generate images", Label("stablediffusion"), func() {
|
It("installs and is capable to generate images", Label("stablediffusion"), func() {
|
||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" {
|
||||||
@@ -772,14 +820,14 @@ var _ = Describe("API test", func() {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
bcl, ml, applicationConfig, err = startup.Startup(
|
application, err := application.New(
|
||||||
append(commonOpts,
|
append(commonOpts,
|
||||||
config.WithExternalBackend("huggingface", os.Getenv("HUGGINGFACE_GRPC")),
|
config.WithExternalBackend("huggingface", os.Getenv("HUGGINGFACE_GRPC")),
|
||||||
config.WithContext(c),
|
config.WithContext(c),
|
||||||
config.WithModelPath(modelPath),
|
config.WithModelPath(modelPath),
|
||||||
)...)
|
)...)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
app, err = App(bcl, ml, applicationConfig)
|
app, err = API(application)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
go app.Listen("127.0.0.1:9090")
|
go app.Listen("127.0.0.1:9090")
|
||||||
|
|
||||||
@@ -990,14 +1038,14 @@ var _ = Describe("API test", func() {
|
|||||||
c, cancel = context.WithCancel(context.Background())
|
c, cancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
bcl, ml, applicationConfig, err = startup.Startup(
|
application, err := application.New(
|
||||||
append(commonOpts,
|
append(commonOpts,
|
||||||
config.WithContext(c),
|
config.WithContext(c),
|
||||||
config.WithModelPath(modelPath),
|
config.WithModelPath(modelPath),
|
||||||
config.WithConfigFile(os.Getenv("CONFIG_FILE")))...,
|
config.WithConfigFile(os.Getenv("CONFIG_FILE")))...,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
app, err = App(bcl, ml, applicationConfig)
|
app, err = API(application)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
go app.Listen("127.0.0.1:9090")
|
go app.Listen("127.0.0.1:9090")
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ func ModelFromContext(ctx *fiber.Ctx, cl *config.BackendConfigLoader, loader *mo
|
|||||||
if ctx.Params("model") != "" {
|
if ctx.Params("model") != "" {
|
||||||
modelInput = ctx.Params("model")
|
modelInput = ctx.Params("model")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Query("model") != "" {
|
if ctx.Query("model") != "" {
|
||||||
modelInput = ctx.Query("model")
|
modelInput = ctx.Query("model")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set model from bearer token, if available
|
// Set model from bearer token, if available
|
||||||
bearer := strings.TrimLeft(ctx.Get("authorization"), "Bear ") // Reduced duplicate characters of Bearer
|
bearer := strings.TrimLeft(ctx.Get("authorization"), "Bear ") // Reduced duplicate characters of Bearer
|
||||||
bearerExists := bearer != "" && loader.ExistsInModelPath(bearer)
|
bearerExists := bearer != "" && loader.ExistsInModelPath(bearer)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func installButton(galleryName string) elem.Node {
|
|||||||
"class": "float-right inline-block rounded bg-primary px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
"class": "float-right inline-block rounded bg-primary px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong",
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
// post the Model ID as param
|
// post the Model ID as param
|
||||||
"hx-post": "/browse/install/model/" + galleryName,
|
"hx-post": "browse/install/model/" + galleryName,
|
||||||
},
|
},
|
||||||
elem.I(
|
elem.I(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
@@ -36,7 +36,7 @@ func reInstallButton(galleryName string) elem.Node {
|
|||||||
"hx-target": "#action-div-" + dropBadChars(galleryName),
|
"hx-target": "#action-div-" + dropBadChars(galleryName),
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
// post the Model ID as param
|
// post the Model ID as param
|
||||||
"hx-post": "/browse/install/model/" + galleryName,
|
"hx-post": "browse/install/model/" + galleryName,
|
||||||
},
|
},
|
||||||
elem.I(
|
elem.I(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
@@ -80,7 +80,7 @@ func deleteButton(galleryID string) elem.Node {
|
|||||||
"hx-target": "#action-div-" + dropBadChars(galleryID),
|
"hx-target": "#action-div-" + dropBadChars(galleryID),
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
// post the Model ID as param
|
// post the Model ID as param
|
||||||
"hx-post": "/browse/delete/model/" + galleryID,
|
"hx-post": "browse/delete/model/" + galleryID,
|
||||||
},
|
},
|
||||||
elem.I(
|
elem.I(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func searchableElement(text, icon string) elem.Node {
|
|||||||
// "value": text,
|
// "value": text,
|
||||||
//"class": "inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2",
|
//"class": "inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2",
|
||||||
"href": "#!",
|
"href": "#!",
|
||||||
"hx-post": "/browse/search/models",
|
"hx-post": "browse/search/models",
|
||||||
"hx-target": "#search-results",
|
"hx-target": "#search-results",
|
||||||
// TODO: this doesn't work
|
// TODO: this doesn't work
|
||||||
// "hx-vals": `{ \"search\": \"` + text + `\" }`,
|
// "hx-vals": `{ \"search\": \"` + text + `\" }`,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func StartProgressBar(uid, progress, text string) string {
|
|||||||
return elem.Div(
|
return elem.Div(
|
||||||
attrs.Props{
|
attrs.Props{
|
||||||
"hx-trigger": "done",
|
"hx-trigger": "done",
|
||||||
"hx-get": "/browse/job/" + uid,
|
"hx-get": "browse/job/" + uid,
|
||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
"hx-target": "this",
|
"hx-target": "this",
|
||||||
},
|
},
|
||||||
@@ -77,7 +77,7 @@ func StartProgressBar(uid, progress, text string) string {
|
|||||||
},
|
},
|
||||||
elem.Text(bluemonday.StrictPolicy().Sanitize(text)), //Perhaps overly defensive
|
elem.Text(bluemonday.StrictPolicy().Sanitize(text)), //Perhaps overly defensive
|
||||||
elem.Div(attrs.Props{
|
elem.Div(attrs.Props{
|
||||||
"hx-get": "/browse/job/progress/" + uid,
|
"hx-get": "browse/job/progress/" + uid,
|
||||||
"hx-trigger": "every 600ms",
|
"hx-trigger": "every 600ms",
|
||||||
"hx-target": "this",
|
"hx-target": "this",
|
||||||
"hx-swap": "innerHTML",
|
"hx-swap": "innerHTML",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/mudler/LocalAI/core/explorer"
|
"github.com/mudler/LocalAI/core/explorer"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
"github.com/mudler/LocalAI/internal"
|
"github.com/mudler/LocalAI/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ func Dashboard() func(*fiber.Ctx) error {
|
|||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI API - " + internal.PrintableVersion(),
|
"Title": "LocalAI API - " + internal.PrintableVersion(),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(c.Context().Request.Header.ContentType()) == "application/json" || len(c.Accepts("html")) == 0 {
|
if string(c.Context().Request.Header.ContentType()) == "application/json" || len(c.Accepts("html")) == 0 {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/gallery"
|
"github.com/mudler/LocalAI/core/gallery"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/mudler/LocalAI/core/services"
|
"github.com/mudler/LocalAI/core/services"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -82,7 +83,8 @@ func (mgs *ModelGalleryEndpointService) ApplyModelGalleryEndpoint() func(c *fibe
|
|||||||
Galleries: mgs.galleries,
|
Galleries: mgs.galleries,
|
||||||
ConfigURL: input.ConfigURL,
|
ConfigURL: input.ConfigURL,
|
||||||
}
|
}
|
||||||
return c.JSON(schema.GalleryResponse{ID: uuid.String(), StatusURL: c.BaseURL() + "/models/jobs/" + uuid.String()})
|
|
||||||
|
return c.JSON(schema.GalleryResponse{ID: uuid.String(), StatusURL: fmt.Sprintf("%smodels/jobs/%s", utils.BaseURL(c), uuid.String())})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ func (mgs *ModelGalleryEndpointService) DeleteModelGalleryEndpoint() func(c *fib
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(schema.GalleryResponse{ID: uuid.String(), StatusURL: c.BaseURL() + "/models/jobs/" + uuid.String()})
|
return c.JSON(schema.GalleryResponse{ID: uuid.String(), StatusURL: fmt.Sprintf("%smodels/jobs/%s", utils.BaseURL(c), uuid.String())})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/gallery"
|
"github.com/mudler/LocalAI/core/gallery"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
"github.com/mudler/LocalAI/core/p2p"
|
"github.com/mudler/LocalAI/core/p2p"
|
||||||
"github.com/mudler/LocalAI/core/services"
|
"github.com/mudler/LocalAI/core/services"
|
||||||
"github.com/mudler/LocalAI/internal"
|
"github.com/mudler/LocalAI/internal"
|
||||||
@@ -32,6 +33,7 @@ func WelcomeEndpoint(appConfig *config.ApplicationConfig,
|
|||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI API - " + internal.PrintableVersion(),
|
"Title": "LocalAI API - " + internal.PrintableVersion(),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"Models": modelsWithoutConfig,
|
"Models": modelsWithoutConfig,
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"GalleryConfig": galleryConfigs,
|
"GalleryConfig": galleryConfigs,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/mudler/LocalAI/pkg/functions"
|
"github.com/mudler/LocalAI/pkg/functions"
|
||||||
|
"github.com/mudler/LocalAI/pkg/templates"
|
||||||
|
|
||||||
model "github.com/mudler/LocalAI/pkg/model"
|
model "github.com/mudler/LocalAI/pkg/model"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
@@ -24,7 +26,7 @@ import (
|
|||||||
// @Param request body schema.OpenAIRequest true "query params"
|
// @Param request body schema.OpenAIRequest true "query params"
|
||||||
// @Success 200 {object} schema.OpenAIResponse "Response"
|
// @Success 200 {object} schema.OpenAIResponse "Response"
|
||||||
// @Router /v1/chat/completions [post]
|
// @Router /v1/chat/completions [post]
|
||||||
func ChatEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, startupOptions *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
func ChatEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, evaluator *templates.Evaluator, startupOptions *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
||||||
var id, textContentToReturn string
|
var id, textContentToReturn string
|
||||||
var created int
|
var created int
|
||||||
|
|
||||||
@@ -39,15 +41,11 @@ func ChatEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, startup
|
|||||||
responses <- initialMessage
|
responses <- initialMessage
|
||||||
|
|
||||||
ComputeChoices(req, s, config, startupOptions, loader, func(s string, c *[]schema.Choice) {}, func(s string, usage backend.TokenUsage) bool {
|
ComputeChoices(req, s, config, startupOptions, loader, func(s string, c *[]schema.Choice) {}, func(s string, usage backend.TokenUsage) bool {
|
||||||
choices := []schema.Choice{}
|
|
||||||
if s != "" {
|
|
||||||
choices = append(choices, schema.Choice{Delta: &schema.Message{Content: &s}, Index: 0})
|
|
||||||
}
|
|
||||||
resp := schema.OpenAIResponse{
|
resp := schema.OpenAIResponse{
|
||||||
ID: id,
|
ID: id,
|
||||||
Created: created,
|
Created: created,
|
||||||
Model: req.Model, // we have to return what the user sent here, due to OpenAI spec.
|
Model: req.Model, // we have to return what the user sent here, due to OpenAI spec.
|
||||||
Choices: choices,
|
Choices: []schema.Choice{{Delta: &schema.Message{Content: &s}, Index: 0}},
|
||||||
Object: "chat.completion.chunk",
|
Object: "chat.completion.chunk",
|
||||||
Usage: schema.OpenAIUsage{
|
Usage: schema.OpenAIUsage{
|
||||||
PromptTokens: usage.Prompt,
|
PromptTokens: usage.Prompt,
|
||||||
@@ -298,148 +296,10 @@ func ChatEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, startup
|
|||||||
// If we are using the tokenizer template, we don't need to process the messages
|
// If we are using the tokenizer template, we don't need to process the messages
|
||||||
// unless we are processing functions
|
// unless we are processing functions
|
||||||
if !config.TemplateConfig.UseTokenizerTemplate || shouldUseFn {
|
if !config.TemplateConfig.UseTokenizerTemplate || shouldUseFn {
|
||||||
suppressConfigSystemPrompt := false
|
predInput = evaluator.TemplateMessages(input.Messages, config, funcs, shouldUseFn)
|
||||||
mess := []string{}
|
|
||||||
for messageIndex, i := range input.Messages {
|
|
||||||
var content string
|
|
||||||
role := i.Role
|
|
||||||
|
|
||||||
// if function call, we might want to customize the role so we can display better that the "assistant called a json action"
|
|
||||||
// if an "assistant_function_call" role is defined, we use it, otherwise we use the role that is passed by in the request
|
|
||||||
if (i.FunctionCall != nil || i.ToolCalls != nil) && i.Role == "assistant" {
|
|
||||||
roleFn := "assistant_function_call"
|
|
||||||
r := config.Roles[roleFn]
|
|
||||||
if r != "" {
|
|
||||||
role = roleFn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r := config.Roles[role]
|
|
||||||
contentExists := i.Content != nil && i.StringContent != ""
|
|
||||||
|
|
||||||
fcall := i.FunctionCall
|
|
||||||
if len(i.ToolCalls) > 0 {
|
|
||||||
fcall = i.ToolCalls
|
|
||||||
}
|
|
||||||
|
|
||||||
// First attempt to populate content via a chat message specific template
|
|
||||||
if config.TemplateConfig.ChatMessage != "" {
|
|
||||||
chatMessageData := model.ChatMessageTemplateData{
|
|
||||||
SystemPrompt: config.SystemPrompt,
|
|
||||||
Role: r,
|
|
||||||
RoleName: role,
|
|
||||||
Content: i.StringContent,
|
|
||||||
FunctionCall: fcall,
|
|
||||||
FunctionName: i.Name,
|
|
||||||
LastMessage: messageIndex == (len(input.Messages) - 1),
|
|
||||||
Function: config.Grammar != "" && (messageIndex == (len(input.Messages) - 1)),
|
|
||||||
MessageIndex: messageIndex,
|
|
||||||
}
|
|
||||||
templatedChatMessage, err := ml.EvaluateTemplateForChatMessage(config.TemplateConfig.ChatMessage, chatMessageData)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Interface("message", chatMessageData).Str("template", config.TemplateConfig.ChatMessage).Msg("error processing message with template, skipping")
|
|
||||||
} else {
|
|
||||||
if templatedChatMessage == "" {
|
|
||||||
log.Warn().Msgf("template \"%s\" produced blank output for %+v. Skipping!", config.TemplateConfig.ChatMessage, chatMessageData)
|
|
||||||
continue // TODO: This continue is here intentionally to skip over the line `mess = append(mess, content)` below, and to prevent the sprintf
|
|
||||||
}
|
|
||||||
log.Debug().Msgf("templated message for chat: %s", templatedChatMessage)
|
|
||||||
content = templatedChatMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
marshalAnyRole := func(f any) {
|
|
||||||
j, err := json.Marshal(f)
|
|
||||||
if err == nil {
|
|
||||||
if contentExists {
|
|
||||||
content += "\n" + fmt.Sprint(r, " ", string(j))
|
|
||||||
} else {
|
|
||||||
content = fmt.Sprint(r, " ", string(j))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
marshalAny := func(f any) {
|
|
||||||
j, err := json.Marshal(f)
|
|
||||||
if err == nil {
|
|
||||||
if contentExists {
|
|
||||||
content += "\n" + string(j)
|
|
||||||
} else {
|
|
||||||
content = string(j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If this model doesn't have such a template, or if that template fails to return a value, template at the message level.
|
|
||||||
if content == "" {
|
|
||||||
if r != "" {
|
|
||||||
if contentExists {
|
|
||||||
content = fmt.Sprint(r, i.StringContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.FunctionCall != nil {
|
|
||||||
marshalAnyRole(i.FunctionCall)
|
|
||||||
}
|
|
||||||
if i.ToolCalls != nil {
|
|
||||||
marshalAnyRole(i.ToolCalls)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if contentExists {
|
|
||||||
content = fmt.Sprint(i.StringContent)
|
|
||||||
}
|
|
||||||
if i.FunctionCall != nil {
|
|
||||||
marshalAny(i.FunctionCall)
|
|
||||||
}
|
|
||||||
if i.ToolCalls != nil {
|
|
||||||
marshalAny(i.ToolCalls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Special Handling: System. We care if it was printed at all, not the r branch, so check seperately
|
|
||||||
if contentExists && role == "system" {
|
|
||||||
suppressConfigSystemPrompt = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mess = append(mess, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
joinCharacter := "\n"
|
|
||||||
if config.TemplateConfig.JoinChatMessagesByCharacter != nil {
|
|
||||||
joinCharacter = *config.TemplateConfig.JoinChatMessagesByCharacter
|
|
||||||
}
|
|
||||||
|
|
||||||
predInput = strings.Join(mess, joinCharacter)
|
|
||||||
log.Debug().Msgf("Prompt (before templating): %s", predInput)
|
|
||||||
|
|
||||||
templateFile := ""
|
|
||||||
|
|
||||||
// A model can have a "file.bin.tmpl" file associated with a prompt template prefix
|
|
||||||
if ml.ExistsInModelPath(fmt.Sprintf("%s.tmpl", config.Model)) {
|
|
||||||
templateFile = config.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.TemplateConfig.Chat != "" && !shouldUseFn {
|
|
||||||
templateFile = config.TemplateConfig.Chat
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.TemplateConfig.Functions != "" && shouldUseFn {
|
|
||||||
templateFile = config.TemplateConfig.Functions
|
|
||||||
}
|
|
||||||
|
|
||||||
if templateFile != "" {
|
|
||||||
templatedInput, err := ml.EvaluateTemplateForPrompt(model.ChatPromptTemplate, templateFile, model.PromptTemplateData{
|
|
||||||
SystemPrompt: config.SystemPrompt,
|
|
||||||
SuppressSystemPrompt: suppressConfigSystemPrompt,
|
|
||||||
Input: predInput,
|
|
||||||
Functions: funcs,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
predInput = templatedInput
|
|
||||||
log.Debug().Msgf("Template found, input modified to: %s", predInput)
|
|
||||||
} else {
|
|
||||||
log.Debug().Msgf("Template failed loading: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug().Msgf("Prompt (after templating): %s", predInput)
|
log.Debug().Msgf("Prompt (after templating): %s", predInput)
|
||||||
if shouldUseFn && config.Grammar != "" {
|
if config.Grammar != "" {
|
||||||
log.Debug().Msgf("Grammar: %+v", config.Grammar)
|
log.Debug().Msgf("Grammar: %+v", config.Grammar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,9 +329,6 @@ func ChatEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, startup
|
|||||||
toolsCalled := false
|
toolsCalled := false
|
||||||
for ev := range responses {
|
for ev := range responses {
|
||||||
usage = &ev.Usage // Copy a pointer to the latest usage chunk so that the stop message can reference it
|
usage = &ev.Usage // Copy a pointer to the latest usage chunk so that the stop message can reference it
|
||||||
if len(ev.Choices) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if len(ev.Choices[0].Delta.ToolCalls) > 0 {
|
if len(ev.Choices[0].Delta.ToolCalls) > 0 {
|
||||||
toolsCalled = true
|
toolsCalled = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/mudler/LocalAI/pkg/functions"
|
"github.com/mudler/LocalAI/pkg/functions"
|
||||||
model "github.com/mudler/LocalAI/pkg/model"
|
model "github.com/mudler/LocalAI/pkg/model"
|
||||||
|
"github.com/mudler/LocalAI/pkg/templates"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
@@ -25,7 +26,7 @@ import (
|
|||||||
// @Param request body schema.OpenAIRequest true "query params"
|
// @Param request body schema.OpenAIRequest true "query params"
|
||||||
// @Success 200 {object} schema.OpenAIResponse "Response"
|
// @Success 200 {object} schema.OpenAIResponse "Response"
|
||||||
// @Router /v1/completions [post]
|
// @Router /v1/completions [post]
|
||||||
func CompletionEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
func CompletionEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, evaluator *templates.Evaluator, appConfig *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
created := int(time.Now().Unix())
|
created := int(time.Now().Unix())
|
||||||
|
|
||||||
@@ -94,17 +95,6 @@ func CompletionEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, a
|
|||||||
c.Set("Transfer-Encoding", "chunked")
|
c.Set("Transfer-Encoding", "chunked")
|
||||||
}
|
}
|
||||||
|
|
||||||
templateFile := ""
|
|
||||||
|
|
||||||
// A model can have a "file.bin.tmpl" file associated with a prompt template prefix
|
|
||||||
if ml.ExistsInModelPath(fmt.Sprintf("%s.tmpl", config.Model)) {
|
|
||||||
templateFile = config.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.TemplateConfig.Completion != "" {
|
|
||||||
templateFile = config.TemplateConfig.Completion
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.Stream {
|
if input.Stream {
|
||||||
if len(config.PromptStrings) > 1 {
|
if len(config.PromptStrings) > 1 {
|
||||||
return errors.New("cannot handle more than 1 `PromptStrings` when Streaming")
|
return errors.New("cannot handle more than 1 `PromptStrings` when Streaming")
|
||||||
@@ -112,15 +102,13 @@ func CompletionEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, a
|
|||||||
|
|
||||||
predInput := config.PromptStrings[0]
|
predInput := config.PromptStrings[0]
|
||||||
|
|
||||||
if templateFile != "" {
|
templatedInput, err := evaluator.EvaluateTemplateForPrompt(templates.CompletionPromptTemplate, *config, templates.PromptTemplateData{
|
||||||
templatedInput, err := ml.EvaluateTemplateForPrompt(model.CompletionPromptTemplate, templateFile, model.PromptTemplateData{
|
Input: predInput,
|
||||||
Input: predInput,
|
SystemPrompt: config.SystemPrompt,
|
||||||
SystemPrompt: config.SystemPrompt,
|
})
|
||||||
})
|
if err == nil {
|
||||||
if err == nil {
|
predInput = templatedInput
|
||||||
predInput = templatedInput
|
log.Debug().Msgf("Template found, input modified to: %s", predInput)
|
||||||
log.Debug().Msgf("Template found, input modified to: %s", predInput)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
responses := make(chan schema.OpenAIResponse)
|
responses := make(chan schema.OpenAIResponse)
|
||||||
@@ -165,16 +153,13 @@ func CompletionEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, a
|
|||||||
totalTokenUsage := backend.TokenUsage{}
|
totalTokenUsage := backend.TokenUsage{}
|
||||||
|
|
||||||
for k, i := range config.PromptStrings {
|
for k, i := range config.PromptStrings {
|
||||||
if templateFile != "" {
|
templatedInput, err := evaluator.EvaluateTemplateForPrompt(templates.CompletionPromptTemplate, *config, templates.PromptTemplateData{
|
||||||
// A model can have a "file.bin.tmpl" file associated with a prompt template prefix
|
SystemPrompt: config.SystemPrompt,
|
||||||
templatedInput, err := ml.EvaluateTemplateForPrompt(model.CompletionPromptTemplate, templateFile, model.PromptTemplateData{
|
Input: i,
|
||||||
SystemPrompt: config.SystemPrompt,
|
})
|
||||||
Input: i,
|
if err == nil {
|
||||||
})
|
i = templatedInput
|
||||||
if err == nil {
|
log.Debug().Msgf("Template found, input modified to: %s", i)
|
||||||
i = templatedInput
|
|
||||||
log.Debug().Msgf("Template found, input modified to: %s", i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r, tokenUsage, err := ComputeChoices(
|
r, tokenUsage, err := ComputeChoices(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
model "github.com/mudler/LocalAI/pkg/model"
|
model "github.com/mudler/LocalAI/pkg/model"
|
||||||
|
"github.com/mudler/LocalAI/pkg/templates"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -21,7 +22,8 @@ import (
|
|||||||
// @Param request body schema.OpenAIRequest true "query params"
|
// @Param request body schema.OpenAIRequest true "query params"
|
||||||
// @Success 200 {object} schema.OpenAIResponse "Response"
|
// @Success 200 {object} schema.OpenAIResponse "Response"
|
||||||
// @Router /v1/edits [post]
|
// @Router /v1/edits [post]
|
||||||
func EditEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
func EditEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, evaluator *templates.Evaluator, appConfig *config.ApplicationConfig) func(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
modelFile, input, err := readRequest(c, cl, ml, appConfig, true)
|
modelFile, input, err := readRequest(c, cl, ml, appConfig, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -35,31 +37,18 @@ func EditEndpoint(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConf
|
|||||||
|
|
||||||
log.Debug().Msgf("Parameter Config: %+v", config)
|
log.Debug().Msgf("Parameter Config: %+v", config)
|
||||||
|
|
||||||
templateFile := ""
|
|
||||||
|
|
||||||
// A model can have a "file.bin.tmpl" file associated with a prompt template prefix
|
|
||||||
if ml.ExistsInModelPath(fmt.Sprintf("%s.tmpl", config.Model)) {
|
|
||||||
templateFile = config.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.TemplateConfig.Edit != "" {
|
|
||||||
templateFile = config.TemplateConfig.Edit
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []schema.Choice
|
var result []schema.Choice
|
||||||
totalTokenUsage := backend.TokenUsage{}
|
totalTokenUsage := backend.TokenUsage{}
|
||||||
|
|
||||||
for _, i := range config.InputStrings {
|
for _, i := range config.InputStrings {
|
||||||
if templateFile != "" {
|
templatedInput, err := evaluator.EvaluateTemplateForPrompt(templates.EditPromptTemplate, *config, templates.PromptTemplateData{
|
||||||
templatedInput, err := ml.EvaluateTemplateForPrompt(model.EditPromptTemplate, templateFile, model.PromptTemplateData{
|
Input: i,
|
||||||
Input: i,
|
Instruction: input.Instruction,
|
||||||
Instruction: input.Instruction,
|
SystemPrompt: config.SystemPrompt,
|
||||||
SystemPrompt: config.SystemPrompt,
|
})
|
||||||
})
|
if err == nil {
|
||||||
if err == nil {
|
i = templatedInput
|
||||||
i = templatedInput
|
log.Debug().Msgf("Template found, input modified to: %s", i)
|
||||||
log.Debug().Msgf("Template found, input modified to: %s", i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r, tokenUsage, err := ComputeChoices(input, i, config, appConfig, ml, func(s string, c *[]schema.Choice) {
|
r, tokenUsage, err := ComputeChoices(input, i, config, appConfig, ml, func(s string, c *[]schema.Choice) {
|
||||||
|
|||||||
1136
core/http/endpoints/openai/realtime.go
Normal file
1136
core/http/endpoints/openai/realtime.go
Normal file
File diff suppressed because it is too large
Load Diff
186
core/http/endpoints/openai/realtime_model.go
Normal file
186
core/http/endpoints/openai/realtime_model.go
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
package openai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/mudler/LocalAI/core/backend"
|
||||||
|
"github.com/mudler/LocalAI/core/config"
|
||||||
|
grpcClient "github.com/mudler/LocalAI/pkg/grpc"
|
||||||
|
"github.com/mudler/LocalAI/pkg/grpc/proto"
|
||||||
|
model "github.com/mudler/LocalAI/pkg/model"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Model = new(wrappedModel)
|
||||||
|
_ Model = new(anyToAnyModel)
|
||||||
|
)
|
||||||
|
|
||||||
|
// wrappedModel represent a model which does not support Any-to-Any operations
|
||||||
|
// This means that we will fake an Any-to-Any model by overriding some of the gRPC client methods
|
||||||
|
// which are for Any-To-Any models, but instead we will call a pipeline (for e.g STT->LLM->TTS)
|
||||||
|
type wrappedModel struct {
|
||||||
|
TTSConfig *config.BackendConfig
|
||||||
|
TranscriptionConfig *config.BackendConfig
|
||||||
|
LLMConfig *config.BackendConfig
|
||||||
|
TTSClient grpcClient.Backend
|
||||||
|
TranscriptionClient grpcClient.Backend
|
||||||
|
LLMClient grpcClient.Backend
|
||||||
|
|
||||||
|
VADConfig *config.BackendConfig
|
||||||
|
VADClient grpcClient.Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
// anyToAnyModel represent a model which supports Any-to-Any operations
|
||||||
|
// We have to wrap this out as well because we want to load two models one for VAD and one for the actual model.
|
||||||
|
// In the future there could be models that accept continous audio input only so this design will be useful for that
|
||||||
|
type anyToAnyModel struct {
|
||||||
|
LLMConfig *config.BackendConfig
|
||||||
|
LLMClient grpcClient.Backend
|
||||||
|
|
||||||
|
VADConfig *config.BackendConfig
|
||||||
|
VADClient grpcClient.Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wrappedModel) VAD(ctx context.Context, in *proto.VADRequest, opts ...grpc.CallOption) (*proto.VADResponse, error) {
|
||||||
|
return m.VADClient.VAD(ctx, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *anyToAnyModel) VAD(ctx context.Context, in *proto.VADRequest, opts ...grpc.CallOption) (*proto.VADResponse, error) {
|
||||||
|
return m.VADClient.VAD(ctx, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wrappedModel) Predict(ctx context.Context, in *proto.PredictOptions, opts ...grpc.CallOption) (*proto.Reply, error) {
|
||||||
|
// TODO: Convert with pipeline (audio to text, text to llm, result to tts, and return it)
|
||||||
|
// sound.BufferAsWAV(audioData, "audio.wav")
|
||||||
|
|
||||||
|
return m.LLMClient.Predict(ctx, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wrappedModel) PredictStream(ctx context.Context, in *proto.PredictOptions, f func(reply *proto.Reply), opts ...grpc.CallOption) error {
|
||||||
|
// TODO: Convert with pipeline (audio to text, text to llm, result to tts, and return it)
|
||||||
|
|
||||||
|
return m.LLMClient.PredictStream(ctx, in, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *anyToAnyModel) Predict(ctx context.Context, in *proto.PredictOptions, opts ...grpc.CallOption) (*proto.Reply, error) {
|
||||||
|
return m.LLMClient.Predict(ctx, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *anyToAnyModel) PredictStream(ctx context.Context, in *proto.PredictOptions, f func(reply *proto.Reply), opts ...grpc.CallOption) error {
|
||||||
|
return m.LLMClient.PredictStream(ctx, in, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns and loads either a wrapped model or a model that support audio-to-audio
|
||||||
|
func newModel(cfg *config.BackendConfig, cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig, modelName string) (Model, error) {
|
||||||
|
|
||||||
|
// Prepare VAD model
|
||||||
|
cfgVAD, err := cl.LoadBackendConfigFileByName(cfg.Pipeline.VAD, ml.ModelPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to load backend config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfgVAD.Validate() {
|
||||||
|
return nil, fmt.Errorf("failed to validate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := backend.ModelOptions(*cfgVAD, appConfig)
|
||||||
|
VADClient, err := ml.Load(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load tts model: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have Wrapped model definitions, just return a standard model
|
||||||
|
if cfg.Pipeline.IsNotConfigured() {
|
||||||
|
|
||||||
|
// Otherwise we want to return a wrapped model, which is a "virtual" model that re-uses other models to perform operations
|
||||||
|
cfgAnyToAny, err := cl.LoadBackendConfigFileByName(cfg.Model, ml.ModelPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to load backend config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfgAnyToAny.Validate() {
|
||||||
|
return nil, fmt.Errorf("failed to validate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := backend.ModelOptions(*cfgAnyToAny, appConfig)
|
||||||
|
anyToAnyClient, err := ml.Load(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load tts model: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &anyToAnyModel{
|
||||||
|
LLMConfig: cfgAnyToAny,
|
||||||
|
LLMClient: anyToAnyClient,
|
||||||
|
VADConfig: cfgVAD,
|
||||||
|
VADClient: VADClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msg("Loading a wrapped model")
|
||||||
|
|
||||||
|
// Otherwise we want to return a wrapped model, which is a "virtual" model that re-uses other models to perform operations
|
||||||
|
cfgLLM, err := cl.LoadBackendConfigFileByName(cfg.Pipeline.LLM, ml.ModelPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to load backend config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfgLLM.Validate() {
|
||||||
|
return nil, fmt.Errorf("failed to validate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgTTS, err := cl.LoadBackendConfigFileByName(cfg.Pipeline.TTS, ml.ModelPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to load backend config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfgTTS.Validate() {
|
||||||
|
return nil, fmt.Errorf("failed to validate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgSST, err := cl.LoadBackendConfigFileByName(cfg.Pipeline.Transcription, ml.ModelPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to load backend config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfgSST.Validate() {
|
||||||
|
return nil, fmt.Errorf("failed to validate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = backend.ModelOptions(*cfgTTS, appConfig)
|
||||||
|
ttsClient, err := ml.Load(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load tts model: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = backend.ModelOptions(*cfgSST, appConfig)
|
||||||
|
transcriptionClient, err := ml.Load(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load SST model: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = backend.ModelOptions(*cfgLLM, appConfig)
|
||||||
|
llmClient, err := ml.Load(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load LLM model: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wrappedModel{
|
||||||
|
TTSConfig: cfgTTS,
|
||||||
|
TranscriptionConfig: cfgSST,
|
||||||
|
LLMConfig: cfgLLM,
|
||||||
|
TTSClient: ttsClient,
|
||||||
|
TranscriptionClient: transcriptionClient,
|
||||||
|
LLMClient: llmClient,
|
||||||
|
|
||||||
|
VADConfig: cfgVAD,
|
||||||
|
VADClient: VADClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -48,6 +48,25 @@ func readRequest(c *fiber.Ctx, cl *config.BackendConfigLoader, ml *model.ModelLo
|
|||||||
return modelFile, input, err
|
return modelFile, input, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func readWSRequest(c *websocket.Conn, cl *config.BackendConfigLoader, ml *model.ModelLoader, o *config.ApplicationConfig, firstModel bool) (string, *schema.OpenAIRequest, error) {
|
||||||
|
// input := new(schema.OpenAIRequest)
|
||||||
|
|
||||||
|
// input.Model = c.Query("name")
|
||||||
|
|
||||||
|
// received, _ := json.Marshal(input)
|
||||||
|
|
||||||
|
// ctx, cancel := context.WithCancel(o.Context)
|
||||||
|
|
||||||
|
// input.Context = ctx
|
||||||
|
// input.Cancel = cancel
|
||||||
|
|
||||||
|
// log.Debug().Msgf("Request received: %s", string(received))
|
||||||
|
|
||||||
|
// modelFile, err := fiberContext.ModelFromContext(c, cl, ml, input.Model, firstModel)
|
||||||
|
|
||||||
|
// return modelFile, input, err
|
||||||
|
// }
|
||||||
|
|
||||||
func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIRequest) {
|
func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIRequest) {
|
||||||
if input.Echo {
|
if input.Echo {
|
||||||
config.Echo = input.Echo
|
config.Echo = input.Echo
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2/middleware/favicon"
|
"github.com/gofiber/fiber/v2/middleware/favicon"
|
||||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||||
"github.com/mudler/LocalAI/core/explorer"
|
"github.com/mudler/LocalAI/core/explorer"
|
||||||
|
"github.com/mudler/LocalAI/core/http/middleware"
|
||||||
"github.com/mudler/LocalAI/core/http/routes"
|
"github.com/mudler/LocalAI/core/http/routes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ func Explorer(db *explorer.Database) *fiber.App {
|
|||||||
|
|
||||||
app := fiber.New(fiberCfg)
|
app := fiber.New(fiberCfg)
|
||||||
|
|
||||||
|
app.Use(middleware.StripPathPrefix())
|
||||||
routes.RegisterExplorerRoutes(app, db)
|
routes.RegisterExplorerRoutes(app, db)
|
||||||
|
|
||||||
httpFS := http.FS(embedDirStatic)
|
httpFS := http.FS(embedDirStatic)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/keyauth"
|
"github.com/gofiber/fiber/v2/middleware/keyauth"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains the configuration generators and handler functions that are used along with the fiber/keyauth middleware
|
// This file contains the configuration generators and handler functions that are used along with the fiber/keyauth middleware
|
||||||
@@ -39,7 +40,9 @@ func getApiKeyErrorHandler(applicationConfig *config.ApplicationConfig) fiber.Er
|
|||||||
if applicationConfig.OpaqueErrors {
|
if applicationConfig.OpaqueErrors {
|
||||||
return ctx.SendStatus(401)
|
return ctx.SendStatus(401)
|
||||||
}
|
}
|
||||||
return ctx.Status(401).Render("views/login", nil)
|
return ctx.Status(401).Render("views/login", fiber.Map{
|
||||||
|
"BaseURL": utils.BaseURL(ctx),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if applicationConfig.OpaqueErrors {
|
if applicationConfig.OpaqueErrors {
|
||||||
return ctx.SendStatus(500)
|
return ctx.SendStatus(500)
|
||||||
|
|||||||
36
core/http/middleware/strippathprefix.go
Normal file
36
core/http/middleware/strippathprefix.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StripPathPrefix returns a middleware that strips a path prefix from the request path.
|
||||||
|
// The path prefix is obtained from the X-Forwarded-Prefix HTTP request header.
|
||||||
|
func StripPathPrefix() fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
for _, prefix := range c.GetReqHeaders()["X-Forwarded-Prefix"] {
|
||||||
|
if prefix != "" {
|
||||||
|
path := c.Path()
|
||||||
|
pos := len(prefix)
|
||||||
|
|
||||||
|
if prefix[pos-1] == '/' {
|
||||||
|
pos--
|
||||||
|
} else {
|
||||||
|
prefix += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(path, prefix) {
|
||||||
|
c.Path(path[pos:])
|
||||||
|
break
|
||||||
|
} else if prefix[:pos] == path {
|
||||||
|
c.Redirect(prefix)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
121
core/http/middleware/strippathprefix_test.go
Normal file
121
core/http/middleware/strippathprefix_test.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStripPathPrefix(t *testing.T) {
|
||||||
|
var actualPath string
|
||||||
|
|
||||||
|
app := fiber.New()
|
||||||
|
|
||||||
|
app.Use(StripPathPrefix())
|
||||||
|
|
||||||
|
app.Get("/hello/world", func(c *fiber.Ctx) error {
|
||||||
|
actualPath = c.Path()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
actualPath = c.Path()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
prefixHeader []string
|
||||||
|
expectStatus int
|
||||||
|
expectPath string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "without prefix and header",
|
||||||
|
path: "/hello/world",
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "without prefix and headers on root path",
|
||||||
|
path: "/",
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "without prefix but header",
|
||||||
|
path: "/hello/world",
|
||||||
|
prefixHeader: []string{"/otherprefix/"},
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix but non-matching header",
|
||||||
|
path: "/prefix/hello/world",
|
||||||
|
prefixHeader: []string{"/otherprefix/"},
|
||||||
|
expectStatus: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix and matching header",
|
||||||
|
path: "/myprefix/hello/world",
|
||||||
|
prefixHeader: []string{"/myprefix/"},
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix and 1st header matching",
|
||||||
|
path: "/myprefix/hello/world",
|
||||||
|
prefixHeader: []string{"/myprefix/", "/otherprefix/"},
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix and 2nd header matching",
|
||||||
|
path: "/myprefix/hello/world",
|
||||||
|
prefixHeader: []string{"/otherprefix/", "/myprefix/"},
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix and header not ending with slash",
|
||||||
|
path: "/myprefix/hello/world",
|
||||||
|
prefixHeader: []string{"/myprefix"},
|
||||||
|
expectStatus: 200,
|
||||||
|
expectPath: "/hello/world",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix and non-matching header not ending with slash",
|
||||||
|
path: "/myprefix-suffix/hello/world",
|
||||||
|
prefixHeader: []string{"/myprefix"},
|
||||||
|
expectStatus: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "redirect when prefix does not end with a slash",
|
||||||
|
path: "/myprefix",
|
||||||
|
prefixHeader: []string{"/myprefix"},
|
||||||
|
expectStatus: 302,
|
||||||
|
expectPath: "/myprefix/",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actualPath = ""
|
||||||
|
req := httptest.NewRequest("GET", tc.path, nil)
|
||||||
|
if tc.prefixHeader != nil {
|
||||||
|
req.Header["X-Forwarded-Prefix"] = tc.prefixHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := app.Test(req, -1)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectStatus, resp.StatusCode, "response status code")
|
||||||
|
|
||||||
|
if tc.expectStatus == 200 {
|
||||||
|
require.Equal(t, tc.expectPath, actualPath, "rewritten path")
|
||||||
|
} else if tc.expectStatus == 302 {
|
||||||
|
require.Equal(t, tc.expectPath, resp.Header.Get("Location"), "redirect location")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
fiberhtml "github.com/gofiber/template/html/v2"
|
fiberhtml "github.com/gofiber/template/html/v2"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
"github.com/mudler/LocalAI/core/schema"
|
"github.com/mudler/LocalAI/core/schema"
|
||||||
"github.com/russross/blackfriday"
|
"github.com/russross/blackfriday"
|
||||||
)
|
)
|
||||||
@@ -26,7 +27,9 @@ func notFoundHandler(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// The client expects an HTML response
|
// The client expects an HTML response
|
||||||
return c.Status(fiber.StatusNotFound).Render("views/404", fiber.Map{})
|
return c.Status(fiber.StatusNotFound).Render("views/404", fiber.Map{
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,62 +11,62 @@ import (
|
|||||||
"github.com/mudler/LocalAI/pkg/model"
|
"github.com/mudler/LocalAI/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterLocalAIRoutes(app *fiber.App,
|
func RegisterLocalAIRoutes(router *fiber.App,
|
||||||
cl *config.BackendConfigLoader,
|
cl *config.BackendConfigLoader,
|
||||||
ml *model.ModelLoader,
|
ml *model.ModelLoader,
|
||||||
appConfig *config.ApplicationConfig,
|
appConfig *config.ApplicationConfig,
|
||||||
galleryService *services.GalleryService) {
|
galleryService *services.GalleryService) {
|
||||||
|
|
||||||
app.Get("/swagger/*", swagger.HandlerDefault) // default
|
router.Get("/swagger/*", swagger.HandlerDefault) // default
|
||||||
|
|
||||||
// LocalAI API endpoints
|
// LocalAI API endpoints
|
||||||
if !appConfig.DisableGalleryEndpoint {
|
if !appConfig.DisableGalleryEndpoint {
|
||||||
modelGalleryEndpointService := localai.CreateModelGalleryEndpointService(appConfig.Galleries, appConfig.ModelPath, galleryService)
|
modelGalleryEndpointService := localai.CreateModelGalleryEndpointService(appConfig.Galleries, appConfig.ModelPath, galleryService)
|
||||||
app.Post("/models/apply", modelGalleryEndpointService.ApplyModelGalleryEndpoint())
|
router.Post("/models/apply", modelGalleryEndpointService.ApplyModelGalleryEndpoint())
|
||||||
app.Post("/models/delete/:name", modelGalleryEndpointService.DeleteModelGalleryEndpoint())
|
router.Post("/models/delete/:name", modelGalleryEndpointService.DeleteModelGalleryEndpoint())
|
||||||
|
|
||||||
app.Get("/models/available", modelGalleryEndpointService.ListModelFromGalleryEndpoint())
|
router.Get("/models/available", modelGalleryEndpointService.ListModelFromGalleryEndpoint())
|
||||||
app.Get("/models/galleries", modelGalleryEndpointService.ListModelGalleriesEndpoint())
|
router.Get("/models/galleries", modelGalleryEndpointService.ListModelGalleriesEndpoint())
|
||||||
app.Post("/models/galleries", modelGalleryEndpointService.AddModelGalleryEndpoint())
|
router.Post("/models/galleries", modelGalleryEndpointService.AddModelGalleryEndpoint())
|
||||||
app.Delete("/models/galleries", modelGalleryEndpointService.RemoveModelGalleryEndpoint())
|
router.Delete("/models/galleries", modelGalleryEndpointService.RemoveModelGalleryEndpoint())
|
||||||
app.Get("/models/jobs/:uuid", modelGalleryEndpointService.GetOpStatusEndpoint())
|
router.Get("/models/jobs/:uuid", modelGalleryEndpointService.GetOpStatusEndpoint())
|
||||||
app.Get("/models/jobs", modelGalleryEndpointService.GetAllStatusEndpoint())
|
router.Get("/models/jobs", modelGalleryEndpointService.GetAllStatusEndpoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Post("/tts", localai.TTSEndpoint(cl, ml, appConfig))
|
router.Post("/tts", localai.TTSEndpoint(cl, ml, appConfig))
|
||||||
app.Post("/vad", localai.VADEndpoint(cl, ml, appConfig))
|
router.Post("/vad", localai.VADEndpoint(cl, ml, appConfig))
|
||||||
|
|
||||||
// Stores
|
// Stores
|
||||||
sl := model.NewModelLoader("")
|
sl := model.NewModelLoader("")
|
||||||
app.Post("/stores/set", localai.StoresSetEndpoint(sl, appConfig))
|
router.Post("/stores/set", localai.StoresSetEndpoint(sl, appConfig))
|
||||||
app.Post("/stores/delete", localai.StoresDeleteEndpoint(sl, appConfig))
|
router.Post("/stores/delete", localai.StoresDeleteEndpoint(sl, appConfig))
|
||||||
app.Post("/stores/get", localai.StoresGetEndpoint(sl, appConfig))
|
router.Post("/stores/get", localai.StoresGetEndpoint(sl, appConfig))
|
||||||
app.Post("/stores/find", localai.StoresFindEndpoint(sl, appConfig))
|
router.Post("/stores/find", localai.StoresFindEndpoint(sl, appConfig))
|
||||||
|
|
||||||
if !appConfig.DisableMetrics {
|
if !appConfig.DisableMetrics {
|
||||||
app.Get("/metrics", localai.LocalAIMetricsEndpoint())
|
router.Get("/metrics", localai.LocalAIMetricsEndpoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental Backend Statistics Module
|
// Experimental Backend Statistics Module
|
||||||
backendMonitorService := services.NewBackendMonitorService(ml, cl, appConfig) // Split out for now
|
backendMonitorService := services.NewBackendMonitorService(ml, cl, appConfig) // Split out for now
|
||||||
app.Get("/backend/monitor", localai.BackendMonitorEndpoint(backendMonitorService))
|
router.Get("/backend/monitor", localai.BackendMonitorEndpoint(backendMonitorService))
|
||||||
app.Post("/backend/shutdown", localai.BackendShutdownEndpoint(backendMonitorService))
|
router.Post("/backend/shutdown", localai.BackendShutdownEndpoint(backendMonitorService))
|
||||||
|
|
||||||
// p2p
|
// p2p
|
||||||
if p2p.IsP2PEnabled() {
|
if p2p.IsP2PEnabled() {
|
||||||
app.Get("/api/p2p", localai.ShowP2PNodes(appConfig))
|
router.Get("/api/p2p", localai.ShowP2PNodes(appConfig))
|
||||||
app.Get("/api/p2p/token", localai.ShowP2PToken(appConfig))
|
router.Get("/api/p2p/token", localai.ShowP2PToken(appConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Get("/version", func(c *fiber.Ctx) error {
|
router.Get("/version", func(c *fiber.Ctx) error {
|
||||||
return c.JSON(struct {
|
return c.JSON(struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}{Version: internal.PrintableVersion()})
|
}{Version: internal.PrintableVersion()})
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Get("/system", localai.SystemInformations(ml, appConfig))
|
router.Get("/system", localai.SystemInformations(ml, appConfig))
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
app.Post("/v1/tokenize", localai.TokenizeEndpoint(cl, ml, appConfig))
|
router.Post("/v1/tokenize", localai.TokenizeEndpoint(cl, ml, appConfig))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,84 +2,137 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/application"
|
||||||
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
||||||
"github.com/mudler/LocalAI/core/http/endpoints/openai"
|
"github.com/mudler/LocalAI/core/http/endpoints/openai"
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterOpenAIRoutes(app *fiber.App,
|
func RegisterOpenAIRoutes(app *fiber.App,
|
||||||
cl *config.BackendConfigLoader,
|
application *application.Application) {
|
||||||
ml *model.ModelLoader,
|
|
||||||
appConfig *config.ApplicationConfig) {
|
|
||||||
// openAI compatible API endpoint
|
// openAI compatible API endpoint
|
||||||
|
|
||||||
|
// realtime
|
||||||
|
app.Get("/v1/realtime", openai.Realtime(application))
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
app.Post("/v1/chat/completions", openai.ChatEndpoint(cl, ml, appConfig))
|
app.Post("/v1/chat/completions",
|
||||||
app.Post("/chat/completions", openai.ChatEndpoint(cl, ml, appConfig))
|
openai.ChatEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
app.Post("/chat/completions",
|
||||||
|
openai.ChatEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// edit
|
// edit
|
||||||
app.Post("/v1/edits", openai.EditEndpoint(cl, ml, appConfig))
|
app.Post("/v1/edits",
|
||||||
app.Post("/edits", openai.EditEndpoint(cl, ml, appConfig))
|
openai.EditEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
app.Post("/edits",
|
||||||
|
openai.EditEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// assistant
|
// assistant
|
||||||
app.Get("/v1/assistants", openai.ListAssistantsEndpoint(cl, ml, appConfig))
|
app.Get("/v1/assistants", openai.ListAssistantsEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/assistants", openai.ListAssistantsEndpoint(cl, ml, appConfig))
|
app.Get("/assistants", openai.ListAssistantsEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/v1/assistants", openai.CreateAssistantEndpoint(cl, ml, appConfig))
|
app.Post("/v1/assistants", openai.CreateAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/assistants", openai.CreateAssistantEndpoint(cl, ml, appConfig))
|
app.Post("/assistants", openai.CreateAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/v1/assistants/:assistant_id", openai.DeleteAssistantEndpoint(cl, ml, appConfig))
|
app.Delete("/v1/assistants/:assistant_id", openai.DeleteAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/assistants/:assistant_id", openai.DeleteAssistantEndpoint(cl, ml, appConfig))
|
app.Delete("/assistants/:assistant_id", openai.DeleteAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/assistants/:assistant_id", openai.GetAssistantEndpoint(cl, ml, appConfig))
|
app.Get("/v1/assistants/:assistant_id", openai.GetAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/assistants/:assistant_id", openai.GetAssistantEndpoint(cl, ml, appConfig))
|
app.Get("/assistants/:assistant_id", openai.GetAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/v1/assistants/:assistant_id", openai.ModifyAssistantEndpoint(cl, ml, appConfig))
|
app.Post("/v1/assistants/:assistant_id", openai.ModifyAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/assistants/:assistant_id", openai.ModifyAssistantEndpoint(cl, ml, appConfig))
|
app.Post("/assistants/:assistant_id", openai.ModifyAssistantEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/assistants/:assistant_id/files", openai.ListAssistantFilesEndpoint(cl, ml, appConfig))
|
app.Get("/v1/assistants/:assistant_id/files", openai.ListAssistantFilesEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/assistants/:assistant_id/files", openai.ListAssistantFilesEndpoint(cl, ml, appConfig))
|
app.Get("/assistants/:assistant_id/files", openai.ListAssistantFilesEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/v1/assistants/:assistant_id/files", openai.CreateAssistantFileEndpoint(cl, ml, appConfig))
|
app.Post("/v1/assistants/:assistant_id/files", openai.CreateAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/assistants/:assistant_id/files", openai.CreateAssistantFileEndpoint(cl, ml, appConfig))
|
app.Post("/assistants/:assistant_id/files", openai.CreateAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/v1/assistants/:assistant_id/files/:file_id", openai.DeleteAssistantFileEndpoint(cl, ml, appConfig))
|
app.Delete("/v1/assistants/:assistant_id/files/:file_id", openai.DeleteAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/assistants/:assistant_id/files/:file_id", openai.DeleteAssistantFileEndpoint(cl, ml, appConfig))
|
app.Delete("/assistants/:assistant_id/files/:file_id", openai.DeleteAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/assistants/:assistant_id/files/:file_id", openai.GetAssistantFileEndpoint(cl, ml, appConfig))
|
app.Get("/v1/assistants/:assistant_id/files/:file_id", openai.GetAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/assistants/:assistant_id/files/:file_id", openai.GetAssistantFileEndpoint(cl, ml, appConfig))
|
app.Get("/assistants/:assistant_id/files/:file_id", openai.GetAssistantFileEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
|
|
||||||
// files
|
// files
|
||||||
app.Post("/v1/files", openai.UploadFilesEndpoint(cl, appConfig))
|
app.Post("/v1/files", openai.UploadFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/files", openai.UploadFilesEndpoint(cl, appConfig))
|
app.Post("/files", openai.UploadFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/files", openai.ListFilesEndpoint(cl, appConfig))
|
app.Get("/v1/files", openai.ListFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/files", openai.ListFilesEndpoint(cl, appConfig))
|
app.Get("/files", openai.ListFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/files/:file_id", openai.GetFilesEndpoint(cl, appConfig))
|
app.Get("/v1/files/:file_id", openai.GetFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/files/:file_id", openai.GetFilesEndpoint(cl, appConfig))
|
app.Get("/files/:file_id", openai.GetFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/v1/files/:file_id", openai.DeleteFilesEndpoint(cl, appConfig))
|
app.Delete("/v1/files/:file_id", openai.DeleteFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Delete("/files/:file_id", openai.DeleteFilesEndpoint(cl, appConfig))
|
app.Delete("/files/:file_id", openai.DeleteFilesEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/v1/files/:file_id/content", openai.GetFilesContentsEndpoint(cl, appConfig))
|
app.Get("/v1/files/:file_id/content", openai.GetFilesContentsEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
app.Get("/files/:file_id/content", openai.GetFilesContentsEndpoint(cl, appConfig))
|
app.Get("/files/:file_id/content", openai.GetFilesContentsEndpoint(application.BackendLoader(), application.ApplicationConfig()))
|
||||||
|
|
||||||
// completion
|
// completion
|
||||||
app.Post("/v1/completions", openai.CompletionEndpoint(cl, ml, appConfig))
|
app.Post("/v1/completions",
|
||||||
app.Post("/completions", openai.CompletionEndpoint(cl, ml, appConfig))
|
openai.CompletionEndpoint(
|
||||||
app.Post("/v1/engines/:model/completions", openai.CompletionEndpoint(cl, ml, appConfig))
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
app.Post("/completions",
|
||||||
|
openai.CompletionEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
app.Post("/v1/engines/:model/completions",
|
||||||
|
openai.CompletionEndpoint(
|
||||||
|
application.BackendLoader(),
|
||||||
|
application.ModelLoader(),
|
||||||
|
application.TemplatesEvaluator(),
|
||||||
|
application.ApplicationConfig(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// embeddings
|
// embeddings
|
||||||
app.Post("/v1/embeddings", openai.EmbeddingsEndpoint(cl, ml, appConfig))
|
app.Post("/v1/embeddings", openai.EmbeddingsEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/embeddings", openai.EmbeddingsEndpoint(cl, ml, appConfig))
|
app.Post("/embeddings", openai.EmbeddingsEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/v1/engines/:model/embeddings", openai.EmbeddingsEndpoint(cl, ml, appConfig))
|
app.Post("/v1/engines/:model/embeddings", openai.EmbeddingsEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
|
|
||||||
// audio
|
// audio
|
||||||
app.Post("/v1/audio/transcriptions", openai.TranscriptEndpoint(cl, ml, appConfig))
|
app.Post("/v1/audio/transcriptions", openai.TranscriptEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
app.Post("/v1/audio/speech", localai.TTSEndpoint(cl, ml, appConfig))
|
app.Post("/v1/audio/speech", localai.TTSEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
|
|
||||||
// images
|
// images
|
||||||
app.Post("/v1/images/generations", openai.ImageEndpoint(cl, ml, appConfig))
|
app.Post("/v1/images/generations", openai.ImageEndpoint(application.BackendLoader(), application.ModelLoader(), application.ApplicationConfig()))
|
||||||
|
|
||||||
if appConfig.ImageDir != "" {
|
if application.ApplicationConfig().ImageDir != "" {
|
||||||
app.Static("/generated-images", appConfig.ImageDir)
|
app.Static("/generated-images", application.ApplicationConfig().ImageDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
if appConfig.AudioDir != "" {
|
if application.ApplicationConfig().AudioDir != "" {
|
||||||
app.Static("/generated-audio", appConfig.AudioDir)
|
app.Static("/generated-audio", application.ApplicationConfig().AudioDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List models
|
// List models
|
||||||
app.Get("/v1/models", openai.ListModelsEndpoint(cl, ml))
|
app.Get("/v1/models", openai.ListModelsEndpoint(application.BackendLoader(), application.ModelLoader()))
|
||||||
app.Get("/models", openai.ListModelsEndpoint(cl, ml))
|
app.Get("/models", openai.ListModelsEndpoint(application.BackendLoader(), application.ModelLoader()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,21 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/microcosm-cc/bluemonday"
|
|
||||||
"github.com/mudler/LocalAI/core/config"
|
"github.com/mudler/LocalAI/core/config"
|
||||||
"github.com/mudler/LocalAI/core/gallery"
|
"github.com/mudler/LocalAI/core/gallery"
|
||||||
"github.com/mudler/LocalAI/core/http/elements"
|
"github.com/mudler/LocalAI/core/http/elements"
|
||||||
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
"github.com/mudler/LocalAI/core/http/endpoints/localai"
|
||||||
|
"github.com/mudler/LocalAI/core/http/utils"
|
||||||
"github.com/mudler/LocalAI/core/p2p"
|
"github.com/mudler/LocalAI/core/p2p"
|
||||||
"github.com/mudler/LocalAI/core/services"
|
"github.com/mudler/LocalAI/core/services"
|
||||||
"github.com/mudler/LocalAI/internal"
|
"github.com/mudler/LocalAI/internal"
|
||||||
"github.com/mudler/LocalAI/pkg/model"
|
"github.com/mudler/LocalAI/pkg/model"
|
||||||
"github.com/mudler/LocalAI/pkg/xsync"
|
"github.com/mudler/LocalAI/pkg/xsync"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/microcosm-cc/bluemonday"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type modelOpCache struct {
|
type modelOpCache struct {
|
||||||
@@ -91,6 +92,7 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
app.Get("/p2p", func(c *fiber.Ctx) error {
|
app.Get("/p2p", func(c *fiber.Ctx) error {
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - P2P dashboard",
|
"Title": "LocalAI - P2P dashboard",
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
//"Nodes": p2p.GetAvailableNodes(""),
|
//"Nodes": p2p.GetAvailableNodes(""),
|
||||||
//"FederatedNodes": p2p.GetAvailableNodes(p2p.FederatedID),
|
//"FederatedNodes": p2p.GetAvailableNodes(p2p.FederatedID),
|
||||||
@@ -149,6 +151,7 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Models",
|
"Title": "LocalAI - Models",
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
"Models": template.HTML(elements.ListModels(models, processingModels, galleryService)),
|
"Models": template.HTML(elements.ListModels(models, processingModels, galleryService)),
|
||||||
"Repositories": appConfig.Galleries,
|
"Repositories": appConfig.Galleries,
|
||||||
@@ -308,6 +311,7 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Chat with " + c.Params("model"),
|
"Title": "LocalAI - Chat with " + c.Params("model"),
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": c.Params("model"),
|
"Model": c.Params("model"),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
@@ -323,11 +327,12 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
if len(backendConfigs) == 0 {
|
if len(backendConfigs) == 0 {
|
||||||
// If no model is available redirect to the index which suggests how to install models
|
// If no model is available redirect to the index which suggests how to install models
|
||||||
return c.Redirect("/")
|
return c.Redirect(utils.BaseURL(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Talk",
|
"Title": "LocalAI - Talk",
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": backendConfigs[0],
|
"Model": backendConfigs[0],
|
||||||
"IsP2PEnabled": p2p.IsP2PEnabled(),
|
"IsP2PEnabled": p2p.IsP2PEnabled(),
|
||||||
@@ -344,11 +349,12 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
if len(backendConfigs) == 0 {
|
if len(backendConfigs) == 0 {
|
||||||
// If no model is available redirect to the index which suggests how to install models
|
// If no model is available redirect to the index which suggests how to install models
|
||||||
return c.Redirect("/")
|
return c.Redirect(utils.BaseURL(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Chat with " + backendConfigs[0],
|
"Title": "LocalAI - Chat with " + backendConfigs[0],
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": backendConfigs[0],
|
"Model": backendConfigs[0],
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
@@ -364,6 +370,7 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Generate images with " + c.Params("model"),
|
"Title": "LocalAI - Generate images with " + c.Params("model"),
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": c.Params("model"),
|
"Model": c.Params("model"),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
@@ -380,11 +387,12 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
if len(backendConfigs) == 0 {
|
if len(backendConfigs) == 0 {
|
||||||
// If no model is available redirect to the index which suggests how to install models
|
// If no model is available redirect to the index which suggests how to install models
|
||||||
return c.Redirect("/")
|
return c.Redirect(utils.BaseURL(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Generate images with " + backendConfigs[0].Name,
|
"Title": "LocalAI - Generate images with " + backendConfigs[0].Name,
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": backendConfigs[0].Name,
|
"Model": backendConfigs[0].Name,
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
@@ -400,6 +408,7 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Generate images with " + c.Params("model"),
|
"Title": "LocalAI - Generate images with " + c.Params("model"),
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": c.Params("model"),
|
"Model": c.Params("model"),
|
||||||
"Version": internal.PrintableVersion(),
|
"Version": internal.PrintableVersion(),
|
||||||
@@ -416,11 +425,12 @@ func RegisterUIRoutes(app *fiber.App,
|
|||||||
|
|
||||||
if len(backendConfigs) == 0 {
|
if len(backendConfigs) == 0 {
|
||||||
// If no model is available redirect to the index which suggests how to install models
|
// If no model is available redirect to the index which suggests how to install models
|
||||||
return c.Redirect("/")
|
return c.Redirect(utils.BaseURL(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
summary := fiber.Map{
|
summary := fiber.Map{
|
||||||
"Title": "LocalAI - Generate audio with " + backendConfigs[0].Name,
|
"Title": "LocalAI - Generate audio with " + backendConfigs[0].Name,
|
||||||
|
"BaseURL": utils.BaseURL(c),
|
||||||
"ModelsConfig": backendConfigs,
|
"ModelsConfig": backendConfigs,
|
||||||
"Model": backendConfigs[0].Name,
|
"Model": backendConfigs[0].Name,
|
||||||
"IsP2PEnabled": p2p.IsP2PEnabled(),
|
"IsP2PEnabled": p2p.IsP2PEnabled(),
|
||||||
|
|||||||
@@ -7,33 +7,33 @@ https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Roboto:wg
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfMZg.ttf) format('truetype');
|
src: url(./UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfMZg.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuGKYMZg.ttf) format('truetype');
|
src: url(./UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuGKYMZg.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuFuYMZg.ttf) format('truetype');
|
src: url(./UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuFuYMZg.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets/KFOmCnqEu92Fr1Me5Q.ttf) format('truetype');
|
src: url(./KFOmCnqEu92Fr1Me5Q.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets/KFOlCnqEu92Fr1MmEU9vAw.ttf) format('truetype');
|
src: url(./KFOlCnqEu92Fr1MmEU9vAw.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,33 +7,33 @@ https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets//KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
|
src: url(./KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets//KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
|
src: url(./KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets//KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
|
src: url(./KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets//KFOlCnqEu92Fr1MmWUlfBBc9.ttf) format('truetype');
|
src: url(./KFOlCnqEu92Fr1MmWUlfBBc9.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url(/static/assets//KFOlCnqEu92Fr1MmYUtfBBc9.ttf) format('truetype');
|
src: url(./KFOlCnqEu92Fr1MmYUtfBBc9.ttf) format('truetype');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ function readInputImage() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Source: https://stackoverflow.com/a/75751803/11386095
|
// Source: https://stackoverflow.com/a/75751803/11386095
|
||||||
const response = await fetch("/v1/chat/completions", {
|
const response = await fetch("v1/chat/completions", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${key}`,
|
Authorization: `Bearer ${key}`,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ async function promptDallE(key, input) {
|
|||||||
document.getElementById("input").disabled = true;
|
document.getElementById("input").disabled = true;
|
||||||
|
|
||||||
const model = document.getElementById("image-model").value;
|
const model = document.getElementById("image-model").value;
|
||||||
const response = await fetch("/v1/images/generations", {
|
const response = await fetch("v1/images/generations", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${key}`,
|
Authorization: `Bearer ${key}`,
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ async function sendAudioToWhisper(audioBlob) {
|
|||||||
formData.append('model', getWhisperModel());
|
formData.append('model', getWhisperModel());
|
||||||
API_KEY = localStorage.getItem("key");
|
API_KEY = localStorage.getItem("key");
|
||||||
|
|
||||||
const response = await fetch('/v1/audio/transcriptions', {
|
const response = await fetch('v1/audio/transcriptions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${API_KEY}`
|
'Authorization': `Bearer ${API_KEY}`
|
||||||
@@ -139,7 +139,7 @@ async function sendTextToChatGPT(text) {
|
|||||||
conversationHistory.push({ role: "user", content: text });
|
conversationHistory.push({ role: "user", content: text });
|
||||||
API_KEY = localStorage.getItem("key");
|
API_KEY = localStorage.getItem("key");
|
||||||
|
|
||||||
const response = await fetch('/v1/chat/completions', {
|
const response = await fetch('v1/chat/completions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${API_KEY}`,
|
'Authorization': `Bearer ${API_KEY}`,
|
||||||
@@ -163,7 +163,7 @@ async function sendTextToChatGPT(text) {
|
|||||||
async function getTextToSpeechAudio(text) {
|
async function getTextToSpeechAudio(text) {
|
||||||
API_KEY = localStorage.getItem("key");
|
API_KEY = localStorage.getItem("key");
|
||||||
|
|
||||||
const response = await fetch('/v1/audio/speech', {
|
const response = await fetch('v1/audio/speech', {
|
||||||
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ async function tts(key, input) {
|
|||||||
document.getElementById("input").disabled = true;
|
document.getElementById("input").disabled = true;
|
||||||
|
|
||||||
const model = document.getElementById("tts-model").value;
|
const model = document.getElementById("tts-model").value;
|
||||||
const response = await fetch("/tts", {
|
const response = await fetch("tts", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${key}`,
|
Authorization: `Bearer ${key}`,
|
||||||
|
|||||||
24
core/http/utils/baseurl.go
Normal file
24
core/http/utils/baseurl.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseURL returns the base URL for the given HTTP request context.
|
||||||
|
// It takes into account that the app may be exposed by a reverse-proxy under a different protocol, host and path.
|
||||||
|
// The returned URL is guaranteed to end with `/`.
|
||||||
|
// The method should be used in conjunction with the StripPathPrefix middleware.
|
||||||
|
func BaseURL(c *fiber.Ctx) string {
|
||||||
|
path := c.Path()
|
||||||
|
origPath := c.OriginalURL()
|
||||||
|
|
||||||
|
if path != origPath && strings.HasSuffix(origPath, path) {
|
||||||
|
pathPrefix := origPath[:len(origPath)-len(path)+1]
|
||||||
|
|
||||||
|
return c.BaseURL() + pathPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.BaseURL() + "/"
|
||||||
|
}
|
||||||
48
core/http/utils/baseurl_test.go
Normal file
48
core/http/utils/baseurl_test.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaseURL(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
prefix string
|
||||||
|
expectURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "without prefix",
|
||||||
|
prefix: "/",
|
||||||
|
expectURL: "http://example.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with prefix",
|
||||||
|
prefix: "/myprefix/",
|
||||||
|
expectURL: "http://example.com/myprefix/",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
app := fiber.New()
|
||||||
|
actualURL := ""
|
||||||
|
|
||||||
|
app.Get(tc.prefix+"hello/world", func(c *fiber.Ctx) error {
|
||||||
|
if tc.prefix != "/" {
|
||||||
|
c.Path("/hello/world")
|
||||||
|
}
|
||||||
|
actualURL = BaseURL(c)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest("GET", tc.prefix+"hello/world", nil)
|
||||||
|
resp, err := app.Test(req, -1)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 200, resp.StatusCode, "response status code")
|
||||||
|
require.Equal(t, tc.expectURL, actualURL, "base URL")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="header text-center py-12">
|
<div class="header text-center py-12">
|
||||||
<h1 class="text-5xl font-bold">Welcome to your LocalAI instance!</h1>
|
<h1 class="text-5xl font-bold">Welcome to your LocalAI instance!</h1>
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<!-- <a href="/" aria-label="HomePage" alt="HomePage">
|
<!-- <a href="./" aria-label="HomePage" alt="HomePage">
|
||||||
<img class="mx-auto w-1/4 h-auto" src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo">
|
<img class="mx-auto w-1/4 h-auto" src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo">
|
||||||
</a>
|
</a>
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ SOFTWARE.
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
{{template "views/partials/head" .}}
|
{{template "views/partials/head" .}}
|
||||||
<script defer src="/static/chat.js"></script>
|
<script defer src="static/chat.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -101,9 +101,9 @@ SOFTWARE.
|
|||||||
{{ $model:=.Model}}
|
{{ $model:=.Model}}
|
||||||
{{ range .ModelsConfig }}
|
{{ range .ModelsConfig }}
|
||||||
{{ if eq . $model }}
|
{{ if eq . $model }}
|
||||||
<option value="/chat/{{.}}" selected class="bg-gray-700 text-white">{{.}}</option>
|
<option value="chat/{{.}}" selected class="bg-gray-700 text-white">{{.}}</option>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<option value="/chat/{{.}}" class="bg-gray-700 text-white">{{.}}</option>
|
<option value="chat/{{.}}" class="bg-gray-700 text-white">{{.}}</option>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</select>
|
</select>
|
||||||
@@ -142,7 +142,7 @@ SOFTWARE.
|
|||||||
<div id="loader" class="my-2 loader" style="display: none;"></div>
|
<div id="loader" class="my-2 loader" style="display: none;"></div>
|
||||||
<input id="chat-model" type="hidden" value="{{.Model}}">
|
<input id="chat-model" type="hidden" value="{{.Model}}">
|
||||||
<input id="input_image" type="file" style="display: none;" @change="fileName = $event.target.files[0].name">
|
<input id="input_image" type="file" style="display: none;" @change="fileName = $event.target.files[0].name">
|
||||||
<form id="prompt" action="/chat/{{.Model}}" method="get" @submit.prevent="submitPrompt">
|
<form id="prompt" action="chat/{{.Model}}" method="get" @submit.prevent="submitPrompt">
|
||||||
<div class="relative w-full">
|
<div class="relative w-full">
|
||||||
<textarea
|
<textarea
|
||||||
id="input"
|
id="input"
|
||||||
|
|||||||
@@ -370,7 +370,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/p2panimation.js"></script>
|
<script src="static/p2panimation.js"></script>
|
||||||
|
|
||||||
{{template "views/partials/footer" .}}
|
{{template "views/partials/footer" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
{{template "views/partials/inprogress" .}}
|
{{template "views/partials/inprogress" .}}
|
||||||
{{ if eq (len .ModelsConfig) 0 }}
|
{{ if eq (len .ModelsConfig) 0 }}
|
||||||
<h2 class="text-center text-3xl font-semibold text-gray-100"> <i class="text-yellow-200 ml-2 fa-solid fa-triangle-exclamation animate-pulse"></i> Ouch! seems you don't have any models installed from the LocalAI gallery!</h2>
|
<h2 class="text-center text-3xl font-semibold text-gray-100"> <i class="text-yellow-200 ml-2 fa-solid fa-triangle-exclamation animate-pulse"></i> Ouch! seems you don't have any models installed from the LocalAI gallery!</h2>
|
||||||
<p class="text-center mt-4 text-xl">..install something from the <a class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded" href="/browse">🖼️ Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded"> <i class="fa-solid fa-book"></i> Getting started documentation </a></p>
|
<p class="text-center mt-4 text-xl">..install something from the <a class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded" href="browse">🖼️ Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded"> <i class="fa-solid fa-book"></i> Getting started documentation </a></p>
|
||||||
|
|
||||||
{{ if ne (len .Models) 0 }}
|
{{ if ne (len .Models) 0 }}
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 font-bold">
|
<td class="px-4 py-3 font-bold">
|
||||||
<p class="font-bold text-white flex items-center"><i class="fas fa-brain pr-2"></i><a href="/browse?term={{.Name}}">{{.Name}}</a></p>
|
<p class="font-bold text-white flex items-center"><i class="fas fa-brain pr-2"></i><a href="browse?term={{.Name}}">{{.Name}}</a></p>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 font-bold">
|
<td class="px-4 py-3 font-bold">
|
||||||
{{ if .Backend }}
|
{{ if .Backend }}
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
<td class="px-4 py-3">
|
<td class="px-4 py-3">
|
||||||
<button
|
<button
|
||||||
class="float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
class="float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
||||||
data-twe-ripple-color="light" data-twe-ripple-init="" hx-confirm="Are you sure you wish to delete the model?" hx-post="/browse/delete/model/{{.Name}}" hx-swap="outerHTML"><i class="fa-solid fa-cancel pr-2"></i>Delete</button>
|
data-twe-ripple-color="light" data-twe-ripple-init="" hx-confirm="Are you sure you wish to delete the model?" hx-post="browse/delete/model/{{.Name}}" hx-swap="outerHTML"><i class="fa-solid fa-cancel pr-2"></i>Delete</button>
|
||||||
</td>
|
</td>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ range .Models }}
|
{{ range .Models }}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Open Authenticated Website</title>
|
<title>Open Authenticated Website</title>
|
||||||
|
<base href="{{.BaseURL}}" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Authorization is required</h1>
|
<h1>Authorization is required</h1>
|
||||||
|
|||||||
@@ -16,38 +16,38 @@
|
|||||||
|
|
||||||
<div class="text-center font-semibold text-gray-100">
|
<div class="text-center font-semibold text-gray-100">
|
||||||
<h2>Filter by type:</h2>
|
<h2>Filter by type:</h2>
|
||||||
<button hx-post="/browse/search/models"
|
<button hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "tts"}'
|
hx-vals='{"search": "tts"}'
|
||||||
hx-indicator=".htmx-indicator" >TTS</button>
|
hx-indicator=".htmx-indicator" >TTS</button>
|
||||||
<button hx-post="/browse/search/models"
|
<button hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "stablediffusion"}'
|
hx-vals='{"search": "stablediffusion"}'
|
||||||
hx-indicator=".htmx-indicator" >Image generation</button>
|
hx-indicator=".htmx-indicator" >Image generation</button>
|
||||||
<button hx-post="/browse/search/models" \
|
<button hx-post="browse/search/models" \
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "llm"}'
|
hx-vals='{"search": "llm"}'
|
||||||
hx-indicator=".htmx-indicator" >Text generation</button>
|
hx-indicator=".htmx-indicator" >Text generation</button>
|
||||||
<button hx-post="/browse/search/models"
|
<button hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "multimodal"}'
|
hx-vals='{"search": "multimodal"}'
|
||||||
hx-indicator=".htmx-indicator" >Multimodal</button>
|
hx-indicator=".htmx-indicator" >Multimodal</button>
|
||||||
<button hx-post="/browse/search/models"
|
<button hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "embedding"}'
|
hx-vals='{"search": "embedding"}'
|
||||||
hx-indicator=".htmx-indicator" >Embeddings</button>
|
hx-indicator=".htmx-indicator" >Embeddings</button>
|
||||||
<button hx-post="/browse/search/models"
|
<button hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "rerank"}'
|
hx-vals='{"search": "rerank"}'
|
||||||
hx-indicator=".htmx-indicator" >Rerankers</button>
|
hx-indicator=".htmx-indicator" >Rerankers</button>
|
||||||
<button
|
<button
|
||||||
hx-post="/browse/search/models"
|
hx-post="browse/search/models"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-vals='{"search": "whisper"}'
|
hx-vals='{"search": "whisper"}'
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<div class="text-center text-xs font-semibold text-gray-100">
|
<div class="text-center text-xs font-semibold text-gray-100">
|
||||||
Filter by tags:
|
Filter by tags:
|
||||||
{{ range .AllTags }}
|
{{ range .AllTags }}
|
||||||
<button hx-post="/browse/search/models" class="text-blue-500" hx-target="#search-results"
|
<button hx-post="browse/search/models" class="text-blue-500" hx-target="#search-results"
|
||||||
hx-vals='{"search": "{{.}}"}'
|
hx-vals='{"search": "{{.}}"}'
|
||||||
hx-indicator=".htmx-indicator" >{{.}}</button>
|
hx-indicator=".htmx-indicator" >{{.}}</button>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
<input class="form-control appearance-none block w-full mt-5 px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
<input class="form-control appearance-none block w-full mt-5 px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
||||||
name="search" placeholder="Begin Typing To Search models..."
|
name="search" placeholder="Begin Typing To Search models..."
|
||||||
hx-post="/browse/search/models"
|
hx-post="browse/search/models"
|
||||||
hx-trigger="input changed delay:500ms, search"
|
hx-trigger="input changed delay:500ms, search"
|
||||||
hx-target="#search-results"
|
hx-target="#search-results"
|
||||||
hx-indicator=".htmx-indicator">
|
hx-indicator=".htmx-indicator">
|
||||||
|
|||||||
@@ -48,11 +48,11 @@
|
|||||||
<!-- Federation Box -->
|
<!-- Federation Box -->
|
||||||
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-12 text-left">
|
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-12 text-left">
|
||||||
|
|
||||||
<p class="text-xl font-semibold text-gray-200"> <i class="text-gray-200 fa-solid fa-circle-nodes"></i> Federated Nodes: <span hx-get="/p2p/ui/workers-federation-stats" hx-trigger="every 1s"></span> </p>
|
<p class="text-xl font-semibold text-gray-200"> <i class="text-gray-200 fa-solid fa-circle-nodes"></i> Federated Nodes: <span hx-get="p2p/ui/workers-federation-stats" hx-trigger="every 1s"></span> </p>
|
||||||
<p class="mb-4">You can start LocalAI in federated mode to share your instance, or start the federated server to balance requests between nodes of the federation.</p>
|
<p class="mb-4">You can start LocalAI in federated mode to share your instance, or start the federated server to balance requests between nodes of the federation.</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-12">
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-12">
|
||||||
<div hx-get="/p2p/ui/workers-federation" hx-trigger="every 1s"></div>
|
<div hx-get="p2p/ui/workers-federation" hx-trigger="every 1s"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="border-gray-700 mb-12">
|
<hr class="border-gray-700 mb-12">
|
||||||
@@ -123,11 +123,11 @@
|
|||||||
|
|
||||||
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-12 text-left">
|
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-12 text-left">
|
||||||
|
|
||||||
<p class="text-xl font-semibold text-gray-200"> <i class="text-gray-200 fa-solid fa-circle-nodes"></i> Workers (llama.cpp): <span hx-get="/p2p/ui/workers-stats" hx-trigger="every 1s"></span> </p>
|
<p class="text-xl font-semibold text-gray-200"> <i class="text-gray-200 fa-solid fa-circle-nodes"></i> Workers (llama.cpp): <span hx-get="p2p/ui/workers-stats" hx-trigger="every 1s"></span> </p>
|
||||||
<p class="mb-4">You can start llama.cpp workers to distribute weights between the workers and offload part of the computation. To start a new worker, you can use the CLI or Docker.</p>
|
<p class="mb-4">You can start llama.cpp workers to distribute weights between the workers and offload part of the computation. To start a new worker, you can use the CLI or Docker.</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-12">
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-12">
|
||||||
<div hx-get="/p2p/ui/workers" hx-trigger="every 1s"></div>
|
<div hx-get="p2p/ui/workers" hx-trigger="every 1s"></div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="border-gray-700 mb-12">
|
<hr class="border-gray-700 mb-12">
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
{{template "views/partials/footer" .}}
|
{{template "views/partials/footer" .}}
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/p2panimation.js"></script>
|
<script src="static/p2panimation.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.token {
|
.token {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
LocalAI Version {{.Version}}<br>
|
LocalAI Version {{.Version}}<br>
|
||||||
<a href='https://github.com/mudler/LocalAI' class="text-blue-400 hover:text-blue-600" target="_blank">LocalAI</a> © 2023-2024 <a href='https://mudler.pm' class="text-blue-400 hover:text-blue-600" target="_blank">Ettore Di Giacinto</a>
|
<a href='https://github.com/mudler/LocalAI' class="text-blue-400 hover:text-blue-600" target="_blank">LocalAI</a> © 2023-2024 <a href='https://mudler.pm' class="text-blue-400 hover:text-blue-600" target="_blank">Ettore Di Giacinto</a>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="/static/assets/tw-elements.js"></script>
|
<script src="static/assets/tw-elements.js"></script>
|
||||||
|
|||||||
@@ -2,33 +2,35 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
|
<base href="{{.BaseURL}}" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="/static/assets/highlightjs.css"
|
href="static/assets/highlightjs.css"
|
||||||
/>
|
/>
|
||||||
<script defer src="/static/assets/highlightjs.js"></script>
|
<script defer src="static/assets/highlightjs.js"></script>
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
src="/static/assets/alpine.js"
|
src="static/assets/alpine.js"
|
||||||
></script>
|
></script>
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
src="/static/assets/marked.js"
|
src="static/assets/marked.js"
|
||||||
></script>
|
></script>
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
src="/static/assets/purify.js"
|
src="static/assets/purify.js"
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
<link href="/static/general.css" rel="stylesheet" />
|
<link href="static/general.css" rel="stylesheet" />
|
||||||
<link href="/static/assets/font1.css" rel="stylesheet">
|
<link href="static/assets/font1.css" rel="stylesheet">
|
||||||
<link
|
<link
|
||||||
href="/static/assets/font2.css"
|
href="static/assets/font2.css"
|
||||||
rel="stylesheet" />
|
rel="stylesheet" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="/static/assets/tw-elements.css" />
|
href="static/assets/tw-elements.css" />
|
||||||
<script src="/static/assets/tailwindcss.js"></script>
|
<script src="static/assets/tailwindcss.js"></script>
|
||||||
<script>
|
<script>
|
||||||
tailwind.config = {
|
tailwind.config = {
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
@@ -54,11 +56,11 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<link href="/static/assets/fontawesome/css/fontawesome.css" rel="stylesheet" />
|
<link href="static/assets/fontawesome/css/fontawesome.css" rel="stylesheet" />
|
||||||
<link href="/static/assets/fontawesome/css/brands.css" rel="stylesheet" />
|
<link href="static/assets/fontawesome/css/brands.css" rel="stylesheet" />
|
||||||
<link href="/static/assets/fontawesome/css/solid.css" rel="stylesheet" />
|
<link href="static/assets/fontawesome/css/solid.css" rel="stylesheet" />
|
||||||
<script src="/static/assets/flowbite.min.js"></script>
|
<script src="static/assets/flowbite.min.js"></script>
|
||||||
<script src="/static/assets/htmx.js" crossorigin="anonymous"></script>
|
<script src="static/assets/htmx.js" crossorigin="anonymous"></script>
|
||||||
<!-- P2P Animation START -->
|
<!-- P2P Animation START -->
|
||||||
<style>
|
<style>
|
||||||
.animation-container {
|
.animation-container {
|
||||||
|
|||||||
@@ -17,13 +17,13 @@
|
|||||||
|
|
||||||
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
||||||
<div class="flex items center">
|
<div class="flex items center">
|
||||||
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
<span class="text-gray-300"><a href="browse?term={{$parts._1}}"
|
||||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||||
>{{$modelName}}</a> {{if $repository}} (from the '{{$repository}}' repository) {{end}}</span>
|
>{{$modelName}}</a> {{if $repository}} (from the '{{$repository}}' repository) {{end}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div hx-get="/browse/job/{{$value}}" hx-swap="outerHTML" hx-target="this" hx-trigger="done">
|
<div hx-get="browse/job/{{$value}}" hx-swap="outerHTML" hx-target="this" hx-trigger="done">
|
||||||
<h3 role="status" id="pblabel" >{{$op}}
|
<h3 role="status" id="pblabel" >{{$op}}
|
||||||
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms"
|
<div hx-get="browse/job/progress/{{$value}}" hx-trigger="every 600ms"
|
||||||
hx-target="this"
|
hx-target="this"
|
||||||
hx-swap="innerHTML" ></div></h3>
|
hx-swap="innerHTML" ></div></h3>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<!-- Logo Image: Replace 'logo_url_here' with your actual logo URL -->
|
<!-- Logo Image: Replace 'logo_url_here' with your actual logo URL -->
|
||||||
<a href="/" class="text-white text-xl font-bold"><img src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo" class="h-10 mr-3 border-2 border-gray-300 shadow rounded"></a>
|
<a href="./" class="text-white text-xl font-bold"><img src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo" class="h-10 mr-3 border-2 border-gray-300 shadow rounded"></a>
|
||||||
<a href="/" class="text-white text-xl font-bold">LocalAI</a>
|
<a href="./" class="text-white text-xl font-bold">LocalAI</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Menu button for small screens -->
|
<!-- Menu button for small screens -->
|
||||||
<div class="lg:hidden">
|
<div class="lg:hidden">
|
||||||
@@ -14,33 +14,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Navigation links -->
|
<!-- Navigation links -->
|
||||||
<div class="hidden lg:flex lg:items-center lg:justify-end lg:flex-1 lg:w-0">
|
<div class="hidden lg:flex lg:items-center lg:justify-end lg:flex-1 lg:w-0">
|
||||||
<a href="/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-home pr-2"></i>Home</a>
|
<a href="./" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-home pr-2"></i>Home</a>
|
||||||
<a href="https://localai.io" class="text-gray-400 hover:text-white px-3 py-2 rounded" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
<a href="https://localai.io" class="text-gray-400 hover:text-white px-3 py-2 rounded" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
||||||
<a href="/browse/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
<a href="browse/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
||||||
<a href="/chat/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-comments pr-2"></i> Chat</a>
|
<a href="chat/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-comments pr-2"></i> Chat</a>
|
||||||
<a href="/text2image/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-image pr-2"></i> Generate images</a>
|
<a href="text2image/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-image pr-2"></i> Generate images</a>
|
||||||
<a href="/tts/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-music pr-2"></i> TTS </a>
|
<a href="tts/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-music pr-2"></i> TTS </a>
|
||||||
<a href="/talk/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-phone pr-2"></i> Talk </a>
|
<a href="talk/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-phone pr-2"></i> Talk </a>
|
||||||
{{ if .IsP2PEnabled }}
|
{{ if .IsP2PEnabled }}
|
||||||
<a href="/p2p/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-circle-nodes"></i> Swarm </a>
|
<a href="p2p/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fa-solid fa-circle-nodes"></i> Swarm </a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="/swagger/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-code pr-2"></i> API</a>
|
<a href="swagger/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-code pr-2"></i> API</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Collapsible menu for small screens -->
|
<!-- Collapsible menu for small screens -->
|
||||||
<div class="hidden lg:hidden" id="mobile-menu">
|
<div class="hidden lg:hidden" id="mobile-menu">
|
||||||
<div class="pt-4 pb-3 border-t border-gray-700">
|
<div class="pt-4 pb-3 border-t border-gray-700">
|
||||||
<a href="/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-home pr-2"></i>Home</a>
|
<a href="./" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-home pr-2"></i>Home</a>
|
||||||
<a href="https://localai.io" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
<a href="https://localai.io" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
||||||
<a href="/browse/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-brain pr-2"></i> Models</a>
|
<a href="browse/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-brain pr-2"></i> Models</a>
|
||||||
<a href="/chat/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-comments pr-2"></i> Chat</a>
|
<a href="chat/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-comments pr-2"></i> Chat</a>
|
||||||
<a href="/text2image/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-image pr-2"></i> Generate images</a>
|
<a href="text2image/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-image pr-2"></i> Generate images</a>
|
||||||
<a href="/tts/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-music pr-2"></i> TTS </a>
|
<a href="tts/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-music pr-2"></i> TTS </a>
|
||||||
<a href="/talk/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-phone pr-2"></i> Talk </a>
|
<a href="talk/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-phone pr-2"></i> Talk </a>
|
||||||
{{ if .IsP2PEnabled }}
|
{{ if .IsP2PEnabled }}
|
||||||
<a href="/p2p/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-circle-nodes"></i> Swarm </a>
|
<a href="p2p/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fa-solid fa-circle-nodes"></i> Swarm </a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="/swagger/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-code pr-2"></i> API</a>
|
<a href="swagger/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-code pr-2"></i> API</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<!-- Logo Image: Replace 'logo_url_here' with your actual logo URL -->
|
<!-- Logo Image: Replace 'logo_url_here' with your actual logo URL -->
|
||||||
<a href="/" class="text-white text-xl font-bold"><img src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo" class="h-10 mr-3 border-2 border-gray-300 shadow rounded"></a>
|
<a href="./" class="text-white text-xl font-bold"><img src="https://github.com/go-skynet/LocalAI/assets/2420543/0966aa2a-166e-4f99-a3e5-6c915fc997dd" alt="LocalAI Logo" class="h-10 mr-3 border-2 border-gray-300 shadow rounded"></a>
|
||||||
<a href="/" class="text-white text-xl font-bold">LocalAI</a>
|
<a href="./" class="text-white text-xl font-bold">LocalAI</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Menu button for small screens -->
|
<!-- Menu button for small screens -->
|
||||||
<div class="lg:hidden">
|
<div class="lg:hidden">
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Navigation links -->
|
<!-- Navigation links -->
|
||||||
<div class="hidden lg:flex lg:items-center lg:justify-end lg:flex-1 lg:w-0">
|
<div class="hidden lg:flex lg:items-center lg:justify-end lg:flex-1 lg:w-0">
|
||||||
<a href="/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-home pr-2"></i>Home</a>
|
<a href="./" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-home pr-2"></i>Home</a>
|
||||||
<a href="https://localai.io" class="text-gray-400 hover:text-white px-3 py-2 rounded" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
<a href="https://localai.io" class="text-gray-400 hover:text-white px-3 py-2 rounded" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
||||||
<a href="https://models.localai.io/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
<a href="https://models.localai.io/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<!-- Collapsible menu for small screens -->
|
<!-- Collapsible menu for small screens -->
|
||||||
<div class="hidden lg:hidden" id="mobile-menu">
|
<div class="hidden lg:hidden" id="mobile-menu">
|
||||||
<div class="pt-4 pb-3 border-t border-gray-700">
|
<div class="pt-4 pb-3 border-t border-gray-700">
|
||||||
<a href="/" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-home pr-2"></i>Home</a>
|
<a href="./" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1"><i class="fas fa-home pr-2"></i>Home</a>
|
||||||
<a href="https://localai.io" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
<a href="https://localai.io" class="block text-gray-400 hover:text-white px-3 py-2 rounded mt-1" target="_blank" ><i class="fas fa-book-reader pr-2"></i> Documentation</a>
|
||||||
<a href="https://models.localai.io/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
<a href="https://models.localai.io/" class="text-gray-400 hover:text-white px-3 py-2 rounded"><i class="fas fa-brain pr-2"></i> Models</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user