Add support for GGUF models (#866)

This commit is contained in:
Juan Calderon-Perez
2023-11-17 09:07:02 -05:00
committed by GitHub
parent f98ab2e781
commit c7f0b487aa
22 changed files with 767 additions and 1089 deletions

View File

@@ -45,4 +45,4 @@ jobs:
- name: Run model health check
working-directory: ./api
run: |
poetry run python -m pytest test/healthcheck_models.py
poetry run python -m pytest -v --color=yes test/healthcheck_models.py

View File

@@ -3,7 +3,7 @@
![License](https://img.shields.io/github/license/serge-chat/serge)
[![Discord](https://img.shields.io/discord/1088427963801948201?label=Discord)](https://discord.gg/62Hc6FEYQH)
Serge is a chat interface crafted with [llama.cpp](https://github.com/ggerganov/llama.cpp) for running Alpaca models. No API keys, entirely self-hosted!
Serge is a chat interface crafted with [llama.cpp](https://github.com/ggerganov/llama.cpp) for running GGUF models. No API keys, entirely self-hosted!
- 🌐 **SvelteKit** frontend
- 💾 **[Redis](https://github.com/redis/redis)** for storing chat history & parameters
@@ -57,18 +57,10 @@ Instructions for setting up Serge on Kubernetes can be found in the [wiki](https
| Category | Models |
|:-------------:|:-------|
| **Alpaca 🦙** | Alpaca-LoRA-65B, GPT4-Alpaca-LoRA-30B |
| **Chronos 🌑**| Chronos-13B, Chronos-33B, Chronos-Hermes-13B |
| **GPT4All 🌍**| GPT4All-13B |
| **Koala 🐨** | Koala-7B, Koala-13B |
| **LLaMA 🦙** | FinLLaMA-33B, LLaMA-Supercot-30B, LLaMA2 7B, LLaMA2 13B, LLaMA2 70B |
| **Lazarus 💀**| Lazarus-30B |
| **Nous 🧠** | Nous-Hermes-13B |
| **OpenAssistant 🎙️** | OpenAssistant-30B |
| **Orca 🐬** | Orca-Mini-v2-7B, Orca-Mini-v2-13B, OpenOrca-Preview1-13B |
| **Samantha 👩**| Samantha-7B, Samantha-13B, Samantha-33B |
| **Vicuna 🦙** | Stable-Vicuna-13B, Vicuna-CoT-7B, Vicuna-CoT-13B, Vicuna-v1.1-7B, Vicuna-v1.1-13B, VicUnlocked-30B, VicUnlocked-65B |
| **Wizard 🧙** | Wizard-Mega-13B, WizardLM-Uncensored-7B, WizardLM-Uncensored-13B, WizardLM-Uncensored-30B, WizardCoder-Python-13B-V1.0 |
| **CodeLLaMA** | 7B, 13B |
| **LLaMA** | 7B, 13B, 70B |
| **Mistral** | 7B-Instruct, 7B-OpenOrca |
| **Zephyr** | 7B-Alpha, 7B-Beta |
Additional weights can be added to the `serge_weights` volume using `docker cp`:
@@ -80,45 +72,6 @@ docker cp ./my_weight.bin serge:/usr/src/app/weights/
LLaMA will crash if you don't have enough available memory for the model:
| Model | Max RAM Required |
|-------------|------------------|
| 7B | 4.5GB |
| 7B-q2_K | 5.37GB |
| 7B-q3_K_L | 6.10GB |
| 7B-q4_1 | 6.71GB |
| 7B-q4_K_M | 6.58GB |
| 7B-q5_1 | 7.56GB |
| 7B-q5_K_M | 7.28GB |
| 7B-q6_K | 8.03GB |
| 7B-q8_0 | 9.66GB |
| 13B | 12GB |
| 13B-q2_K | 8.01GB |
| 13B-q3_K_L | 9.43GB |
| 13B-q4_1 | 10.64GB |
| 13B-q4_K_M | 10.37GB |
| 13B-q5_1 | 12.26GB |
| 13B-q5_K_M | 11.73GB |
| 13B-q6_K | 13.18GB |
| 13B-q8_0 | 16.33GB |
| 33B | 20GB |
| 33B-q2_K | 16.21GB |
| 33B-q3_K_L | 19.78GB |
| 33B-q4_1 | 22.83GB |
| 33B-q4_K_M | 22.12GB |
| 33B-q5_1 | 26.90GB |
| 33B-q5_K_M | 25.55GB |
| 33B-q6_K | 29.19GB |
| 33B-q8_0 | 37.06GB |
| 65B | 50GB |
| 65B-q2_K | 29.95GB |
| 65B-q3_K_L | 37.15GB |
| 65B-q4_1 | 43.31GB |
| 65B-q4_K_M | 41.85GB |
| 65B-q5_1 | 51.47GB |
| 65B-q5_K_M | 48.74GB |
| 65B-q6_K | 56.06GB |
| 65B-q8_0 | 71.87GB |
## 💬 Support
Need help? Join our [Discord](https://discord.gg/62Hc6FEYQH)
@@ -139,4 +92,4 @@ To run Serge in development mode:
```bash
git clone https://github.com/serge-chat/serge.git
docker compose -f docker-compose.dev.yml up -d --build
```
```

View File

@@ -1 +1,2 @@
./weights/*.bin**
./weights/*.bin**
./weights/*.gguf**

View File

@@ -1,539 +1,140 @@
[
{
"name": "Chronos",
"name": "CodeLLaMA",
"models": [
{
"name": "Chronos-13B",
"repo": "TheBloke/chronos-13B-GGML",
"name": "CodeLlama-7B",
"repo": "TheBloke/CodeLlama-7B-GGUF",
"files": [
{
"name": "q6_K",
"filename": "chronos-13b.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
"name": "q4_K_M",
"filename": "codellama-7b.Q4_K_M.gguf",
"disk_space": 4080000000.0
}
]
},
{
"name": "Chronos-33B",
"repo": "TheBloke/chronos-33B-GGML",
"name": "CodeLlama-7B-Instruct",
"repo": "TheBloke/CodeLlama-7B-Instruct-GGUF",
"files": [
{
"name": "q6_K",
"filename": "chronos-33b.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
"name": "q4_K_M",
"filename": "codellama-7b-instruct.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
},
{
"name": "Chronos-Hermes-13B",
"repo": "TheBloke/chronos-hermes-13B-GGML",
"name": "CodeLlama-13B-Instruct",
"repo": "TheBloke/CodeLlama-13B-Instruct-GGUF",
"files": [
{
"name": "q4_0",
"filename": "chronos-hermes-13b.ggmlv3.q4_0.bin",
"disk_space": 73200000000.0
},
{
"name": "q4_1",
"filename": "chronos-hermes-13b.ggmlv3.q4_1.bin",
"disk_space": 81400000000.0
},
{
"name": "q5_0",
"filename": "chronos-hermes-13b.ggmlv3.q5_0.bin",
"disk_space": 89500000000.0
},
{
"name": "q5_1",
"filename": "chronos-hermes-13b.ggmlv3.q5_1.bin",
"disk_space": 97600000000.0
},
{
"name": "q8_0",
"filename": "chronos-hermes-13b.ggmlv3.q8_0.bin",
"disk_space": 13800000000.0
"name": "q4_K_M",
"filename": "codellama-13b-instruct.Q4_K_M.gguf",
"disk_space": 7870000000.0
}
]
}
]
},
{
"name": "Koala",
"name": "Zephyr",
"models": [
{
"name": "Koala-7B",
"repo": "TheBloke/koala-7B-GGML",
"name": "Zephyr-7B-Alpha",
"repo": "TheBloke/zephyr-7B-alpha-GGUF",
"files": [
{
"name": "q5_1",
"filename": "koala-7B.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q6_K",
"filename": "koala-7B.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
"name": "q4_K_M",
"filename": "zephyr-7b-alpha.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
},
{
"name": "Koala-13B",
"repo": "TheBloke/koala-13B-GGML",
"name": "Zephyr-7B-Beta",
"repo": "TheBloke/zephyr-7B-beta-GGUF",
"files": [
{
"name": "q6_K",
"filename": "koala-13B.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
"name": "q4_K_M",
"filename": "zephyr-7b-beta.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
}
]
},
{
"name": "Samantha",
"name": "Mistral",
"models": [
{
"name": "Samantha-7B",
"repo": "TheBloke/samantha-7B-GGML",
"name": "Mistral-7B-Instruct",
"repo": "TheBloke/Mistral-7B-Instruct-v0.1-GGUF",
"files": [
{
"name": "q5_1",
"filename": "samantha-7B.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q6_K",
"filename": "samantha-7B.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
"name": "q4_K_M",
"filename": "mistral-7b-instruct-v0.1.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
},
{
"name": "Samantha-13B",
"repo": "TheBloke/samantha-13B-GGML",
"name": "Mistral-7B",
"repo": "TheBloke/Mistral-7B-v0.1-GGUF",
"files": [
{
"name": "q6_K",
"filename": "samantha-13B.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
"name": "q4_K_M",
"filename": "mistral-7b-v0.1.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
},
{
"name": "Samantha-33B",
"repo": "TheBloke/samantha-33B-GGML",
"name": "Mistral-7B-OpenOrca",
"repo": "TheBloke/Mistral-7B-OpenOrca-GGUF",
"files": [
{
"name": "q6_K",
"filename": "samantha-33B.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
"name": "q4_K_M",
"filename": "mistral-7b-openorca.Q4_K_M.gguf",
"disk_space": 4370000000.0
}
]
}
]
},
{
"name": "Vicuna",
"name": "LLaMA",
"models": [
{
"name": "StableVicuna-13B",
"repo": "TheBloke/stable-vicuna-13B-GGML",
"name": "LLaMA2-7B-Chat",
"repo": "TheBloke/Llama-2-7B-Chat-GGUF",
"files": [
{
"name": "q6_K",
"filename": "stable-vicuna-13B.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
"name": "q4_K_M",
"filename": "llama-2-7b-chat.Q4_K_M.gguf",
"disk_space": 4080000000.0
}
]
},
{
"name": "Vicuna-CoT-7B",
"repo": "TheBloke/Vicuna-7B-CoT-GGML",
"name": "LLaMA2-13B-Chat",
"repo": "TheBloke/Llama-2-13B-chat-GGUF",
"files": [
{
"name": "q5_1",
"filename": "vicuna-7B-cot.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q6_K",
"filename": "vicuna-7B-cot.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
"name": "q4_K_M",
"filename": "llama-2-13b-chat.Q4_K_M.gguf",
"disk_space": 7870000000.0
}
]
},
{
"name": "Vicuna-CoT-13B",
"repo": "TheBloke/Vicuna-13B-CoT-GGML",
"name": "LLaMA2-70B-Chat",
"repo": "TheBloke/Llama-2-70B-Chat-GGUF",
"files": [
{
"name": "q6_K",
"filename": "vicuna-13b-cot.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
}
]
},
{
"name": "Vicuna-v1.1-7B",
"repo": "TheBloke/vicuna-7B-1.1-GGML",
"files": [
{
"name": "q5_1",
"filename": "vicuna-7b-1.1.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q6_K",
"filename": "vicuna-7b-1.1.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
}
]
},
{
"name": "Vicuna-v1.1-13B",
"repo": "TheBloke/vicuna-13b-1.1-GGML",
"files": [
{
"name": "q6_K",
"filename": "vicuna-13b-1.1.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
}
]
},
{
"name": "VicUnlocked-30B",
"repo": "TheBloke/VicUnlocked-30B-LoRA-GGML",
"files": [
{
"name": "q6_K",
"filename": "VicUnlocked-30B-LoRA.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
}
]
},
{
"name": "VicUnlocked-65B",
"repo": "TheBloke/VicUnlocked-alpaca-65B-QLoRA-GGML",
"files": [
{
"name": "q5_K_M",
"filename": "vicunlocked-65b.ggmlv3.q5_K_M.bin",
"disk_space": 58700000000.0
}
]
}
]
},
{
"name": "WizardLM",
"models": [
{
"name": "WizardCoder-Python-13B-V1.0-GGML",
"repo": "TheBloke/WizardCoder-Python-13B-V1.0-GGML",
"files": [
{
"name": "q5_K_M",
"filename": "wizardcoder-python-13b-v1.0.ggmlv3.Q5_K_M.bin",
"disk_space": 24100000000.0
},
{
"name": "q6_K",
"filename": "wizardcoder-python-13b-v1.0.ggmlv3.Q6_K.bin",
"disk_space": 27900000000.0
}
]
},
{
"name": "WizardLM-Uncensored-7B",
"repo": "TheBloke/WizardLM-7B-uncensored-GGML",
"files": [
{
"name": "q5_1",
"filename": "WizardLM-7B-uncensored.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q6_K",
"filename": "WizardLM-7B-uncensored.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
}
]
},
{
"name": "WizardLM-Uncensored-13B",
"repo": "TheBloke/WizardLM-13B-Uncensored-GGML",
"files": [
{
"name": "q6_K",
"filename": "wizardLM-13B-Uncensored.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
}
]
},
{
"name": "WizardLM-Uncensored-30B",
"repo": "TheBloke/WizardLM-30B-Uncensored-GGML",
"files": [
{
"name": "q6_K",
"filename": "wizardlm-30b-uncensored.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
}
]
}
]
},
{
"name": "Alpaca",
"models": [
{
"name": "GPT4AlpacaLoRA-30B",
"repo": "TheBloke/gpt4-alpaca-lora-30B-4bit-GGML",
"files": [
{
"name": "q5_1",
"filename": "gpt4-alpaca-lora-30b.ggmlv3.q5_1.bin",
"disk_space": 24400000000.0
}
]
},
{
"name": "AlpacaLoRA-65B",
"repo": "TheBloke/alpaca-lora-65B-GGML",
"files": [
{
"name": "q5_K_M",
"filename": "alpaca-lora-65B.ggmlv3.q5_K_M.bin",
"disk_space": 46200000000.0
}
]
}
]
},
{
"name": "Llama",
"models": [
{
"name": "FinLlama-33B",
"repo": "TheBloke/fin-llama-33B-GGML",
"files": [
{
"name": "q6_K",
"filename": "fin-llama-33b.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
}
]
},
{
"name": "Llama-Supercot-30B",
"repo": "TheBloke/llama-30b-supercot-GGML",
"files": [
{
"name": "q6_K",
"filename": "llama-30b-supercot.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
}
]
},
{
"name": "Facebook-LLaMA2-7B",
"repo": "TheBloke/Llama-2-7B-GGML",
"files": [
{
"name": "q4_0",
"filename": "llama-2-7b.ggmlv3.q4_0.bin",
"disk_space": 3790000000.0
}
]
},
{
"name": "Facebook-LLaMA2-13B",
"repo": "TheBloke/Llama-2-13B-chat-GGML",
"files": [
{
"name": "q4_0",
"filename": "llama-2-13b-chat.ggmlv3.q4_1.bin",
"disk_space": 8140000000.0
}
]
},
{
"name": "Facebook-LLaMA2-70B",
"repo": "TheBloke/Llama-2-70B-Chat-GGML",
"files": [
{
"name": "q4_0",
"filename": "llama-2-70b-chat.ggmlv3.q4_0.bin",
"disk_space": 38900000000.0
}
]
}
]
},
{
"name": "Orca",
"models": [
{
"name": "Orca-Mini-v2-7B",
"repo": "TheBloke/orca_mini_v2_7B-GGML",
"files": [
{
"name": "q5_0",
"filename": "orca-mini-v2_7b.ggmlv3.q5_0.bin",
"disk_space": 4630000000.0
},
{
"name": "q5_1",
"filename": "orca-mini-v2_7b.ggmlv3.q5_1.bin",
"disk_space": 5060000000.0
},
{
"name": "q5_K_M",
"filename": "orca-mini-v2_7b.ggmlv3.q5_K_M.bin",
"disk_space": 4780000000.0
},
{
"name": "q6_K",
"filename": "orca-mini-v2_7b.ggmlv3.q6_K.bin",
"disk_space": 5530000000.0
},
{
"name": "q8_0",
"filename": "orca-mini-v2_7b.ggmlv3.q8_0.bin",
"disk_space": 7160000000.0
}
]
},
{
"name": "Orca-Mini-v2-13B",
"repo": "TheBloke/orca_mini_v2_13b-GGML",
"files": [
{
"name": "q5_0",
"filename": "orca_mini_v2_13b.ggmlv3.q5_0.bin",
"disk_space": 8950000000.0
},
{
"name": "q5_1",
"filename": "orca_mini_v2_13b.ggmlv3.q5_1.bin",
"disk_space": 9760000000.0
},
{
"name": "q5_K_M",
"filename": "orca_mini_v2_13b.ggmlv3.q5_K_M.bin",
"disk_space": 9230000000.0
},
{
"name": "q6_K",
"filename": "orca_mini_v2_13b.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
},
{
"name": "q8_0",
"filename": "orca_mini_v2_13b.ggmlv3.q8_0.bin",
"disk_space": 13800000000.0
}
]
},
{
"name": "OpenOrca-Preview1-13B",
"repo": "TheBloke/OpenOrca-Preview1-13B-GGML",
"files": [
{
"name": "q5_0",
"filename": "openorca-preview1-200k-llama-13b.ggmlv3.q5_0.bin",
"disk_space": 89500000000.0
},
{
"name": "q5_1",
"filename": "openorca-preview1-200k-llama-13b.ggmlv3.q5_1.bin",
"disk_space": 97600000000.0
},
{
"name": "q5_K_M",
"filename": "openorca-preview1-200k-llama-13b.ggmlv3.q5_K_M.bin",
"disk_space": 92300000000.0
},
{
"name": "q6_K",
"filename": "openorca-preview1-200k-llama-13b.ggmlv3.q6_K.bin",
"disk_space": 107000000000.0
},
{
"name": "q8_0",
"filename": "openorca-preview1-200k-llama-13b.ggmlv3.q8_0.bin",
"disk_space": 13800000000.0
}
]
}
]
},
{
"name": "misc",
"models": [
{
"name": "Marx-3B-V2",
"repo": "NikolayKozloff/Marx-3B-V2",
"files": [
{
"name": "q4_1",
"filename": "Marx-3B-V2-Q4_1-GGML.bin",
"disk_space": 2140000000.0
}
]
},
{
"name": "Wizard-Mega-13B",
"repo": "TheBloke/wizard-mega-13B-GGML",
"files": [
{
"name": "q5_1",
"filename": "wizard-mega-13B.ggmlv3.q5_1.bin",
"disk_space": 9760000000.0
}
]
},
{
"name": "Lazarus-30B",
"repo": "TheBloke/30B-Lazarus-GGML",
"files": [
{
"name": "q6_K",
"filename": "30b-Lazarus.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
}
]
},
{
"name": "GPT4All-13B",
"repo": "TheBloke/GPT4All-13B-snoozy-GGML",
"files": [
{
"name": "q6_K",
"filename": "GPT4All-13B-snoozy.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
}
]
},
{
"name": "Nous-Hermes-13B",
"repo": "TheBloke/Nous-Hermes-13B-GGML",
"files": [
{
"name": "q6_K",
"filename": "nous-hermes-13b.ggmlv3.q6_K.bin",
"disk_space": 10700000000.0
}
]
},
{
"name": "OpenAssistant-30B",
"repo": "TheBloke/OpenAssistant-SFT-7-Llama-30B-GGML",
"files": [
{
"name": "q6_K",
"filename": "OpenAssistant-SFT-7-Llama-30B.ggmlv3.q6_K.bin",
"disk_space": 26700000000.0
"name": "q4_K_M",
"filename": "llama-2-70b-chat.Q4_K_M.gguf",
"disk_space": 41420000000.0
}
]
}

View File

@@ -32,7 +32,7 @@ async def create_new_chat(
):
try:
client = Llama(
model_path="/usr/src/app/weights/" + model + ".bin",
model_path=f"/usr/src/app/weights/{model}.bin",
)
del client
except Exception as exc:
@@ -136,7 +136,8 @@ async def delete_prompt(chat_id: str, idx: int):
history = RedisChatMessageHistory(chat_id)
if idx >= len(history.messages):
raise ValueError("Index out of range")
logger.error("Unable to delete message, chat in progress")
return False
messages = history.messages.copy()[:idx]
history.clear()
@@ -195,7 +196,7 @@ def stream_ask_a_question(chat_id: str, prompt: str):
logger.debug("creating Llama client")
try:
client = Llama(
model_path="/usr/src/app/weights/" + chat.params.model_path + ".bin",
model_path=f"/usr/src/app/weights/{chat.params.model_path}.bin",
n_ctx=len(chat.params.init_prompt) + chat.params.n_ctx,
n_gpu_layers=chat.params.n_gpu_layers,
n_threads=chat.params.n_threads,
@@ -264,7 +265,7 @@ async def ask_a_question(chat_id: str, prompt: str):
try:
client = Llama(
model_path="/usr/src/app/weights/" + chat.params.model_path + ".bin",
model_path=f"/usr/src/app/weights/{chat.params.model_path}.bin",
n_ctx=len(chat.params.init_prompt) + chat.params.n_ctx,
n_threads=chat.params.n_threads,
n_gpu_layers=chat.params.n_gpu_layers,

View File

@@ -1,11 +1,10 @@
import asyncio
import os
import urllib.request
import requests
import shutil
from fastapi import APIRouter, HTTPException
import huggingface_hub
from typing import Annotated
from fastapi import APIRouter, HTTPException, Form
from serge.models.models import Families
from pathlib import Path
@@ -32,66 +31,46 @@ for family in families.__root__:
)
@model_router.post("/refresh")
async def refresh_models(url: Annotated[str, Form()]):
"""
Refreshes the list of models available for download.
"""
global models_info
r = requests.get(url)
if not r.ok:
raise HTTPException(status_code=500, detail="Could not refresh models using the link provided.")
families = Families.parse_obj(r.json())
models_info = {}
for family in families.__root__:
for model in family.models:
for file in model.files:
models_info[model.name] = (
model.repo,
file.filename,
file.disk_space,
)
return
# Helper functions
async def is_model_installed(model_name: str) -> bool:
installed_models = await list_of_installed_models()
return f"{model_name}.bin" in installed_models
async def get_file_size(file_path: str) -> int:
return os.stat(file_path).st_size
# Handlers
@model_router.get("/all")
async def list_of_all_models():
res = []
installed_models = await list_of_installed_models()
resp = []
for model in models_info.keys():
progress = await download_status(model)
if f"{model}.bin" in installed_models:
if await is_model_installed(model):
available = True
# if model exists in WEIGHTS directory remove it from the list
installed_models.remove(f"{model}.bin")
else:
available = False
res.append(
resp.append(
{
"name": model,
"size": models_info[model][2],
"available": available,
"progress": progress,
"progress": await download_status(model),
}
)
# append the rest of the models
for model in installed_models:
# .bin is removed for compatibility with generate.py
res.append(
resp.append(
{
"name": model.replace(".bin", "").lstrip("/"),
"size": os.stat(WEIGHTS + model).st_size,
"size": await get_file_size(WEIGHTS + model),
"available": True,
"progress": None,
"progress": 100.0,
}
)
return res
return resp
@model_router.get("/downloadable")
@@ -108,7 +87,7 @@ async def list_of_downloadable_models():
async def list_of_installed_models():
# after iterating through the WEIGHTS directory, return location and filename
files = [
model_location.replace(WEIGHTS, "") + "/" + bin_file
f"{model_location.replace(WEIGHTS, '')}/{bin_file}"
for model_location, directory, filenames in os.walk(WEIGHTS)
for bin_file in filenames
if os.path.splitext(bin_file)[1] == ".bin"
@@ -118,55 +97,76 @@ async def list_of_installed_models():
@model_router.post("/{model_name}/download")
def download_model(model_name: str):
models = list(models_info.keys())
if model_name not in models:
async def download_model(model_name: str):
if model_name not in models_info:
raise HTTPException(status_code=404, detail="Model not found")
if not os.path.exists(WEIGHTS + "tokenizer.model"):
print("Downloading tokenizer...")
url = huggingface_hub.hf_hub_url(
"nsarrazin/alpaca",
"alpaca-7B-ggml/tokenizer.model",
repo_type="model",
revision="main",
try:
# Download file, and resume broken downloads
model_repo, filename, _ = models_info[model_name]
model_path = f"{WEIGHTS}{model_name}.bin"
await asyncio.to_thread(
huggingface_hub.hf_hub_download, repo_id=model_repo, filename=filename, local_dir=WEIGHTS, cache_dir=WEIGHTS, resume_download=True
)
urllib.request.urlretrieve(url, WEIGHTS + "tokenizer.model")
repo_id, filename, _ = models_info[model_name]
print(f"Downloading {model_name} model from {repo_id}...")
url = huggingface_hub.hf_hub_url(repo_id, filename, repo_type="model", revision="main")
urllib.request.urlretrieve(url, WEIGHTS + f"{model_name}.bin.tmp")
os.rename(WEIGHTS + f"{model_name}.bin.tmp", WEIGHTS + f"{model_name}.bin")
return {"message": f"Model {model_name} downloaded"}
# Rename file
os.rename(os.path.join(WEIGHTS, filename), os.path.join(WEIGHTS, model_path))
return {"message": f"Model {model_name} downloaded"}
except Exception as e:
# Handle exceptions, possibly log them
raise HTTPException(status_code=500, detail=f"Error downloading model: {str(e)}")
@model_router.get("/{model_name}/download/status")
async def download_status(model_name: str):
models = list(models_info.keys())
if model_name not in models:
if model_name not in models_info:
raise HTTPException(status_code=404, detail="Model not found")
filesize = models_info[model_name][2]
model_repo, _, _ = models_info[model_name]
bin_path = WEIGHTS + f"{model_name}.bin.tmp"
# Construct the path to the blobs directory
blobs_dir = os.path.join(WEIGHTS, f"models--{model_repo.replace('/', '--')}", "blobs")
if os.path.exists(bin_path):
currentsize = os.path.getsize(bin_path)
# Check for the .incomplete file in the blobs directory
if os.path.exists(os.path.join(WEIGHTS, f"{model_name}.bin")):
currentsize = os.path.getsize(os.path.join(WEIGHTS, f"{model_name}.bin"))
return min(round(currentsize / filesize * 100, 1), 100)
return None
elif os.path.exists(blobs_dir):
for file in os.listdir(blobs_dir):
if file.endswith(".incomplete"):
incomplete_file_path = os.path.join(blobs_dir, file)
# Check if the .incomplete file exists and calculate the download status
if os.path.exists(incomplete_file_path):
currentsize = os.path.getsize(incomplete_file_path)
return min(round(currentsize / filesize * 100, 1), 100)
return 0
@model_router.delete("/{model_name}")
async def delete_model(model_name: str):
if model_name + ".bin" not in await list_of_installed_models():
if f"{model_name}.bin" not in await list_of_installed_models():
raise HTTPException(status_code=404, detail="Model not found")
if os.path.exists(WEIGHTS + f"{model_name}.bin"):
os.remove(WEIGHTS + f"{model_name}.bin")
return {"message": f"Model {model_name} deleted"}
model_repo, _, _ = models_info.get(model_name, (None, None, None))
if not model_repo:
raise HTTPException(status_code=404, detail="Model info not found")
raise HTTPException(status_code=404, detail="Model file not found")
# Remove link to model file
try:
os.remove(os.path.join(WEIGHTS, f"{model_name}.bin"))
except OSError as e:
print(f"Error removing model file: {e}")
# Remove lock file
try:
shutil.rmtree(os.path.join(WEIGHTS, ".locks", f"models--{model_repo.replace('/', '--')}"))
except OSError as e:
print(f"Error removing lock directory: {e}")
# Remove cache directory
try:
shutil.rmtree(os.path.join(WEIGHTS, f"models--{model_repo.replace('/', '--')}"))
except OSError as e:
print(f"Error removing cache directory: {e}")
return {"message": f"Model {model_name} deleted"}

View File

@@ -51,7 +51,7 @@ def get_prompt(history: RedisChatMessageHistory, params):
else:
stop = True
if len(next_prompt) > 0:
prompts.append(instruction + next_prompt + "\n")
prompts.append(f"{instruction + next_prompt}\n")
if stop:
break
@@ -60,6 +60,6 @@ def get_prompt(history: RedisChatMessageHistory, params):
for next_prompt in prompts:
message_prompt += next_prompt
final_prompt = params.init_prompt + "\n" + message_prompt[: params.n_ctx]
final_prompt = f"{params.init_prompt}\n{message_prompt[:params.n_ctx]}"
logger.debug(final_prompt)
return final_prompt

View File

@@ -1,26 +1,36 @@
import json
from pathlib import Path
import huggingface_hub
import pytest
import requests
from huggingface_hub import hf_hub_url
import pytest
def load_model_data(file_path):
with open(file_path, "r") as models_file:
return json.load(models_file)
def flatten_model_data(families):
for family in families:
for model in family["models"]:
for file in model["files"]:
yield model["repo"], file["filename"]
def check_model_availability(repo, filename):
url = hf_hub_url(repo, filename, repo_type="model", revision="main")
response = requests.head(url)
if response.ok:
return True
else:
return False
# this test file specifically doesn't start with test_* so it's not picked up by pytest
test_dir = Path(__file__).parent
with open(test_dir.parent / "src/serge/data/models.json", "r") as models_file:
families = json.load(models_file)
# generate list of checks
checks = []
for family in families:
for model in family["models"]:
for file in model["files"]:
checks.append((model["repo"], file["filename"]))
model_data = load_model_data(test_dir.parent / "src/serge/data/models.json")
checks = list(flatten_model_data(model_data))
@pytest.mark.parametrize("repo,filename", checks)
def test_model_available(repo, filename):
url = huggingface_hub.hf_hub_url(repo, filename, repo_type="model", revision="main")
r = requests.head(url)
assert r.ok, f"Model {repo}/{filename} not available"
assert check_model_availability(repo, filename), f"Model {repo}/{filename} not available"

View File

@@ -10,7 +10,7 @@ _term() {
}
# Install python bindings
UNAME_M=$(dpkg --print-architecture) pip install llama-cpp-python==0.1.78 || {
UNAME_M=$(dpkg --print-architecture) pip install llama-cpp-python==0.2.18 || {
echo 'Failed to install llama-cpp-python'
exit 1
}

View File

@@ -9,7 +9,7 @@ pip install -e ./api || {
}
# Install python bindings
UNAME_M=$(dpkg --print-architecture) pip install llama-cpp-python==0.1.78 || {
UNAME_M=$(dpkg --print-architecture) pip install llama-cpp-python==0.2.18 || {
echo 'Failed to install llama-cpp-python'
exit 1
}

305
web/package-lock.json generated
View File

@@ -11,21 +11,21 @@
"@iconify/svelte": "^3.1.4",
"@sveltestack/svelte-query": "^1.6.0",
"clipboard": "^2.0.11",
"daisyui": "^3.9.4",
"daisyui": "^4.3.1",
"highlight.js": "^11.9.0",
"markdown-it": "^13.0.2",
"markdown-it-highlightjs": "^4.0.1",
"prettier-plugin-svelte": "^3.0.3",
"prettier-plugin-svelte": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.1.1",
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.4",
"@sveltejs/kit": "^1.27.6",
"@types/markdown-it": "^13.0.6",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"autoprefixer": "^10.4.16",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
@@ -34,9 +34,9 @@
"eslint-plugin-svelte": "^2.35.0",
"eslint-plugin-vue": "^9.18.1",
"postcss": "^8.4.31",
"prettier": "3.0.3",
"svelte": "^4.2.2",
"svelte-check": "^3.5.2",
"prettier": "3.1.0",
"svelte": "^4.2.4",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.3.5",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
@@ -56,6 +56,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -591,6 +592,7 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -603,6 +605,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"engines": {
"node": ">= 8"
}
@@ -611,6 +614,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -774,9 +778,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "1.27.4",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.27.4.tgz",
"integrity": "sha512-Vxl8Jf0C1+/8i/slsxFOnwJntCBDLueO/O6GJ0390KUnyW3Zs+4ZiIinD+cEcYnJPQQ9CRzVSr9Bn6DbmTn4Dw==",
"version": "1.27.6",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.27.6.tgz",
"integrity": "sha512-GsjTkMbKzXdbeRg0tk8S7HNShQ4879ftRr0ZHaZfjbig1xQwG57Bvcm9U9/mpLJtCapLbLWUnygKrgcLISLC8A==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -805,22 +809,10 @@
"vite": "^4.0.0"
}
},
"node_modules/@sveltejs/kit/node_modules/magic-string": {
"version": "0.30.2",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz",
"integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.1.tgz",
"integrity": "sha512-ZD9MibRc1dZVxLwDKCdsv9SW1bTBSi+7cyqGWisT9qEOO076Ojw2DYhIF3UTa8iUchbOwvexjme0P4kvKR//7g==",
"node_modules/@sveltejs/kit/node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.2.tgz",
"integrity": "sha512-Dfy0Rbl+IctOVfJvWGxrX/3m6vxPLH8o0x+8FA5QEyMUQMo4kGOVIojjryU7YomBAexOTAuYf1RT7809yDziaA==",
"dev": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^1.0.4",
@@ -839,7 +831,7 @@
"vite": "^4.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"node_modules/@sveltejs/kit/node_modules/@sveltejs/vite-plugin-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz",
"integrity": "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==",
@@ -856,7 +848,7 @@
"vite": "^4.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte/node_modules/magic-string": {
"node_modules/@sveltejs/kit/node_modules/magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
@@ -927,9 +919,9 @@
"dev": true
},
"node_modules/@types/pug": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz",
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.9.tgz",
"integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==",
"dev": true
},
"node_modules/@types/resolve": {
@@ -945,16 +937,16 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz",
"integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz",
"integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.10.0",
"@typescript-eslint/type-utils": "6.10.0",
"@typescript-eslint/utils": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0",
"@typescript-eslint/scope-manager": "6.11.0",
"@typescript-eslint/type-utils": "6.11.0",
"@typescript-eslint/utils": "6.11.0",
"@typescript-eslint/visitor-keys": "6.11.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -980,15 +972,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz",
"integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz",
"integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.10.0",
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/typescript-estree": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0",
"@typescript-eslint/scope-manager": "6.11.0",
"@typescript-eslint/types": "6.11.0",
"@typescript-eslint/typescript-estree": "6.11.0",
"@typescript-eslint/visitor-keys": "6.11.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1008,13 +1000,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz",
"integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz",
"integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0"
"@typescript-eslint/types": "6.11.0",
"@typescript-eslint/visitor-keys": "6.11.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -1025,13 +1017,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz",
"integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz",
"integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "6.10.0",
"@typescript-eslint/utils": "6.10.0",
"@typescript-eslint/typescript-estree": "6.11.0",
"@typescript-eslint/utils": "6.11.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
@@ -1052,9 +1044,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz",
"integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz",
"integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -1065,13 +1057,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz",
"integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz",
"integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/visitor-keys": "6.10.0",
"@typescript-eslint/types": "6.11.0",
"@typescript-eslint/visitor-keys": "6.11.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -1092,17 +1084,17 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz",
"integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz",
"integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.10.0",
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/typescript-estree": "6.10.0",
"@typescript-eslint/scope-manager": "6.11.0",
"@typescript-eslint/types": "6.11.0",
"@typescript-eslint/typescript-estree": "6.11.0",
"semver": "^7.5.4"
},
"engines": {
@@ -1117,12 +1109,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz",
"integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz",
"integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.10.0",
"@typescript-eslint/types": "6.11.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -1202,12 +1194,14 @@
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -1219,7 +1213,8 @@
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true
},
"node_modules/argparse": {
"version": "2.0.1",
@@ -1411,7 +1406,8 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/big-integer": {
"version": "1.6.51",
@@ -1426,6 +1422,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -1452,6 +1449,7 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1461,6 +1459,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -1607,6 +1606,7 @@
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -1633,6 +1633,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -1688,15 +1689,11 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/colord": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -1710,7 +1707,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/cookie": {
"version": "0.5.0",
@@ -1767,16 +1765,23 @@
"node": ">=4"
}
},
"node_modules/culori": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/culori/-/culori-3.2.0.tgz",
"integrity": "sha512-HIEbTSP7vs1mPq/2P9In6QyFE0Tkpevh0k9a+FkjhD+cwsYm9WRSbn4uMdW9O0yXlNYC3ppxL3gWWPOcvEl57w==",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/daisyui": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.4.tgz",
"integrity": "sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.3.1.tgz",
"integrity": "sha512-dCi91VD+57lkoBd10CjdW4wPOeOPYvvzQbxti6xmyQbDMbCeCXwNq2KdoU798I4OsCcD5B+n7yVG7HAgYW+cvw==",
"dependencies": {
"colord": "^2.9",
"css-selector-tokenizer": "^0.8",
"postcss": "^8",
"postcss-js": "^4",
"tailwindcss": "^3.1"
"culori": "^3",
"picocolors": "^1",
"postcss-js": "^4"
},
"engines": {
"node": ">=16.9.0"
@@ -1926,7 +1931,8 @@
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true
},
"node_modules/dir-glob": {
"version": "3.0.1",
@@ -1943,7 +1949,8 @@
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true
},
"node_modules/doctrine": {
"version": "3.0.0",
@@ -2540,6 +2547,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -2555,6 +2563,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -2583,6 +2592,7 @@
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@@ -2603,6 +2613,7 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -2670,12 +2681,14 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -2689,6 +2702,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2786,6 +2800,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -2981,6 +2996,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -3053,6 +3069,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -3061,7 +3078,8 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/internal-slot": {
"version": "1.0.6",
@@ -3107,6 +3125,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -3161,6 +3180,7 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
@@ -3202,6 +3222,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3210,6 +3231,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -3257,6 +3279,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -3434,6 +3457,7 @@
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz",
"integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==",
"dev": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -3506,6 +3530,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
"dev": true,
"engines": {
"node": ">=10"
}
@@ -3513,7 +3538,8 @@
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/linkify-it": {
"version": "4.0.1",
@@ -3622,6 +3648,7 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"engines": {
"node": ">= 8"
}
@@ -3630,6 +3657,7 @@
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
@@ -3663,6 +3691,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -3719,6 +3748,7 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@@ -3758,6 +3788,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3814,6 +3845,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3822,6 +3854,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -3912,6 +3945,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -4021,6 +4055,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4037,7 +4072,8 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/path-type": {
"version": "4.0.0",
@@ -4083,6 +4119,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
@@ -4094,6 +4131,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4102,6 +4140,7 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -4137,6 +4176,7 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -4200,6 +4240,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
"dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.11"
},
@@ -4260,6 +4301,7 @@
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
"dev": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -4271,7 +4313,8 @@
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/prelude-ls": {
"version": "1.2.1",
@@ -4283,9 +4326,9 @@
}
},
"node_modules/prettier": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -4309,12 +4352,12 @@
}
},
"node_modules/prettier-plugin-svelte": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.0.3.tgz",
"integrity": "sha512-dLhieh4obJEK1hnZ6koxF+tMUrZbV5YGvRpf2+OADyanjya5j0z1Llo8iGwiHmFWZVG/hLEw/AJD5chXd9r3XA==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.0.tgz",
"integrity": "sha512-96+AZxs2ESqIFA9j+o+DHqY+BsUglezfl553LQd6VOtTyJq5GPuBEb3ElxF2cerFzKlYKttlH/VcVmRNj5oc3A==",
"peerDependencies": {
"prettier": "^3.0.0",
"svelte": "^3.2.0 || ^4.0.0-next.0"
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
}
},
"node_modules/prettier-plugin-tailwindcss": {
@@ -4401,6 +4444,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@@ -4420,6 +4464,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dev": true,
"dependencies": {
"pify": "^2.3.0"
}
@@ -4428,6 +4473,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -4456,6 +4502,7 @@
"version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
"integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
"dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -4481,6 +4528,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -4645,6 +4693,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@@ -4999,6 +5048,7 @@
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
"integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
@@ -5020,6 +5070,7 @@
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -5051,6 +5102,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -5059,9 +5111,9 @@
}
},
"node_modules/svelte": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz",
"integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==",
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.4.tgz",
"integrity": "sha512-hpl7nZhSY+pCL/gsXTyixmGR9H0qDrbn9TT8wfLQyX9jRZeDgIvMca7vrE/tgPXZmQpjIcJ6AsoSdAlyRCDuiQ==",
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
@@ -5082,9 +5134,9 @@
}
},
"node_modules/svelte-check": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.5.2.tgz",
"integrity": "sha512-5a/YWbiH4c+AqAUP+0VneiV5bP8YOk9JL3jwvN+k2PEPLgpu85bjQc5eE67+eIZBBwUEJzmO3I92OqKcqbp3fw==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.0.tgz",
"integrity": "sha512-8VfqhfuRJ1sKW+o8isH2kPi0RhjXH1nNsIbCFGyoUHG+ZxVxHYRKcb+S8eaL/1tyj3VGvWYx3Y5+oCUsJgnzcw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
@@ -5093,14 +5145,14 @@
"import-fresh": "^3.2.1",
"picocolors": "^1.0.0",
"sade": "^1.7.4",
"svelte-preprocess": "^5.0.4",
"svelte-preprocess": "^5.1.0",
"typescript": "^5.0.3"
},
"bin": {
"svelte-check": "bin/svelte-check"
},
"peerDependencies": {
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0"
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
}
},
"node_modules/svelte-eslint-parser": {
@@ -5143,9 +5195,9 @@
}
},
"node_modules/svelte-preprocess": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz",
"integrity": "sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.0.tgz",
"integrity": "sha512-EkErPiDzHAc0k2MF5m6vBNmRUh338h2myhinUw/xaqsLs7/ZvsgREiLGj03VrSzbY/TB5ZXgBOsKraFee5yceA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -5168,7 +5220,7 @@
"sass": "^1.26.8",
"stylus": "^0.55.0",
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0",
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
@@ -5251,6 +5303,7 @@
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
"integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
"dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -5287,6 +5340,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
"dev": true,
"dependencies": {
"lilconfig": "^2.0.5",
"yaml": "^2.1.1"
@@ -5315,6 +5369,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
"dev": true,
"engines": {
"node": ">= 14"
}
@@ -5329,6 +5384,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0"
}
@@ -5337,6 +5393,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
@@ -5375,6 +5432,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@@ -5406,7 +5464,8 @@
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true
},
"node_modules/tsconfig-paths": {
"version": "3.14.2",
@@ -5611,7 +5670,8 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"node_modules/vite": {
"version": "4.5.0",
@@ -5759,7 +5819,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/xml-name-validator": {
"version": "4.0.0",

View File

@@ -15,10 +15,10 @@
"@sveltejs/adapter-auto": "^2.1.1",
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.4",
"@sveltejs/kit": "^1.27.6",
"@types/markdown-it": "^13.0.6",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"autoprefixer": "^10.4.16",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
@@ -27,9 +27,9 @@
"eslint-plugin-svelte": "^2.35.0",
"eslint-plugin-vue": "^9.18.1",
"postcss": "^8.4.31",
"prettier": "3.0.3",
"svelte": "^4.2.2",
"svelte-check": "^3.5.2",
"prettier": "3.1.0",
"svelte": "^4.2.4",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.3.5",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
@@ -40,11 +40,11 @@
"@iconify/svelte": "^3.1.4",
"@sveltestack/svelte-query": "^1.6.0",
"clipboard": "^2.0.11",
"daisyui": "^3.9.4",
"daisyui": "^4.3.1",
"highlight.js": "^11.9.0",
"markdown-it": "^13.0.2",
"markdown-it-highlightjs": "^4.0.1",
"prettier-plugin-svelte": "^3.0.3",
"prettier-plugin-svelte": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7"
}
}

View File

@@ -85,3 +85,56 @@ markdown. .hljs {
.ie-edge-no-scrollbar {
-ms-overflow-style: none;
}
/* Models Grid Layout */
.models-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
padding: 30px;
padding-top: 10px;
}
/* Model Accordion Styles */
.model-accordion {
border-radius: 8px;
box-shadow: 0 5px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
background-color: "bg-base-200";
}
.model-accordion button {
width: 100%;
padding: 15px;
text-align: left;
border: none;
outline: none;
transition: background-color 0.3s ease;
cursor: pointer;
}
.model-details {
padding: 10px;
border-top: 1px solid #ddd;
}
.model-details p {
margin: 10px 0;
}
.top-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 20px;
padding-top: 10px;
}
.search-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 0 2rem;
}

View File

@@ -1,57 +0,0 @@
<script lang="ts">
import { invalidate, invalidateAll } from "$app/navigation";
let dialogTag: HTMLDialogElement;
let isLoading = false;
let link =
"https://raw.githubusercontent.com/serge-chat/serge/main/api/src/serge/data/models.json";
const handleRefresh = async (e: Event) => {
isLoading = true;
const r = await fetch("/api/model/refresh", {
method: "POST",
body: new FormData(e.target as HTMLFormElement),
});
if (r.ok) {
await invalidate("/api/model/all");
dialogTag.close();
} else {
console.error("Error refreshing models");
}
isLoading = false;
};
</script>
<button class="btn-outline btn" on:click={() => dialogTag.showModal()}
>Refresh Models</button
>
<dialog bind:this={dialogTag} class="modal">
<form method="dialog" class="modal-box">
<button class="btn-ghost btn-sm btn-circle btn absolute right-2 top-2"
></button
>
<form on:submit|preventDefault={handleRefresh}>
<h3 class="text-lg font-bold">Model refresh</h3>
<p class="py-4">
Enter the URL of the JSON file containing the models below
</p>
<input
type="text"
name="url"
class="input-bordered input-primary input mb-4 w-full"
bind:value={link}
/>
<div class="modal-action">
<!-- if there is a button in form, it will close the modal -->
<button type="submit" class="btn" disabled={isLoading}>
{#if isLoading}
<span class="loading loading-spinner" />
{/if}
Refresh
</button>
</div>
</form>
</form>
</dialog>

View File

@@ -2,8 +2,6 @@ import { writable, type Writable } from "svelte/store";
const themeStore = writable("dark");
const barVisible = writable(true);
const newChat: Writable<object | null> = writable(null);
export { barVisible, newChat, themeStore };
export { newChat, themeStore };

View File

@@ -1,30 +1,42 @@
<script lang="ts">
import "../app.css";
import type { LayoutData } from "./$types";
import type { PageData } from "./$types";
import { invalidate, goto } from "$app/navigation";
import { onMount, onDestroy } from "svelte";
import { page } from "$app/stores";
import { barVisible, newChat, themeStore } from "$lib/stores.js";
import { newChat, themeStore } from "$lib/stores.js";
import { fly } from "svelte/transition";
export let data: LayoutData;
export let data: PageData;
let models;
let modelAvailable: boolean;
const isLoading = false;
let deleteConfirm = false;
let deleteAllConfirm = false;
let theme: string;
let bar_visible: boolean;
let dataCht: Response | any = null;
const unsubscribe = barVisible.subscribe((value) => (bar_visible = value));
const unsubscribe1 = newChat.subscribe((value) => (dataCht = value));
const unsubscribe = newChat.subscribe((value) => (dataCht = value));
onMount(() => {
bar_visible = window.innerWidth > 768;
barVisible.set(bar_visible);
theme = localStorage.getItem("data-theme") || "dark";
document.documentElement.setAttribute("data-theme", theme);
});
$: if (data && data.models) {
models = data.models.filter((el) => el.available);
modelAvailable = models.length > 0;
} else {
models = [];
modelAvailable = false;
}
$: id = $page.params.id || "";
async function goToHome() {
await goto("/");
}
async function deleteChat(chatID: string) {
const response = await fetch("/api/chat/" + chatID, { method: "DELETE" });
if (response.status === 200) {
@@ -93,89 +105,50 @@
localStorage.setItem("data-theme", $themeStore);
}
function toggleBar() {
bar_visible = !bar_visible;
barVisible.set(bar_visible);
}
async function createSameSession() {
if (dataCht) {
const newData = await fetch(
`/api/chat/?model=${dataCht.params.model_path}&temperature=${dataCht.params.temperature}&top_k=${dataCht.params.top_k}` +
`&top_p=${dataCht.params.top_p}&max_length=${dataCht.params.max_tokens}&context_window=${dataCht.params.n_ctx}` +
`&repeat_last_n=${dataCht.params.last_n_tokens_size}&repeat_penalty=${dataCht.params.repeat_penalty}` +
`&n_threads=${dataCht.params.n_threads}&init_prompt=${dataCht.history[0].data.content}` +
`&gpu_layers=${dataCht.params.n_gpu_layers}`,
{
method: "POST",
headers: {
accept: "application/json",
},
},
).then((response) => response.json());
await invalidate("/api/chat/");
await goto("/chat/" + newData);
}
}
onDestroy(() => {
unsubscribe;
unsubscribe1;
});
// onDestroy(unsubscribe1);
</script>
<aside
id="default-sidebar"
class={"border-base-content/[.2] fixed left-0 top-0 z-40 h-screen w-80 -translate-x-full border-r transition-transform overflow-hidden" +
(bar_visible ? " translate-x-0" : "")}
aria-label="Sidebar"
class="border-base-content/[.2] fixed left-0 top-0 z-40 h-screen w-80 -translate-x-full border-r transition-transform overflow-hidden translate-x-0 aria-label=Sidebar"
>
<div
class="bg-base-200 relative h-screen py-1 px-2 overflow-hidden flex flex-col items-center justify-between"
>
<div
class="w-full flex items-center justify-between border-b border-base-content/[.2] pb-1"
class="w-full flex items-center border-b border-base-content/[.2] pb-1"
>
<button class="btn btn-ghost flex-shrink-0" on:click={goToHome}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-5 h-5"
>
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
</svg>
</button>
<button
disabled={isLoading}
class="btn btn-ghost h-6 w-4/5 justify-between font-semibold text-left text-sm capitalize"
disabled={isLoading || !modelAvailable}
class="btn btn-ghost flex-grow h-6 font-semibold text-left text-sm capitalize"
class:loading={isLoading}
on:click|preventDefault={() => createSameSession()}
on:click|preventDefault={() => goto("/")}
style="justify-content: flex-start;"
>
<span>New chat</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
class="w-4 h-4 mr-2"
>
<path
d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
>
</path>
</svg>
</button>
<button
class="btn btn-ghost flex h-6 w-1/6 items-center justify-center font-semibold z-40"
on:click={toggleBar}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-6 h-6"
>
<path
d="M11.28 9.53 8.81 12l2.47 2.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-3-3a.75.75 0 0 1 0-1.06l3-3a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"
>
</path>
<path
d="M3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25V3.75C2 2.784 2.784 2 3.75 2ZM3.5 3.75v16.5c0 .138.112.25.25.25H15v-17H3.75a.25.25 0 0 0-.25.25Zm13 16.75h3.75a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25H16.5Z"
>
</path>
</svg>
<span>New Chat</span>
</button>
</div>
<ul
@@ -407,8 +380,6 @@
</div>
</aside>
<div
class={"relative h-full transition-all" + (bar_visible ? " md:ml-80" : "")}
>
<div class={"relative h-full transition-all md:ml-80"}>
<slot />
</div>

View File

@@ -1,16 +1,27 @@
import type { LayoutLoad } from "./$types";
interface t {
interface ChatMetadata {
id: string;
created: string;
model: string;
subtitle: string;
}
export interface ModelStatus {
name: string;
size: number;
available: boolean;
progress?: number;
}
export const load: LayoutLoad = async ({ fetch }) => {
const r = await fetch("/api/chat/");
const chats = (await r.json()) as t[];
const api_chat = await fetch("/api/chat/");
const chats = (await api_chat.json()) as ChatMetadata[];
const model_api = await fetch("/api/model/all");
const models = (await model_api.json()) as ModelStatus[];
return {
chats,
models,
};
};

View File

@@ -1,16 +1,12 @@
<script lang="ts">
import type { PageData } from "./$types";
import { goto, invalidate } from "$app/navigation";
import { barVisible } from "$lib/stores";
import { onDestroy } from "svelte";
export let data: PageData;
const models = data.models.filter((el) => el.available);
const modelAvailable = models.length > 0;
const modelsLabels = models.map((el) => el.name);
let bar_visible: boolean;
const unsubscribe = barVisible.subscribe((value) => (bar_visible = value));
let temp = 0.1;
let top_k = 50;
@@ -48,40 +44,13 @@
await invalidate("/api/chat/");
}
}
function toggleBar() {
bar_visible = !bar_visible;
barVisible.set(bar_visible);
}
onDestroy(unsubscribe);
</script>
{#if !bar_visible}
<button
class="absolute p-0 top-1 left-2 md:left-16 h-10 w-10 min-h-0 btn btn-ghost flex items-center justify-center font-semibold z-40"
on:click={toggleBar}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M11.28 9.53 8.81 12l2.47 2.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-3-3a.75.75 0 0 1 0-1.06l3-3a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"
>
</path>
<path
d="M3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25V3.75C2 2.784 2.784 2 3.75 2ZM3.5 3.75v16.5c0 .138.112.25.25.25H15v-17H3.75a.25.25 0 0 0-.25.25Zm13 16.75h3.75a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25H16.5Z"
>
</path>
</svg>
</button>
{/if}
<div class="flex flex-col items-center justify-center pt-5">
<h1 class="pb-2 text-3xl font-bold">Say Hi to Serge</h1>
</div>
<h1 class="pb-5 pt-2 text-center text-xl font-light">
An easy way to chat with Alpaca & other LLaMA based models.
An easy way to chat with LLaMA based models.
</h1>
<form on:submit|preventDefault={onCreateChat} id="form-create-chat" class="p-5">
@@ -259,7 +228,7 @@
</div>
<div class="col-span-3 flex flex-col">
<label for="init_prompt" class="label-text pb-1"
>Pre-Prompt for initializing a conversation.</label
>Prompt Template</label
>
<textarea
class="textarea-bordered textarea h-24 w-full"

View File

@@ -1,6 +1,6 @@
import type { PageLoad } from "./$types";
interface ModelStatus {
export interface ModelStatus {
name: string;
size: number;
available: boolean;
@@ -8,8 +8,8 @@ interface ModelStatus {
}
export const load: PageLoad = async ({ fetch }) => {
const r = await fetch("/api/model/all");
const models = (await r.json()) as ModelStatus[];
const api_model = await fetch("/api/model/all");
const models = (await api_model.json()) as ModelStatus[];
return {
models,
};

View File

@@ -2,7 +2,7 @@
import type { PageData } from "./$types";
import { invalidate, goto } from "$app/navigation";
import { page } from "$app/stores";
import { barVisible, newChat, themeStore } from "$lib/stores";
import { newChat, themeStore } from "$lib/stores";
import { onMount, onDestroy } from "svelte";
import ClipboardJS from "clipboard";
import hljs from "highlight.js";
@@ -11,7 +11,6 @@
import css from "highlight.js/lib/languages/css";
import cpp from "highlight.js/lib/languages/cpp";
import dockerfile from "highlight.js/lib/languages/dockerfile";
import graphql from "highlight.js/lib/languages/graphql";
import go from "highlight.js/lib/languages/go";
import javascript from "highlight.js/lib/languages/javascript";
import json from "highlight.js/lib/languages/json";
@@ -32,7 +31,6 @@
hljs.registerLanguage("bash", bash);
hljs.registerLanguage("css", css);
hljs.registerLanguage("cpp", cpp);
hljs.registerLanguage("graphql", graphql);
hljs.registerLanguage("dockerfile", dockerfile);
hljs.registerLanguage("go", go);
hljs.registerLanguage("javascript", javascript);
@@ -61,8 +59,6 @@
messageContainer.scrollBottom = messageContainer.scrollHeight;
}
let prompt = "";
let bar_visible: boolean;
const unsubscribe = barVisible.subscribe((value) => (bar_visible = value));
async function askQuestion() {
const data = new URLSearchParams();
@@ -243,10 +239,6 @@
const onMouseLeave = () => {
sendBottomHovered = false;
};
const toggleBar = () => {
bar_visible = !bar_visible;
barVisible.set(bar_visible);
};
const scrollToBottom = (node: Element, history: any[]) => {
const scroll = () =>
node.scroll({
@@ -258,7 +250,6 @@
return { update: scroll };
};
onDestroy(() => {
unsubscribe;
styleElement && styleElement.remove();
});
</script>
@@ -271,27 +262,6 @@
<div class="w-full border-b border-base-content/[.2]">
<div class="h-8 px-2 md:container md:mx-auto md:px-0">
<div class="w-full h-full relative flex items-center justify-center">
{#if !bar_visible}
<button
class="absolute p-0 top-0 bottom-0 left-0 w-10 h-8 min-h-0 btn btn-ghost flex items-center justify-center font-semibold z-40"
on:click={toggleBar}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="w-4 h-4 fill-base-content"
>
<path
d="M7.22 14.47 9.69 12 7.22 9.53a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l3 3a.75.75 0 0 1 0 1.06l-3 3a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Z"
>
</path>
<path
d="M3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25V3.75C2 2.784 2.784 2 3.75 2ZM3.5 3.75v16.5c0 .138.112.25.25.25H15v-17H3.75a.25.25 0 0 0-.25.25Zm13 16.75h3.75a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25H16.5Z"
>
</path>
</svg>
</button>
{/if}
<div class="flex flex-row items-center justify-center color-base-300">
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@@ -1,166 +1,302 @@
<script lang="ts">
import { invalidate } from "$app/navigation";
import type { ModelStatus } from "../+page";
import type { PageData } from "./$types";
import RefreshModal from "../../lib/components/models/RefreshModal.svelte";
import { barVisible } from "$lib/stores";
import { onDestroy } from "svelte";
import Icon from "@iconify/svelte";
import { onMount } from "svelte";
export let data: PageData;
let searchQuery = "";
let selectedVariant: Record<string, string> = {};
let downloading = false;
let bar_visible: boolean;
const unsubscribe = barVisible.subscribe((value) => (bar_visible = value));
console.log(data);
setInterval(async () => {
if (downloading) {
// Add a reactive statement to keep track of downloading models
$: downloadingModels = new Set(
data.models
.filter(
(model) =>
(model.progress > 0 && model.progress < 100) || !model.available,
)
.map((model) => model.name),
);
function onComponentMount() {
const downloadingModelsArray = JSON.parse(
localStorage.getItem("downloadingModels") || "[]",
);
downloadingModelsArray.forEach((model: string) => {
downloadingModels.add(model);
checkDownloadProgress(model);
});
}
onMount(() => {
onComponentMount();
});
/**
* Handles the fetching the status of an active download
* @param modelName - The model name.
*/
async function fetchDownloadProgress(modelName: string) {
const response = await fetch(`/api/model/${modelName}/download/status`);
if (response.ok) {
const progress = await response.text();
const progressNumber = parseFloat(progress);
const modelIndex = data.models.findIndex((m) => m.name === modelName);
if (modelIndex !== -1) {
data.models[modelIndex].progress = progressNumber;
data.models = [...data.models]; // enable reactivity
}
return progressNumber;
}
return 0;
}
function startDownload(modelName: string) {
const currentDownloads = JSON.parse(
localStorage.getItem("downloadingModels") || "[]",
);
if (!currentDownloads.includes(modelName)) {
currentDownloads.push(modelName);
localStorage.setItem(
"downloadingModels",
JSON.stringify(currentDownloads),
);
}
downloadingModels.add(modelName);
checkDownloadProgress(modelName);
}
/**
* Debounce function to limit how often a function can be called.
* @param func - The function to be debounced.
* @param wait - The time to wait in milliseconds.
* @returns A debounced version of the given function.
*/
function debounce(func: (...args: any[]) => void, wait: number) {
let timeout: ReturnType<typeof setTimeout>;
return function (...args: any[]) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Update search query with debounce to improve performance
const updateSearch = debounce((query: string) => {
searchQuery = query;
}, 300);
/**
* Wrapper function for fetch to include invalidate call on successful response.
* @param url - The URL to fetch.
* @param options - Fetch request options.
* @returns The fetch response.
*/
async function fetchWithInvalidate(url: string, options: any) {
const response = await fetch(url, options);
if (response.ok) {
await invalidate("/api/model/all");
}
}, 2500);
return response;
}
async function onClick(model: string) {
if (downloading) {
return;
}
/**
* Truncates a string to the specified length and appends an ellipsis.
* @param str - The string to truncate.
* @param maxLength - The maximum length of the truncated string.
* @returns The truncated string with an ellipsis if needed.
*/
function truncateString(str: string, maxLength: number): string {
return str.length > maxLength
? str.substring(0, maxLength - 1) + "..."
: str;
}
downloading = true;
const r = await fetch(`/api/model/${model}/download`, {
method: "POST",
/**
* Handles the action (download/delete) on a model.
* @param model - The model name.
* @param isAvailable - Boolean indicating if the model is available.
*/
async function handleModelAction(model: string, isAvailable: boolean) {
const url = `/api/model/${model}${isAvailable ? "" : "/download"}`;
const method = isAvailable ? "DELETE" : "POST";
console.log("Before fetch invalidate");
fetchWithInvalidate(url, { method }).then((response) => {
console.log(`After fetch for ${url}`);
});
if (r.ok) {
await invalidate("/api/model/all");
}
downloading = false;
}
async function deleteModel(model: string) {
const r = await fetch(`/api/model/${model}`, {
method: "DELETE",
});
if (r.ok) {
await invalidate("/api/model/all");
if (method === "POST") {
// Start tracking download progress for the model
console.log(`Calling startDownload() for ${model}`);
startDownload(model);
}
}
function toggleBar() {
bar_visible = !bar_visible;
barVisible.set(bar_visible);
// Function to periodically check download progress for a model
async function checkDownloadProgress(modelName: string) {
let progress = await fetchDownloadProgress(modelName);
console.log(`Download status for ${modelName} ${progress}/100.0%`);
// Continue checking until progress reaches 100
if (progress < 100) {
setTimeout(() => checkDownloadProgress(modelName), 1500);
} else {
// Stop tracking the model once download is complete
console.log(`Stopping tracker for ${modelName}`);
const currentDownloads = JSON.parse(
localStorage.getItem("downloadingModels") || "[]",
);
const updatedDownloads = currentDownloads.filter(
(model: string) => model !== modelName,
);
localStorage.setItem(
"downloadingModels",
JSON.stringify(updatedDownloads),
);
downloadingModels.delete(modelName);
}
}
onDestroy(unsubscribe);
/**
* Groups models by their prefix.
* @param models - Array of ModelStatus objects.
* @returns An object grouping models by their prefix.
*/
function groupModelsByPrefix(
models: ModelStatus[],
): Record<string, ModelStatus[]> {
return models.reduce(
(acc, model) => {
const prefix = model.name.split("-")[0];
acc[prefix] = acc[prefix] || [];
acc[prefix].push(model);
return acc;
},
{} as Record<string, ModelStatus[]>,
);
}
/**
* Handles change in variant selection for a model.
* @param modelPrefix - The prefix of the model.
* @param event - The change event.
*/
function handleVariantChange(modelPrefix: string, event: Event) {
const target = event.target as HTMLSelectElement;
selectedVariant[modelPrefix] = target.value;
}
/**
* Retrieves model details based on the selected variant or default.
* @param models - Array of ModelStatus objects.
* @param prefix - The prefix of the model group.
* @returns The selected or default ModelStatus object.
*/
function getModelDetails(models: ModelStatus[], prefix: string): ModelStatus {
return models.find((m) => m.name === selectedVariant[prefix]) || models[0];
}
// Reactive statements to filter and group models based on search query
$: filteredModels = data.models
.filter(
(model) =>
!downloadedOrDownloadingModels.includes(model) &&
model.name.toLowerCase().includes(searchQuery.toLowerCase()),
)
.sort((a, b) => a.name.localeCompare(b.name));
// Reactive statement with models grouped by prefix
$: groupedModels = groupModelsByPrefix(filteredModels);
// Reactive statement to filter models that are downloaded or downloading
$: downloadedOrDownloadingModels = data.models
.filter((model) => model.progress > 0 || model.available)
.sort((a, b) => a.name.localeCompare(b.name));
</script>
{#if !bar_visible}
<button
class="absolute p-0 top-1 left-2 md:left-16 h-10 w-10 min-h-0 btn btn-ghost flex items-center justify-center font-semibold z-40"
on:click={toggleBar}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M11.28 9.53 8.81 12l2.47 2.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-3-3a.75.75 0 0 1 0-1.06l3-3a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"
>
</path>
<path
d="M3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25V3.75C2 2.784 2.784 2 3.75 2ZM3.5 3.75v16.5c0 .138.112.25.25.25H15v-17H3.75a.25.25 0 0 0-.25.25Zm13 16.75h3.75a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25H16.5Z"
>
</path>
</svg>
</button>
{/if}
<div class="flex flex-row items-center justify-center pt-5">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width="24"
height="24"
>
<path
class="fill-warning"
d="M9.504.43a1.516 1.516 0 0 1 2.437 1.713L10.415 5.5h2.123c1.57 0 2.346 1.909 1.22 3.004l-7.34 7.142a1.249 1.249 0 0 1-.871.354h-.302a1.25 1.25 0 0 1-1.157-1.723L5.633 10.5H3.462c-1.57 0-2.346-1.909-1.22-3.004L9.503.429Zm1.047 1.074L3.286 8.571A.25.25 0 0 0 3.462 9H6.75a.75.75 0 0 1 .694 1.034l-1.713 4.188 6.982-6.793A.25.25 0 0 0 12.538 7H9.25a.75.75 0 0 1-.683-1.06l2.008-4.418.003-.006a.036.036 0 0 0-.004-.009l-.006-.006-.008-.001c-.003 0-.006.002-.009.004Z"
<div class="top-section">
<div class="search-row">
<input
type="text"
bind:value={searchQuery}
class="input input-bordered flex-grow"
placeholder="Search models..."
on:input={(e) => {
const target = e.target;
if (target instanceof HTMLInputElement) {
updateSearch(target.value);
}
}}
/>
</svg>
<h1 class="px-2 text-center text-3xl font-bold">Download a model</h1>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width="24"
height="24"
>
<path
class="fill-warning"
d="M9.504.43a1.516 1.516 0 0 1 2.437 1.713L10.415 5.5h2.123c1.57 0 2.346 1.909 1.22 3.004l-7.34 7.142a1.249 1.249 0 0 1-.871.354h-.302a1.25 1.25 0 0 1-1.157-1.723L5.633 10.5H3.462c-1.57 0-2.346-1.909-1.22-3.004L9.503.429Zm1.047 1.074L3.286 8.571A.25.25 0 0 0 3.462 9H6.75a.75.75 0 0 1 .694 1.034l-1.713 4.188 6.982-6.793A.25.25 0 0 0 12.538 7H9.25a.75.75 0 0 1-.683-1.06l2.008-4.418.003-.006a.036.036 0 0 0-.004-.009l-.006-.006-.008-.001c-.003 0-.006.002-.009.004Z"
/>
</svg>
</div>
<h1 class="pb-5 pt-2 text-center text-xl font-light">
Make sure you have enough disk space and available RAM to run them.<br />
7B requires about 4.5GB of free RAM, 13B requires about 12GB free, 30B requires
about 20GB free
</h1>
<div class="mx-auto w-fit">
<RefreshModal />
</div>
<div class="mt-30 mx-auto flex flex-col">
<div class="mx-auto w-full max-w-4xl">
<div class="divider" />
{#each data.models as model}
<div class="my-5 flex flex-col content-around">
<div
class="mx-auto flex flex-row items-center justify-center text-3xl font-semibold"
>
<span class="mr-2">{model.name}</span>
{#if model.available}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width="24"
height="24"
>
<path
class="fill-info"
d="m9.585.52.929.68c.153.112.331.186.518.215l1.138.175a2.678 2.678 0 0 1 2.24 2.24l.174 1.139c.029.187.103.365.215.518l.68.928a2.677 2.677 0 0 1 0 3.17l-.68.928a1.174 1.174 0 0 0-.215.518l-.175 1.138a2.678 2.678 0 0 1-2.241 2.241l-1.138.175a1.17 1.17 0 0 0-.518.215l-.928.68a2.677 2.677 0 0 1-3.17 0l-.928-.68a1.174 1.174 0 0 0-.518-.215L3.83 14.41a2.678 2.678 0 0 1-2.24-2.24l-.175-1.138a1.17 1.17 0 0 0-.215-.518l-.68-.928a2.677 2.677 0 0 1 0-3.17l.68-.928c.112-.153.186-.331.215-.518l.175-1.14a2.678 2.678 0 0 1 2.24-2.24l1.139-.175c.187-.029.365-.103.518-.215l.928-.68a2.677 2.677 0 0 1 3.17 0ZM7.303 1.728l-.927.68a2.67 2.67 0 0 1-1.18.489l-1.137.174a1.179 1.179 0 0 0-.987.987l-.174 1.136a2.677 2.677 0 0 1-.489 1.18l-.68.928a1.18 1.18 0 0 0 0 1.394l.68.927c.256.348.424.753.489 1.18l.174 1.137c.078.509.478.909.987.987l1.136.174a2.67 2.67 0 0 1 1.18.489l.928.68c.414.305.979.305 1.394 0l.927-.68a2.67 2.67 0 0 1 1.18-.489l1.137-.174a1.18 1.18 0 0 0 .987-.987l.174-1.136a2.67 2.67 0 0 1 .489-1.18l.68-.928a1.176 1.176 0 0 0 0-1.394l-.68-.927a2.686 2.686 0 0 1-.489-1.18l-.174-1.137a1.179 1.179 0 0 0-.987-.987l-1.136-.174a2.677 2.677 0 0 1-1.18-.489l-.928-.68a1.176 1.176 0 0 0-1.394 0ZM11.28 6.78l-3.75 3.75a.75.75 0 0 1-1.06 0L4.72 8.78a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L7 8.94l3.22-3.22a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
/>
</svg>
{/if}
</div>
<p class="mx-auto pb-2 text-xl font-light">
({model.size / 1e9}GB)
</p>
{#if model.progress}
<div class="mx-auto my-5 w-56 justify-center">
<p class="w-full text-center font-light">{model.progress}%</p>
<progress
class="progress-primary progress mx-auto h-5 w-56"
value={model.progress}
max="100"
/>
</div>
{/if}
{#if model.available}
<button
on:click={() => deleteModel(model.name)}
class="btn-warning btn-outline btn mx-auto">Delete</button
>
{:else}
<button
on:click={() => onClick(model.name)}
class="btn-primary btn mx-auto"
class:model.available={() => "btn-outline"}
disabled={model.available ||
!!(model.progress && model.progress > 0)}
>
Download
</button>
{/if}
</div>
<div class="divider" />
{/each}
</div>
</div>
<div class="models-grid grid">
{#each downloadedOrDownloadingModels as model}
<div class="model card card-bordered">
<div class="card-body">
<h2 class="card-title">{truncateString(model.name, 24)}</h2>
<div class="model-details">
{#if model.progress < 100}
<div class="progress-bar">
<progress value={model.progress} max="100"></progress> / {model.progress}%
</div>
{/if}
{#if model.progress >= 100}
<p>Size: {model.size / 1e9}GB</p>
<button
on:click={() => handleModelAction(model.name, model.available)}
class="btn btn-error mt-2"
>
<Icon icon="mdi:trash" width="32" height="32" />
</button>
{/if}
</div>
</div>
</div>
{/each}
</div>
<div class="models-grid grid">
{#each Object.entries(groupedModels) as [prefix, models]}
<div class="model-group card card-bordered">
<div class="card-body">
<h2 class="card-title">{truncateString(prefix, 24)}</h2>
<div class="model-details">
{#if models.length > 1}
<select
bind:value={selectedVariant[prefix]}
on:change={(event) => handleVariantChange(prefix, event)}
>
{#each models as model}
<option value={model.name}
>{truncateString(model.name, 32)}</option
>
{/each}
</select>
{/if}
{#if models.length === 1 || selectedVariant[prefix]}
{@const model = getModelDetails(models, prefix)}
{#if models.length === 1}
<h3>{truncateString(model.name, 24)}</h3>
{/if}
<p>Size: {model.size / 1e9}GB</p>
<button
on:click={() => handleModelAction(model.name, model.available)}
class="btn btn-primary mt-2"
>
<Icon icon="ic:baseline-download" width="32" height="32" />
</button>
{/if}
</div>
</div>
</div>
{/each}
</div>

View File

@@ -4,7 +4,7 @@ interface ModelStatus {
name: string;
size: number;
available: boolean;
progress?: number;
progress: number;
}
export const load: PageLoad = async ({ fetch }) => {