Unverified Commit 0ddb2b32 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #413 from ollama-webui/main

dev
parents 880f58e8 ed1d9e61
apiVersion: v1
kind: Service
metadata:
name: ollama-webui-service
namespace: ollama-namespace
spec:
type: NodePort # Use LoadBalancer if you're on a cloud that supports it
selector:
app: ollama-webui
ports:
- protocol: TCP
port: 8080
targetPort: 8080
# If using NodePort, you can optionally specify the nodePort:
# nodePort: 30000
\ No newline at end of file
resources:
- base/ollama-namespace.yaml
- base/ollama-service.yaml
- base/ollama-statefulset.yaml
- base/webui-deployment.yaml
- base/webui-service.yaml
- base/webui-ingress.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patches/ollama-statefulset-gpu.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ollama
namespace: ollama-namespace
spec:
selector:
matchLabels:
app: ollama
serviceName: "ollama"
template:
spec:
containers:
- name: ollama
resources:
limits:
nvidia.com/gpu: "1"
This diff is collapsed.
......@@ -8,22 +8,23 @@
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "npm run eslint",
"lint": "npm run lint:frontend ; npm run lint:types ; npm run lint:backend",
"lint:frontend": "eslint . --fix",
"lint:types": "npm run check",
"fmt": "npm run prettier:svelte && npm run prettier",
"eslint": "npx -p eslint@8 -- eslint .",
"prettier:svelte": "npx -p prettier@2 -- prettier --plugin-search-dir . --write .",
"prettier": "npx -p prettier@2 -- prettier --write '**/*.{js,css,md,html,json}'"
"lint:backend": "pylint backend/",
"format": "prettier --plugin-search-dir --write '**/*.{js,ts,svelte,css,md,html,json}'",
"format:backend": "yapf --recursive backend -p -i"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.20.4",
"@sveltejs/kit": "^1.30.0",
"@tailwindcss/typography": "^0.5.10",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@types/bun": "latest",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"autoprefixer": "^10.4.16",
"eslint": "^8.28.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
"postcss": "^8.4.31",
......@@ -39,6 +40,7 @@
"type": "module",
"dependencies": {
"@sveltejs/adapter-node": "^1.3.1",
"dayjs": "^1.11.10",
"file-saver": "^2.0.5",
"highlight.js": "^11.9.0",
"idb": "^7.1.1",
......
#!/bin/bash
# Define color and formatting codes
BOLD='\033[1m'
GREEN='\033[1;32m'
WHITE='\033[1;37m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Unicode character for tick mark
TICK='\u2713'
# Detect GPU driver
get_gpu_driver() {
# Detect NVIDIA GPUs
if lspci | grep -i nvidia >/dev/null; then
echo "nvidia"
return
fi
# Detect AMD GPUs (including GCN architecture check for amdgpu vs radeon)
if lspci | grep -i amd >/dev/null; then
# List of known GCN and later architecture cards
# This is a simplified list, and in a real-world scenario, you'd want a more comprehensive one
local gcn_and_later=("Radeon HD 7000" "Radeon HD 8000" "Radeon R5" "Radeon R7" "Radeon R9" "Radeon RX")
# Get GPU information
local gpu_info=$(lspci | grep -i 'vga.*amd')
for model in "${gcn_and_later[@]}"; do
if echo "$gpu_info" | grep -iq "$model"; then
echo "amdgpu"
return
fi
done
# Default to radeon if no GCN or later architecture is detected
echo "radeon"
return
fi
# Detect Intel GPUs
if lspci | grep -i intel >/dev/null; then
echo "i915"
return
fi
# If no known GPU is detected
echo "Unknown or unsupported GPU driver"
exit 1
}
# Function for rolling animation
show_loading() {
local spin='-\|/'
local i=0
printf " "
while kill -0 $1 2>/dev/null; do
i=$(( (i+1) %4 ))
printf "\b${spin:$i:1}"
sleep .1
done
# Replace the spinner with a tick
printf "\b${GREEN}${TICK}${NC}"
}
# Usage information
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --enable-gpu[count=COUNT] Enable GPU support with the specified count."
echo " --enable-api[port=PORT] Enable API and expose it on the specified port."
echo " --webui[port=PORT] Set the port for the web user interface."
echo " --data[folder=PATH] Bind mount for ollama data folder (by default will create the 'ollama' volume)."
echo " --build Build the docker image before running the compose project."
echo " --drop Drop the compose project."
echo " -q, --quiet Run script in headless mode."
echo " -h, --help Show this help message."
echo ""
echo "Examples:"
echo " $0 --drop"
echo " $0 --enable-gpu[count=1]"
echo " $0 --enable-api[port=11435]"
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000]"
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data]"
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data] --build"
echo ""
echo "This script configures and runs a docker-compose setup with optional GPU support, API exposure, and web UI configuration."
echo "About the gpu to use, the script automatically detects it using the "lspci" command."
echo "In this case the gpu detected is: $(get_gpu_driver)"
}
# Default values
gpu_count=1
api_port=11435
webui_port=3000
headless=false
build_image=false
kill_compose=false
# Function to extract value from the parameter
extract_value() {
echo "$1" | sed -E 's/.*\[.*=(.*)\].*/\1/; t; s/.*//'
}
# Parse arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--enable-gpu*)
enable_gpu=true
value=$(extract_value "$key")
gpu_count=${value:-1}
;;
--enable-api*)
enable_api=true
value=$(extract_value "$key")
api_port=${value:-11435}
;;
--webui*)
value=$(extract_value "$key")
webui_port=${value:-3000}
;;
--data*)
value=$(extract_value "$key")
data_dir=${value:-"./ollama-data"}
;;
--drop)
kill_compose=true
;;
--build)
build_image=true
;;
-q|--quiet)
headless=true
;;
-h|--help)
usage
exit
;;
*)
# Unknown option
echo "Unknown option: $key"
usage
exit 1
;;
esac
shift # past argument or value
done
if [[ $kill_compose == true ]]; then
docker compose down --remove-orphans
echo -e "${GREEN}${BOLD}Compose project dropped successfully.${NC}"
exit
else
DEFAULT_COMPOSE_COMMAND="docker compose -f docker-compose.yaml"
if [[ $enable_gpu == true ]]; then
# Validate and process command-line arguments
if [[ -n $gpu_count ]]; then
if ! [[ $gpu_count =~ ^[0-9]+$ ]]; then
echo "Invalid GPU count: $gpu_count"
exit 1
fi
echo "Enabling GPU with $gpu_count GPUs"
# Add your GPU allocation logic here
export OLLAMA_GPU_DRIVER=$(get_gpu_driver)
export OLLAMA_GPU_COUNT=$gpu_count # Set OLLAMA_GPU_COUNT environment variable
fi
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.gpu.yaml"
fi
if [[ $enable_api == true ]]; then
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.api.yaml"
if [[ -n $api_port ]]; then
export OLLAMA_WEBAPI_PORT=$api_port # Set OLLAMA_WEBAPI_PORT environment variable
fi
fi
if [[ -n $data_dir ]]; then
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.data.yaml"
export OLLAMA_DATA_DIR=$data_dir # Set OLLAMA_DATA_DIR environment variable
fi
DEFAULT_COMPOSE_COMMAND+=" up -d"
DEFAULT_COMPOSE_COMMAND+=" --remove-orphans"
DEFAULT_COMPOSE_COMMAND+=" --force-recreate"
if [[ $build_image == true ]]; then
DEFAULT_COMPOSE_COMMAND+=" --build"
fi
fi
# Recap of environment variables
echo
echo -e "${WHITE}${BOLD}Current Setup:${NC}"
echo -e " ${GREEN}${BOLD}GPU Driver:${NC} ${OLLAMA_GPU_DRIVER:-Not Enabled}"
echo -e " ${GREEN}${BOLD}GPU Count:${NC} ${OLLAMA_GPU_COUNT:-Not Enabled}"
echo -e " ${GREEN}${BOLD}WebAPI Port:${NC} ${OLLAMA_WEBAPI_PORT:-Not Enabled}"
echo -e " ${GREEN}${BOLD}Data Folder:${NC} ${data_dir:-Using ollama volume}"
echo -e " ${GREEN}${BOLD}WebUI Port:${NC} $webui_port"
echo
if [[ $headless == true ]]; then
echo -ne "${WHITE}${BOLD}Running in headless mode... ${NC}"
choice="y"
else
# Ask for user acceptance
echo -ne "${WHITE}${BOLD}Do you want to proceed with current setup? (Y/n): ${NC}"
read -n1 -s choice
fi
echo
if [[ $choice == "" || $choice == "y" ]]; then
# Execute the command with the current user
eval "$DEFAULT_COMPOSE_COMMAND" &
# Capture the background process PID
PID=$!
# Display the loading animation
#show_loading $PID
# Wait for the command to finish
wait $PID
echo
# Check exit status
if [ $? -eq 0 ]; then
echo -e "${GREEN}${BOLD}Compose project started successfully.${NC}"
else
echo -e "${RED}${BOLD}There was an error starting the compose project.${NC}"
fi
else
echo "Aborted."
fi
echo
docker rm -f ollama || true
docker pull ollama/ollama
# CPU Only
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
# GPU Support
# docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
docker image prune -f
\ No newline at end of file
......@@ -16,7 +16,7 @@ html {
code {
/* white-space-collapse: preserve !important; */
white-space: pre;
overflow-x: auto;
width: auto;
}
......
......@@ -12,6 +12,10 @@
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
) {
document.documentElement.classList.add('light');
} else if (localStorage.theme) {
localStorage.theme.split(' ').forEach((e) => {
document.documentElement.classList.add(e);
});
} else {
document.documentElement.classList.add('dark');
}
......
......@@ -119,3 +119,57 @@ export const updateUserPassword = async (token: string, password: string, newPas
return res;
};
export const getSignUpEnabledStatus = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const toggleSignUpEnabledStatus = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled/toggle`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
import { WEBUI_API_BASE_URL } from '$lib/constants';
export const setDefaultModels = async (token: string, models: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/models`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
models: models
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
import { OLLAMA_API_BASE_URL } from '$lib/constants';
export const getOllamaVersion = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string = ''
) => {
export const getOllamaAPIUrl = async (token: string = '') => {
let error = null;
const res = await fetch(`${base_url}/version`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/url`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OLLAMA_API_BASE_URL;
};
export const updateOllamaAPIUrl = async (token: string = '', url: string) => {
let error = null;
const res = await fetch(`${OLLAMA_API_BASE_URL}/url/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
},
body: JSON.stringify({
url: url
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OLLAMA_API_BASE_URL;
};
export const getOllamaVersion = async (token: string = '') => {
let error = null;
const res = await fetch(`${OLLAMA_API_BASE_URL}/version`, {
method: 'GET',
headers: {
Accept: 'application/json',
......@@ -35,13 +99,10 @@ export const getOllamaVersion = async (
return res?.version ?? '';
};
export const getOllamaModels = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string = ''
) => {
export const getOllamaModels = async (token: string = '') => {
let error = null;
const res = await fetch(`${base_url}/tags`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/tags`, {
method: 'GET',
headers: {
Accept: 'application/json',
......@@ -67,18 +128,15 @@ export const getOllamaModels = async (
throw error;
}
return res?.models ?? [];
return (res?.models ?? []).sort((a, b) => {
return a.name.localeCompare(b.name);
});
};
export const generateTitle = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string = '',
model: string,
prompt: string
) => {
export const generateTitle = async (token: string = '', model: string, prompt: string) => {
let error = null;
const res = await fetch(`${base_url}/generate`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/generate`, {
method: 'POST',
headers: {
'Content-Type': 'text/event-stream',
......@@ -86,7 +144,7 @@ export const generateTitle = async (
},
body: JSON.stringify({
model: model,
prompt: `Generate a brief 3-5 word title for this question, excluding the term 'title.' Then, please reply with only the title: ${prompt}`,
prompt: `Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title': ${prompt}`,
stream: false
})
})
......@@ -109,14 +167,10 @@ export const generateTitle = async (
return res?.response ?? 'New Chat';
};
export const generateChatCompletion = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string = '',
body: object
) => {
export const generateChatCompletion = async (token: string = '', body: object) => {
let error = null;
const res = await fetch(`${base_url}/chat`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'text/event-stream',
......@@ -135,15 +189,10 @@ export const generateChatCompletion = async (
return res;
};
export const createModel = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string,
tagName: string,
content: string
) => {
export const createModel = async (token: string, tagName: string, content: string) => {
let error = null;
const res = await fetch(`${base_url}/create`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/create`, {
method: 'POST',
headers: {
'Content-Type': 'text/event-stream',
......@@ -165,14 +214,10 @@ export const createModel = async (
return res;
};
export const deleteModel = async (
base_url: string = OLLAMA_API_BASE_URL,
token: string,
tagName: string
) => {
export const deleteModel = async (token: string, tagName: string) => {
let error = null;
const res = await fetch(`${base_url}/delete`, {
const res = await fetch(`${OLLAMA_API_BASE_URL}/delete`, {
method: 'DELETE',
headers: {
'Content-Type': 'text/event-stream',
......@@ -202,3 +247,27 @@ export const deleteModel = async (
return res;
};
export const pullModel = async (token: string, tagName: string) => {
let error = null;
const res = await fetch(`${OLLAMA_API_BASE_URL}/pull`, {
method: 'POST',
headers: {
'Content-Type': 'text/event-stream',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
name: tagName
})
}).catch((err) => {
error = err;
return null;
});
if (error) {
throw error;
}
return res;
};
export const getOpenAIModels = async (
import { OPENAI_API_BASE_URL } from '$lib/constants';
export const getOpenAIUrl = async (token: string = '') => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/url`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OPENAI_API_BASE_URL;
};
export const updateOpenAIUrl = async (token: string = '', url: string) => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/url/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
},
body: JSON.stringify({
url: url
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OPENAI_API_BASE_URL;
};
export const getOpenAIKey = async (token: string = '') => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/key`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OPENAI_API_KEY;
};
export const updateOpenAIKey = async (token: string = '', key: string) => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
},
body: JSON.stringify({
key: key
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
} else {
error = 'Server connection failed';
}
return null;
});
if (error) {
throw error;
}
return res.OPENAI_API_KEY;
};
export const getOpenAIModels = async (token: string = '') => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/models`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = `OpenAI: ${err?.error?.message ?? 'Network Problem'}`;
return [];
});
if (error) {
throw error;
}
const models = Array.isArray(res) ? res : res?.data ?? null;
return models
? models
.map((model) => ({ name: model.id, external: true }))
.sort((a, b) => {
return a.name.localeCompare(b.name);
})
: models;
};
export const getOpenAIModelsDirect = async (
base_url: string = 'https://api.openai.com/v1',
api_key: string = ''
) => {
......@@ -25,9 +197,35 @@ export const getOpenAIModels = async (
throw error;
}
let models = Array.isArray(res) ? res : res?.data ?? null;
const models = Array.isArray(res) ? res : res?.data ?? null;
return models
.map((model) => ({ name: model.id, external: true }))
.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true));
.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true))
.sort((a, b) => {
return a.name.localeCompare(b.name);
});
};
export const generateOpenAIChatCompletion = async (token: string = '', body: object) => {
let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/chat/completions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).catch((err) => {
console.log(err);
error = err;
return null;
});
if (error) {
throw error;
}
return res;
};
import { WEBUI_API_BASE_URL } from '$lib/constants';
export const createNewPrompt = async (
token: string,
command: string,
title: string,
content: string
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/create`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: JSON.stringify({
command: `/${command}`,
title: title,
content: content
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const getPrompts = async (token: string = '') => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const getPromptByCommand = async (token: string, command: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${command}`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const updatePromptByCommand = async (
token: string,
command: string,
title: string,
content: string
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${command}/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: JSON.stringify({
command: `/${command}`,
title: title,
content: content
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const deletePromptByCommand = async (token: string, command: string) => {
let error = null;
command = command.charAt(0) === '/' ? command.slice(1) : command;
const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${command}/delete`, {
method: 'DELETE',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
......@@ -84,3 +84,43 @@ export const deleteUserById = async (token: string, userId: string) => {
return res;
};
type UserUpdateForm = {
profile_image_url: string;
email: string;
name: string;
password: string;
};
export const updateUserById = async (token: string, userId: string, user: UserUpdateForm) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
profile_image_url: user.profile_image_url,
email: user.email,
name: user.name,
password: user.password !== '' ? user.password : undefined
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -7,7 +7,7 @@
{#each suggestionPrompts as prompt, promptIdx}
<div class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px]">
<button
class=" flex-1 flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
class=" flex-1 flex justify-between w-full h-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
on:click={() => {
submitPrompt(prompt.content);
}}
......@@ -17,7 +17,9 @@
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
<div class="text-sm text-gray-500">{prompt.title[1]}</div>
{:else}
<div class=" self-center text-sm font-medium dark:text-gray-300">{prompt.content}</div>
<div class=" self-center text-sm font-medium dark:text-gray-300 line-clamp-2">
{prompt.content}
</div>
{/if}
</div>
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment