Compare commits
No commits in common. "master" and "main" have entirely different histories.
32 changed files with 59 additions and 3265 deletions
15
.gitignore
vendored
15
.gitignore
vendored
|
|
@ -1,15 +0,0 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
.python-version
|
||||
.env
|
||||
|
||||
# Databases
|
||||
*.sqlite3
|
||||
42
Dockerfile
42
Dockerfile
|
|
@ -1,42 +0,0 @@
|
|||
# Multi-stage build for a Python backend with Deno frontend
|
||||
|
||||
# Stage 1: Build the frontend
|
||||
FROM denoland/deno:2.4.3 AS frontend-builder
|
||||
|
||||
# Set working directory for frontend
|
||||
WORKDIR /app/frontend
|
||||
|
||||
# Copy frontend files
|
||||
COPY frontend/ .
|
||||
|
||||
# Install dependencies and build the frontend
|
||||
RUN deno install --allow-scripts
|
||||
RUN deno run build
|
||||
|
||||
# Stage 2: Setup Python backend with uv
|
||||
FROM python:3.11-slim AS backend
|
||||
|
||||
# Install uv
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy Python project files
|
||||
COPY pyproject.toml uv.lock ./
|
||||
|
||||
# Install Python dependencies
|
||||
RUN uv sync --frozen
|
||||
|
||||
# Copy backend source code and .env file
|
||||
COPY *.py ./
|
||||
COPY .env ./
|
||||
|
||||
# Copy built frontend from previous stage
|
||||
COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
|
||||
|
||||
# Expose the port (adjust if your app uses a different port)
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the application
|
||||
CMD ["uv", "run", "app.py"]
|
||||
131
TESTING.md
131
TESTING.md
|
|
@ -1,131 +0,0 @@
|
|||
# Backend API Testing with Hurl
|
||||
|
||||
This document provides instructions for testing the chat backend API using Hurl.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Install Hurl**:
|
||||
- macOS: `brew install hurl`
|
||||
- Ubuntu/Debian: `sudo apt update && sudo apt install hurl`
|
||||
- Windows: Download from [hurl.dev](https://hurl.dev)
|
||||
- Or use Docker: `docker pull ghcr.io/orange-opensource/hurl:latest`
|
||||
|
||||
2. **Start the backend server**:
|
||||
```bash
|
||||
# Make sure you're in the project root directory
|
||||
python -m uvicorn app:application --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
## Running the Tests
|
||||
|
||||
### Basic Test Run
|
||||
```bash
|
||||
# Run all tests
|
||||
hurl test-backend.hurl
|
||||
|
||||
# Run with verbose output
|
||||
hurl --verbose test-backend.hurl
|
||||
|
||||
# Run with color output
|
||||
hurl --color test-backend.hurl
|
||||
```
|
||||
|
||||
### Advanced Options
|
||||
```bash
|
||||
# Run with detailed report
|
||||
hurl --report-html report.html test-backend.hurl
|
||||
|
||||
# Run specific tests (first 5 tests)
|
||||
hurl --to-entry 5 test-backend.hurl
|
||||
|
||||
# Run tests with custom variables
|
||||
hurl --variable host=localhost --variable port=8000 test-backend.hurl
|
||||
|
||||
# Run with retry on failure
|
||||
hurl --retry 3 test-backend.hurl
|
||||
```
|
||||
|
||||
## Test Coverage
|
||||
|
||||
The test script covers:
|
||||
|
||||
### ✅ Happy Path Tests
|
||||
- **GET /api/models** - Retrieves available AI models
|
||||
- **POST /api/chats** - Creates new chat sessions
|
||||
- **GET /api/chats/{id}** - Retrieves chat history
|
||||
- **POST /api/chats/{id}/messages** - Sends messages to chat
|
||||
- **GET /api/chats/{id}/stream** - Streams AI responses via SSE
|
||||
|
||||
### ✅ Error Handling Tests
|
||||
- Invalid model names
|
||||
- Non-existent chat IDs
|
||||
- Missing required parameters
|
||||
|
||||
### ✅ Multi-turn Conversation Tests
|
||||
- Multiple message exchanges
|
||||
- Conversation history persistence
|
||||
- Different model selection
|
||||
|
||||
## Test Structure
|
||||
|
||||
The tests are organized in logical flow:
|
||||
|
||||
1. **Model Discovery** - Get available models
|
||||
2. **Chat Creation** - Create new chat sessions
|
||||
3. **Message Exchange** - Send messages and receive responses
|
||||
4. **History Verification** - Ensure messages are persisted
|
||||
5. **Error Scenarios** - Test edge cases and error handling
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can customize the test environment:
|
||||
|
||||
```bash
|
||||
# Set custom host/port
|
||||
export HURL_host=localhost
|
||||
export HURL_port=8000
|
||||
|
||||
# Or pass as arguments
|
||||
hurl --variable host=127.0.0.1 --variable port=8080 test-backend.hurl
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection refused**
|
||||
- Ensure the backend is running on port 8000
|
||||
- Check firewall settings
|
||||
|
||||
2. **Tests fail with 404 errors**
|
||||
- Verify the backend routes are correctly configured
|
||||
- Check if database migrations have been run
|
||||
|
||||
3. **SSE streaming tests timeout**
|
||||
- Increase timeout: `hurl --max-time 30 test-backend.hurl`
|
||||
- Check if AI provider is responding
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Run tests with maximum verbosity:
|
||||
```bash
|
||||
hurl --very-verbose test-backend.hurl
|
||||
```
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Add to your CI/CD pipeline:
|
||||
|
||||
```yaml
|
||||
# GitHub Actions example
|
||||
- name: Run API Tests
|
||||
run: |
|
||||
hurl --test --report-junit results.xml test-backend.hurl
|
||||
```
|
||||
|
||||
## Test Results
|
||||
|
||||
After running tests, you'll see:
|
||||
- ✅ **Green** - Tests passed
|
||||
- ❌ **Red** - Tests failed with details
|
||||
- 📊 **Summary** - Total tests, duration, and success rate
|
||||
46
app.py
46
app.py
|
|
@ -1,46 +0,0 @@
|
|||
from starlette.applications import Starlette
|
||||
from starlette.routing import Route, Mount
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from starlette.responses import FileResponse
|
||||
from controllers import create_chat, post_message, chat_stream, history, get_models
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
|
||||
middleware = [
|
||||
Middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
]
|
||||
|
||||
async def serve_frontend(request):
|
||||
"""Serve the frontend index.html file"""
|
||||
return FileResponse(os.path.join("frontend", "dist", "index.html"))
|
||||
|
||||
async def serve_chat(request):
|
||||
"""Serve the chat.html file for specific chat routes"""
|
||||
return FileResponse(os.path.join("frontend", "dist", "chat.html"))
|
||||
|
||||
routes = [
|
||||
Route("/", serve_frontend, methods=["GET"]),
|
||||
Route("/chats/{chat_id:str}", serve_chat, methods=["GET"]),
|
||||
Route("/api/models", get_models, methods=["GET"]),
|
||||
Route("/api/chats", create_chat, methods=["POST"]),
|
||||
Route("/api/chats/{chat_id:str}", history, methods=["GET"]),
|
||||
Route("/api/chats/{chat_id:str}/messages", post_message, methods=["POST"]),
|
||||
Route("/api/chats/{chat_id:str}/stream", chat_stream, methods=["GET"]),
|
||||
Mount("/assets", StaticFiles(directory=os.path.join("frontend", "dist", "assets")), name="assets"),
|
||||
Mount("/icon", StaticFiles(directory=os.path.join("frontend", "dist", "icon")), name="icon"),
|
||||
]
|
||||
|
||||
|
||||
application = Starlette(debug=True, routes=routes, middleware=middleware)
|
||||
|
||||
# ----------------- Run -----------------
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run("app:application", host="0.0.0.0", port=8000, reload=True)
|
||||
19
chatgraph.py
19
chatgraph.py
|
|
@ -1,19 +0,0 @@
|
|||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import HumanMessage, AIMessage
|
||||
from os import getenv
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import SecretStr
|
||||
|
||||
load_dotenv()
|
||||
|
||||
def get_llm(provider: str):
|
||||
"""Return a LangChain chat model for the requested provider."""
|
||||
return ChatOpenAI(
|
||||
api_key=SecretStr(getenv("OPENROUTER_API_KEY","")),
|
||||
base_url=getenv("OPENROUTER_BASE_URL"),
|
||||
model=provider,
|
||||
)
|
||||
|
||||
def get_messages(chats, chat_id):
|
||||
print(chats)
|
||||
return [HumanMessage(**m) if m["role"] == "human" else AIMessage(**m) for m in chats]
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Masonite-orm module
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from masoniteorm.connections import ConnectionResolver
|
||||
|
||||
DATABASES = {
|
||||
"default": "sqlite",
|
||||
"sqlite": {
|
||||
"driver": "sqlite",
|
||||
"database": "database.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
DB = ConnectionResolver().set_connection_details(DATABASES)
|
||||
115
controllers.py
115
controllers.py
|
|
@ -1,115 +0,0 @@
|
|||
import uuid
|
||||
import json
|
||||
from typing import Dict, List, Tuple
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.requests import Request
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
from chatgraph import get_messages, get_llm
|
||||
from models.Chat import Chat
|
||||
|
||||
|
||||
PENDING: Dict[str, Tuple[str, str]] = {} # message_id -> (chat_id, provider)
|
||||
|
||||
MODELS = {
|
||||
"qwen/qwen3-235b-a22b-2507",
|
||||
"deepseek/deepseek-r1-0528",
|
||||
"moonshotai/kimi-k2",
|
||||
"x-ai/grok-4",
|
||||
"openai/gpt-4.1",
|
||||
"anthropic/claude-sonnet-4",
|
||||
"meta-llama/llama-4-maverick",
|
||||
"mistralai/devstral-medium",
|
||||
"qwen/qwen3-coder",
|
||||
"google/gemini-2.5-pro",
|
||||
}
|
||||
|
||||
async def get_models(request: Request):
|
||||
"""GET /models -> {models: [...]}"""
|
||||
return JSONResponse({"models": list(MODELS)})
|
||||
|
||||
async def create_chat(request: Request):
|
||||
"""POST /chats -> {chat_id, model}"""
|
||||
body = await request.json()
|
||||
provider = body.get("model","")
|
||||
if provider not in MODELS:
|
||||
return JSONResponse({"error": "Unknown model"}, status_code=400)
|
||||
chat = Chat()
|
||||
chat_id = str(uuid.uuid4())
|
||||
chat.id = chat_id
|
||||
chat.title = "New Chat"
|
||||
chat.messages = json.dumps([])
|
||||
chat.save()
|
||||
return JSONResponse({"id": chat_id, "model": provider})
|
||||
|
||||
async def history(request : Request):
|
||||
"""GET /chats/{chat_id} -> previous messages"""
|
||||
chat_id = request.path_params["chat_id"]
|
||||
chat = Chat.find(chat_id)
|
||||
if not chat:
|
||||
return JSONResponse({"error": "Not found"}, status_code=404)
|
||||
messages = json.loads(chat.messages) if chat.messages else []
|
||||
return JSONResponse({"messages": messages})
|
||||
|
||||
async def post_message(request: Request):
|
||||
"""POST /chats/{chat_id}/messages
|
||||
Body: {"message": "...", "model": "model_name"}
|
||||
Returns: {"message_id": "<chat_id>"}
|
||||
"""
|
||||
chat_id = request.path_params["chat_id"]
|
||||
chat = Chat.find(chat_id)
|
||||
if not chat:
|
||||
return JSONResponse({"error": "Chat not found"}, status_code=404)
|
||||
|
||||
body = await request.json()
|
||||
user_text = body.get("message", "")
|
||||
provider = body.get("model", "")
|
||||
if provider not in MODELS:
|
||||
return JSONResponse({"error": "Unknown model"}, status_code=400)
|
||||
|
||||
# Load existing messages and add the new user message
|
||||
messages = json.loads(chat.messages) if chat.messages else []
|
||||
messages.append({"role": "human", "content": user_text})
|
||||
chat.messages = json.dumps(messages)
|
||||
chat.save()
|
||||
|
||||
message_id = str(uuid.uuid4())
|
||||
PENDING[message_id] = (chat_id, provider)
|
||||
|
||||
return JSONResponse({
|
||||
"status": "queued",
|
||||
"message_id": message_id
|
||||
})
|
||||
|
||||
async def chat_stream(request):
|
||||
"""GET /chats/{chat_id}/stream?message_id=<chat_id>"""
|
||||
chat_id = request.path_params["chat_id"]
|
||||
message_id = request.query_params.get("message_id")
|
||||
|
||||
if message_id not in PENDING:
|
||||
return JSONResponse({"error": "Not found"}, status_code=404)
|
||||
|
||||
chat_id_from_map, provider = PENDING.pop(message_id)
|
||||
assert chat_id == chat_id_from_map
|
||||
|
||||
chat = Chat.find(chat_id)
|
||||
if not chat:
|
||||
return JSONResponse({"error": "Chat not found"}, status_code=404)
|
||||
|
||||
messages = json.loads(chat.messages) if chat.messages else []
|
||||
msgs = get_messages( messages , chat_id)
|
||||
llm = get_llm(provider)
|
||||
|
||||
async def event_generator():
|
||||
buffer = ""
|
||||
async for chunk in llm.astream(msgs):
|
||||
token = chunk.content
|
||||
buffer += token
|
||||
yield {"data": token}
|
||||
# Finished: store assistant reply
|
||||
messages.append({"role": "assistant", "content": buffer})
|
||||
chat.messages = json.dumps(messages)
|
||||
chat.save()
|
||||
yield {"event": "done", "data": ""}
|
||||
|
||||
return EventSourceResponse(event_generator())
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from masoniteorm.migrations import Migration
|
||||
|
||||
|
||||
class CreateChatsTable(Migration):
|
||||
def up(self):
|
||||
with self.schema.create("chats") as table:
|
||||
table.uuid("id").primary()
|
||||
table.string("title")
|
||||
table.json("messages")
|
||||
|
||||
def down(self):
|
||||
self.schema.drop("chats")
|
||||
24
frontend/.gitignore
vendored
24
frontend/.gitignore
vendored
|
|
@ -1,24 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
972
frontend/deno.lock
generated
972
frontend/deno.lock
generated
|
|
@ -1,972 +0,0 @@
|
|||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"npm:@sveltejs/vite-plugin-svelte@6": "6.1.0_svelte@5.36.8__acorn@8.15.0_vite@7.0.5__picomatch@4.0.3",
|
||||
"npm:@tailwindcss/typography@~0.5.16": "0.5.16_tailwindcss@4.1.11",
|
||||
"npm:@tailwindcss/vite@^4.1.11": "4.1.11_vite@7.0.5__picomatch@4.0.3",
|
||||
"npm:daisyui@^5.0.46": "5.0.46",
|
||||
"npm:marked@^16.1.1": "16.1.1",
|
||||
"npm:rolldown-vite@latest": "7.0.12_picomatch@4.0.3",
|
||||
"npm:svelte@^5.35.5": "5.36.8_acorn@8.15.0",
|
||||
"npm:tailwindcss@^4.1.11": "4.1.11"
|
||||
},
|
||||
"npm": {
|
||||
"@ampproject/remapping@2.3.0": {
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dependencies": [
|
||||
"@jridgewell/gen-mapping",
|
||||
"@jridgewell/trace-mapping"
|
||||
]
|
||||
},
|
||||
"@emnapi/core@1.4.5": {
|
||||
"integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
|
||||
"dependencies": [
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@emnapi/runtime@1.4.5": {
|
||||
"integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@emnapi/wasi-threads@1.0.4": {
|
||||
"integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.25.6": {
|
||||
"integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==",
|
||||
"os": ["aix"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/android-arm64@0.25.6": {
|
||||
"integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/android-arm@0.25.6": {
|
||||
"integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/android-x64@0.25.6": {
|
||||
"integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==",
|
||||
"os": ["android"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.25.6": {
|
||||
"integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/darwin-x64@0.25.6": {
|
||||
"integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.25.6": {
|
||||
"integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.25.6": {
|
||||
"integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/linux-arm64@0.25.6": {
|
||||
"integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/linux-arm@0.25.6": {
|
||||
"integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/linux-ia32@0.25.6": {
|
||||
"integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/linux-loong64@0.25.6": {
|
||||
"integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["loong64"]
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.25.6": {
|
||||
"integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["mips64el"]
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.25.6": {
|
||||
"integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.25.6": {
|
||||
"integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@esbuild/linux-s390x@0.25.6": {
|
||||
"integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["s390x"]
|
||||
},
|
||||
"@esbuild/linux-x64@0.25.6": {
|
||||
"integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/netbsd-arm64@0.25.6": {
|
||||
"integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.25.6": {
|
||||
"integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/openbsd-arm64@0.25.6": {
|
||||
"integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.25.6": {
|
||||
"integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/openharmony-arm64@0.25.6": {
|
||||
"integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==",
|
||||
"os": ["openharmony"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/sunos-x64@0.25.6": {
|
||||
"integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==",
|
||||
"os": ["sunos"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/win32-arm64@0.25.6": {
|
||||
"integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/win32-ia32@0.25.6": {
|
||||
"integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/win32-x64@0.25.6": {
|
||||
"integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@isaacs/fs-minipass@4.0.1": {
|
||||
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
||||
"dependencies": [
|
||||
"minipass"
|
||||
]
|
||||
},
|
||||
"@jridgewell/gen-mapping@0.3.12": {
|
||||
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec",
|
||||
"@jridgewell/trace-mapping"
|
||||
]
|
||||
},
|
||||
"@jridgewell/resolve-uri@3.1.2": {
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
|
||||
},
|
||||
"@jridgewell/sourcemap-codec@1.5.4": {
|
||||
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping@0.3.29": {
|
||||
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
|
||||
"dependencies": [
|
||||
"@jridgewell/resolve-uri",
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"@napi-rs/wasm-runtime@0.2.12": {
|
||||
"integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
|
||||
"dependencies": [
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util@0.10.0"
|
||||
]
|
||||
},
|
||||
"@napi-rs/wasm-runtime@1.0.1": {
|
||||
"integrity": "sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g==",
|
||||
"dependencies": [
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util@0.10.0"
|
||||
]
|
||||
},
|
||||
"@oxc-project/runtime@0.78.0": {
|
||||
"integrity": "sha512-jOU7sDFMyq5ShGJC21UobalVzqcdtWGfySVp8ELvKoVLzMpLHb4kv1bs9VKxaP8XC7Z9hlAXwEKVhCTN+j21aQ=="
|
||||
},
|
||||
"@oxc-project/types@0.78.0": {
|
||||
"integrity": "sha512-8FvExh0WRWN1FoSTjah1xa9RlavZcJQ8/yxRbZ7ElmSa2Ij5f5Em7MvRbSthE6FbwC6Wh8iAw0Gpna7QdoqLGg=="
|
||||
},
|
||||
"@rolldown/binding-android-arm64@1.0.0-beta.30": {
|
||||
"integrity": "sha512-4j7QBitb/WMT1fzdJo7BsFvVNaFR5WCQPdf/RPDHEsgQIYwBaHaL47KTZxncGFQDD1UAKN3XScJ0k7LAsZfsvg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-darwin-arm64@1.0.0-beta.30": {
|
||||
"integrity": "sha512-4vWFTe1o5LXeitI2lW8qMGRxxwrH/LhKd2HDLa/QPhdxohvdnfKyDZWN96XUhDyje2bHFCFyhMs3ak2lg2mJFA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-darwin-x64@1.0.0-beta.30": {
|
||||
"integrity": "sha512-MxrfodqImbsDFFFU/8LxyFPZjt7s4ht8g2Zb76EmIQ+xlmit46L9IzvWiuMpEaSJ5WbnjO7fCDWwakMGyJJ+Dw==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rolldown/binding-freebsd-x64@1.0.0-beta.30": {
|
||||
"integrity": "sha512-c/TQXcATKoO8qE1bCjCOkymZTu7yVUAxBSNLp42Q97XHCb0Cu9v6MjZpB6c7Hq9NQ9NzW44uglak9D/r77JeDw==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.30": {
|
||||
"integrity": "sha512-Vxci4xylM11zVqvrmezAaRjGBDyOlMRtlt7TDgxaBmSYLuiokXbZpD8aoSuOyjUAeN0/tmWItkxNGQza8UWGNQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rolldown/binding-linux-arm64-gnu@1.0.0-beta.30": {
|
||||
"integrity": "sha512-iEBEdSs25Ol0lXyVNs763f7YPAIP0t1EAjoXME81oJ94DesJslaLTj71Rn1shoMDVA+dfkYA286w5uYnOs9ZNA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-linux-arm64-musl@1.0.0-beta.30": {
|
||||
"integrity": "sha512-Ny684Sn1X8c+gGLuDlxkOuwiEE3C7eEOqp1/YVBzQB4HO7U/b4n7alvHvShboOEY5DP1fFUjq6Z+sBLYlCIZbQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-linux-arm64-ohos@1.0.0-beta.30": {
|
||||
"integrity": "sha512-6moyULHDPKwt5RDEV72EqYw5n+s46AerTwtEBau5wCsZd1wuHS1L9z6wqhKISXAFTK9sneN0TEjvYKo+sgbbiA==",
|
||||
"os": ["openharmony"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-linux-x64-gnu@1.0.0-beta.30": {
|
||||
"integrity": "sha512-p0yoPdoGg5Ow2YZKKB5Ypbn58i7u4XFk3PvMkriFnEcgtVk40c5u7miaX7jH0JdzahyXVBJ/KT5yEpJrzQn8yg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rolldown/binding-linux-x64-musl@1.0.0-beta.30": {
|
||||
"integrity": "sha512-sM/KhCrsT0YdHX10mFSr0cvbfk1+btG6ftepAfqhbcDfhi0s65J4dTOxGmklJnJL9i1LXZ8WA3N4wmnqsfoK8Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rolldown/binding-wasm32-wasi@1.0.0-beta.30": {
|
||||
"integrity": "sha512-i3kD5OWs8PQP0V+JW3TFyCLuyjuNzrB45em0g84Jc+gvnDsGVlzVjMNPo7txE/yT8CfE90HC/lDs3ry9FvaUyw==",
|
||||
"dependencies": [
|
||||
"@napi-rs/wasm-runtime@1.0.1"
|
||||
],
|
||||
"cpu": ["wasm32"]
|
||||
},
|
||||
"@rolldown/binding-win32-arm64-msvc@1.0.0-beta.30": {
|
||||
"integrity": "sha512-q7mrYln30V35VrCqnBVQQvNPQm8Om9HC59I3kMYiOWogvJobzSPyO+HA1MP363+Qgwe39I2I1nqBKPOtWZ33AQ==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rolldown/binding-win32-ia32-msvc@1.0.0-beta.30": {
|
||||
"integrity": "sha512-nUqGBt39XTpbBEREEnyKofdP3uz+SN/x2884BH+N3B2NjSUrP6NXwzltM35C0wKK42hX/nthRrwSgj715m99Jw==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@rolldown/binding-win32-x64-msvc@1.0.0-beta.30": {
|
||||
"integrity": "sha512-lbnvUwAXIVWSXAeZrCa4b1KvV/DW0rBnMHuX0T7I6ey1IsXZ90J37dEgt3j48Ex1Cw1E+5H7VDNP2gyOX8iu3w==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rolldown/pluginutils@1.0.0-beta.30": {
|
||||
"integrity": "sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw=="
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi@4.45.1": {
|
||||
"integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-android-arm64@4.45.1": {
|
||||
"integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-darwin-arm64@4.45.1": {
|
||||
"integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-darwin-x64@4.45.1": {
|
||||
"integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-freebsd-arm64@4.45.1": {
|
||||
"integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-freebsd-x64@4.45.1": {
|
||||
"integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.45.1": {
|
||||
"integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.45.1": {
|
||||
"integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-gnu@4.45.1": {
|
||||
"integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-musl@4.45.1": {
|
||||
"integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-linux-loongarch64-gnu@4.45.1": {
|
||||
"integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["loong64"]
|
||||
},
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.45.1": {
|
||||
"integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.45.1": {
|
||||
"integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@rollup/rollup-linux-riscv64-musl@4.45.1": {
|
||||
"integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@rollup/rollup-linux-s390x-gnu@4.45.1": {
|
||||
"integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["s390x"]
|
||||
},
|
||||
"@rollup/rollup-linux-x64-gnu@4.45.1": {
|
||||
"integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-linux-x64-musl@4.45.1": {
|
||||
"integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-win32-arm64-msvc@4.45.1": {
|
||||
"integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-win32-ia32-msvc@4.45.1": {
|
||||
"integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@rollup/rollup-win32-x64-msvc@4.45.1": {
|
||||
"integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@sveltejs/acorn-typescript@1.0.5_acorn@8.15.0": {
|
||||
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
|
||||
"dependencies": [
|
||||
"acorn"
|
||||
]
|
||||
},
|
||||
"@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.0__svelte@5.36.8___acorn@8.15.0__vite@7.0.5___picomatch@4.0.3_svelte@5.36.8__acorn@8.15.0_vite@7.0.5__picomatch@4.0.3": {
|
||||
"integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==",
|
||||
"dependencies": [
|
||||
"@sveltejs/vite-plugin-svelte",
|
||||
"debug",
|
||||
"svelte",
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"@sveltejs/vite-plugin-svelte@6.1.0_svelte@5.36.8__acorn@8.15.0_vite@7.0.5__picomatch@4.0.3": {
|
||||
"integrity": "sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==",
|
||||
"dependencies": [
|
||||
"@sveltejs/vite-plugin-svelte-inspector",
|
||||
"debug",
|
||||
"deepmerge",
|
||||
"kleur",
|
||||
"magic-string",
|
||||
"svelte",
|
||||
"vite",
|
||||
"vitefu"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/node@4.1.11": {
|
||||
"integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==",
|
||||
"dependencies": [
|
||||
"@ampproject/remapping",
|
||||
"enhanced-resolve",
|
||||
"jiti",
|
||||
"lightningcss",
|
||||
"magic-string",
|
||||
"source-map-js",
|
||||
"tailwindcss"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/oxide-android-arm64@4.1.11": {
|
||||
"integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.11": {
|
||||
"integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.11": {
|
||||
"integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.11": {
|
||||
"integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11": {
|
||||
"integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.11": {
|
||||
"integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.11": {
|
||||
"integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.11": {
|
||||
"integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.11": {
|
||||
"integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.11": {
|
||||
"integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==",
|
||||
"dependencies": [
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@emnapi/wasi-threads",
|
||||
"@napi-rs/wasm-runtime@0.2.12",
|
||||
"@tybys/wasm-util@0.9.0",
|
||||
"tslib"
|
||||
],
|
||||
"cpu": ["wasm32"]
|
||||
},
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.11": {
|
||||
"integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.11": {
|
||||
"integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@tailwindcss/oxide@4.1.11": {
|
||||
"integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==",
|
||||
"dependencies": [
|
||||
"detect-libc",
|
||||
"tar"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"@tailwindcss/oxide-android-arm64",
|
||||
"@tailwindcss/oxide-darwin-arm64",
|
||||
"@tailwindcss/oxide-darwin-x64",
|
||||
"@tailwindcss/oxide-freebsd-x64",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu",
|
||||
"@tailwindcss/oxide-linux-arm64-musl",
|
||||
"@tailwindcss/oxide-linux-x64-gnu",
|
||||
"@tailwindcss/oxide-linux-x64-musl",
|
||||
"@tailwindcss/oxide-wasm32-wasi",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc",
|
||||
"@tailwindcss/oxide-win32-x64-msvc"
|
||||
],
|
||||
"scripts": true
|
||||
},
|
||||
"@tailwindcss/typography@0.5.16_tailwindcss@4.1.11": {
|
||||
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
|
||||
"dependencies": [
|
||||
"lodash.castarray",
|
||||
"lodash.isplainobject",
|
||||
"lodash.merge",
|
||||
"postcss-selector-parser",
|
||||
"tailwindcss"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/vite@4.1.11_vite@7.0.5__picomatch@4.0.3": {
|
||||
"integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==",
|
||||
"dependencies": [
|
||||
"@tailwindcss/node",
|
||||
"@tailwindcss/oxide",
|
||||
"tailwindcss",
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"@tybys/wasm-util@0.10.0": {
|
||||
"integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@tybys/wasm-util@0.9.0": {
|
||||
"integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@types/estree@1.0.8": {
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"acorn@8.15.0": {
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"bin": true
|
||||
},
|
||||
"ansis@4.1.0": {
|
||||
"integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="
|
||||
},
|
||||
"aria-query@5.3.2": {
|
||||
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
|
||||
},
|
||||
"axobject-query@4.1.0": {
|
||||
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
|
||||
},
|
||||
"chownr@3.0.0": {
|
||||
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
|
||||
},
|
||||
"clsx@2.1.1": {
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
|
||||
},
|
||||
"cssesc@3.0.0": {
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"bin": true
|
||||
},
|
||||
"daisyui@5.0.46": {
|
||||
"integrity": "sha512-vMDZK1tI/bOb2Mc3Mk5WpquBG3ZqBz1YKZ0xDlvpOvey60dOS4/5Qhdowq1HndbQl7PgDLDYysxAjjUjwR7/eQ=="
|
||||
},
|
||||
"debug@4.4.1": {
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dependencies": [
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
"deepmerge@4.3.1": {
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
|
||||
},
|
||||
"detect-libc@2.0.4": {
|
||||
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="
|
||||
},
|
||||
"enhanced-resolve@5.18.2": {
|
||||
"integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
|
||||
"dependencies": [
|
||||
"graceful-fs",
|
||||
"tapable"
|
||||
]
|
||||
},
|
||||
"esbuild@0.25.6": {
|
||||
"integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==",
|
||||
"optionalDependencies": [
|
||||
"@esbuild/aix-ppc64",
|
||||
"@esbuild/android-arm",
|
||||
"@esbuild/android-arm64",
|
||||
"@esbuild/android-x64",
|
||||
"@esbuild/darwin-arm64",
|
||||
"@esbuild/darwin-x64",
|
||||
"@esbuild/freebsd-arm64",
|
||||
"@esbuild/freebsd-x64",
|
||||
"@esbuild/linux-arm",
|
||||
"@esbuild/linux-arm64",
|
||||
"@esbuild/linux-ia32",
|
||||
"@esbuild/linux-loong64",
|
||||
"@esbuild/linux-mips64el",
|
||||
"@esbuild/linux-ppc64",
|
||||
"@esbuild/linux-riscv64",
|
||||
"@esbuild/linux-s390x",
|
||||
"@esbuild/linux-x64",
|
||||
"@esbuild/netbsd-arm64",
|
||||
"@esbuild/netbsd-x64",
|
||||
"@esbuild/openbsd-arm64",
|
||||
"@esbuild/openbsd-x64",
|
||||
"@esbuild/openharmony-arm64",
|
||||
"@esbuild/sunos-x64",
|
||||
"@esbuild/win32-arm64",
|
||||
"@esbuild/win32-ia32",
|
||||
"@esbuild/win32-x64"
|
||||
],
|
||||
"scripts": true,
|
||||
"bin": true
|
||||
},
|
||||
"esm-env@1.2.2": {
|
||||
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
|
||||
},
|
||||
"esrap@2.1.0": {
|
||||
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"fdir@6.4.6_picomatch@4.0.3": {
|
||||
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
||||
"dependencies": [
|
||||
"picomatch"
|
||||
],
|
||||
"optionalPeers": [
|
||||
"picomatch"
|
||||
]
|
||||
},
|
||||
"fsevents@2.3.3": {
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"os": ["darwin"],
|
||||
"scripts": true
|
||||
},
|
||||
"graceful-fs@4.2.11": {
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"is-reference@3.0.3": {
|
||||
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
||||
"dependencies": [
|
||||
"@types/estree"
|
||||
]
|
||||
},
|
||||
"jiti@2.4.2": {
|
||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
||||
"bin": true
|
||||
},
|
||||
"kleur@4.1.5": {
|
||||
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="
|
||||
},
|
||||
"lightningcss-darwin-arm64@1.30.1": {
|
||||
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"lightningcss-darwin-x64@1.30.1": {
|
||||
"integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"lightningcss-freebsd-x64@1.30.1": {
|
||||
"integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"lightningcss-linux-arm-gnueabihf@1.30.1": {
|
||||
"integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"lightningcss-linux-arm64-gnu@1.30.1": {
|
||||
"integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"lightningcss-linux-arm64-musl@1.30.1": {
|
||||
"integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"lightningcss-linux-x64-gnu@1.30.1": {
|
||||
"integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"lightningcss-linux-x64-musl@1.30.1": {
|
||||
"integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"lightningcss-win32-arm64-msvc@1.30.1": {
|
||||
"integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"lightningcss-win32-x64-msvc@1.30.1": {
|
||||
"integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"lightningcss@1.30.1": {
|
||||
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
|
||||
"dependencies": [
|
||||
"detect-libc"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"lightningcss-darwin-arm64",
|
||||
"lightningcss-darwin-x64",
|
||||
"lightningcss-freebsd-x64",
|
||||
"lightningcss-linux-arm-gnueabihf",
|
||||
"lightningcss-linux-arm64-gnu",
|
||||
"lightningcss-linux-arm64-musl",
|
||||
"lightningcss-linux-x64-gnu",
|
||||
"lightningcss-linux-x64-musl",
|
||||
"lightningcss-win32-arm64-msvc",
|
||||
"lightningcss-win32-x64-msvc"
|
||||
]
|
||||
},
|
||||
"locate-character@3.0.0": {
|
||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
|
||||
},
|
||||
"lodash.castarray@4.4.0": {
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="
|
||||
},
|
||||
"lodash.isplainobject@4.0.6": {
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"lodash.merge@4.6.2": {
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
||||
},
|
||||
"magic-string@0.30.17": {
|
||||
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"marked@16.1.1": {
|
||||
"integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==",
|
||||
"bin": true
|
||||
},
|
||||
"minipass@7.1.2": {
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
|
||||
},
|
||||
"minizlib@3.0.2": {
|
||||
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
|
||||
"dependencies": [
|
||||
"minipass"
|
||||
]
|
||||
},
|
||||
"mkdirp@3.0.1": {
|
||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
||||
"bin": true
|
||||
},
|
||||
"ms@2.1.3": {
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"nanoid@3.3.11": {
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"bin": true
|
||||
},
|
||||
"picocolors@1.1.1": {
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"picomatch@4.0.3": {
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
|
||||
},
|
||||
"postcss-selector-parser@6.0.10": {
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dependencies": [
|
||||
"cssesc",
|
||||
"util-deprecate"
|
||||
]
|
||||
},
|
||||
"postcss@8.5.6": {
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"dependencies": [
|
||||
"nanoid",
|
||||
"picocolors",
|
||||
"source-map-js"
|
||||
]
|
||||
},
|
||||
"rolldown-vite@7.0.12_picomatch@4.0.3": {
|
||||
"integrity": "sha512-Gr40FRnE98FwPJcMwcJgBwP6U7Qxw/VEtDsFdFjvGUTdgI/tTmF7z7dbVo/ajItM54G+Zo9w5BIrUmat6MbuWQ==",
|
||||
"dependencies": [
|
||||
"fdir",
|
||||
"lightningcss",
|
||||
"picomatch",
|
||||
"postcss",
|
||||
"rolldown",
|
||||
"tinyglobby"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"fsevents"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"rolldown@1.0.0-beta.30": {
|
||||
"integrity": "sha512-H/LmDTUPlm65hWOTjXvd1k0qrGinNi8LrG3JsHVm6Oit7STg0upBmgoG5PZUHbAnGTHr0MLoLyzjmH261lIqSg==",
|
||||
"dependencies": [
|
||||
"@oxc-project/runtime",
|
||||
"@oxc-project/types",
|
||||
"@rolldown/pluginutils",
|
||||
"ansis"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"@rolldown/binding-android-arm64",
|
||||
"@rolldown/binding-darwin-arm64",
|
||||
"@rolldown/binding-darwin-x64",
|
||||
"@rolldown/binding-freebsd-x64",
|
||||
"@rolldown/binding-linux-arm-gnueabihf",
|
||||
"@rolldown/binding-linux-arm64-gnu",
|
||||
"@rolldown/binding-linux-arm64-musl",
|
||||
"@rolldown/binding-linux-arm64-ohos",
|
||||
"@rolldown/binding-linux-x64-gnu",
|
||||
"@rolldown/binding-linux-x64-musl",
|
||||
"@rolldown/binding-wasm32-wasi",
|
||||
"@rolldown/binding-win32-arm64-msvc",
|
||||
"@rolldown/binding-win32-ia32-msvc",
|
||||
"@rolldown/binding-win32-x64-msvc"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"rollup@4.45.1": {
|
||||
"integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==",
|
||||
"dependencies": [
|
||||
"@types/estree"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"@rollup/rollup-android-arm-eabi",
|
||||
"@rollup/rollup-android-arm64",
|
||||
"@rollup/rollup-darwin-arm64",
|
||||
"@rollup/rollup-darwin-x64",
|
||||
"@rollup/rollup-freebsd-arm64",
|
||||
"@rollup/rollup-freebsd-x64",
|
||||
"@rollup/rollup-linux-arm-gnueabihf",
|
||||
"@rollup/rollup-linux-arm-musleabihf",
|
||||
"@rollup/rollup-linux-arm64-gnu",
|
||||
"@rollup/rollup-linux-arm64-musl",
|
||||
"@rollup/rollup-linux-loongarch64-gnu",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu",
|
||||
"@rollup/rollup-linux-riscv64-gnu",
|
||||
"@rollup/rollup-linux-riscv64-musl",
|
||||
"@rollup/rollup-linux-s390x-gnu",
|
||||
"@rollup/rollup-linux-x64-gnu",
|
||||
"@rollup/rollup-linux-x64-musl",
|
||||
"@rollup/rollup-win32-arm64-msvc",
|
||||
"@rollup/rollup-win32-ia32-msvc",
|
||||
"@rollup/rollup-win32-x64-msvc",
|
||||
"fsevents"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"source-map-js@1.2.1": {
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||
},
|
||||
"svelte@5.36.8_acorn@8.15.0": {
|
||||
"integrity": "sha512-8JbZWQu96hMjH/oYQPxXW6taeC6Awl6muGHeZzJTxQx7NGRQ/J9wN1hkzRKLOlSDlbS2igiFg7p5xyTp5uXG3A==",
|
||||
"dependencies": [
|
||||
"@ampproject/remapping",
|
||||
"@jridgewell/sourcemap-codec",
|
||||
"@sveltejs/acorn-typescript",
|
||||
"@types/estree",
|
||||
"acorn",
|
||||
"aria-query",
|
||||
"axobject-query",
|
||||
"clsx",
|
||||
"esm-env",
|
||||
"esrap",
|
||||
"is-reference",
|
||||
"locate-character",
|
||||
"magic-string",
|
||||
"zimmerframe"
|
||||
]
|
||||
},
|
||||
"tailwindcss@4.1.11": {
|
||||
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="
|
||||
},
|
||||
"tapable@2.2.2": {
|
||||
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="
|
||||
},
|
||||
"tar@7.4.3": {
|
||||
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
|
||||
"dependencies": [
|
||||
"@isaacs/fs-minipass",
|
||||
"chownr",
|
||||
"minipass",
|
||||
"minizlib",
|
||||
"mkdirp",
|
||||
"yallist"
|
||||
]
|
||||
},
|
||||
"tinyglobby@0.2.14_picomatch@4.0.3": {
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"dependencies": [
|
||||
"fdir",
|
||||
"picomatch"
|
||||
]
|
||||
},
|
||||
"tslib@2.8.1": {
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"util-deprecate@1.0.2": {
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"vite@7.0.5_picomatch@4.0.3": {
|
||||
"integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
|
||||
"dependencies": [
|
||||
"esbuild",
|
||||
"fdir",
|
||||
"picomatch",
|
||||
"postcss",
|
||||
"rollup",
|
||||
"tinyglobby"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"fsevents"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"vitefu@1.1.1_vite@7.0.5__picomatch@4.0.3": {
|
||||
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
|
||||
"dependencies": [
|
||||
"vite"
|
||||
],
|
||||
"optionalPeers": [
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"yallist@5.0.0": {
|
||||
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="
|
||||
},
|
||||
"zimmerframe@1.1.2": {
|
||||
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"packageJson": {
|
||||
"dependencies": [
|
||||
"npm:@sveltejs/vite-plugin-svelte@6",
|
||||
"npm:@tailwindcss/typography@~0.5.16",
|
||||
"npm:@tailwindcss/vite@^4.1.11",
|
||||
"npm:daisyui@^5.0.46",
|
||||
"npm:marked@^16.1.1",
|
||||
"npm:rolldown-vite@latest",
|
||||
"npm:svelte@^5.35.5",
|
||||
"npm:tailwindcss@^4.1.11"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/icon/multibot_32.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Multi chat LLM</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "bundler",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
/**
|
||||
* svelte-preprocess cannot figure out whether you have
|
||||
* a value or a type, so tell TypeScript to enforce using
|
||||
* `import type` instead of `import` for Types.
|
||||
*/
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
* To have warnings / errors of the Svelte compiler at the
|
||||
* correct position, enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable this if you'd like to use dynamic types.
|
||||
*/
|
||||
"checkJs": true
|
||||
},
|
||||
/**
|
||||
* Use global.d.ts instead of compilerOptions.types
|
||||
* to avoid limiting type declarations.
|
||||
*/
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "chatsbt",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"daisyui": "^5.0.46",
|
||||
"marked": "^16.1.1",
|
||||
"svelte": "^5.35.5",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"vite": "npm:rolldown-vite@latest"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 18 KiB |
|
|
@ -1,16 +0,0 @@
|
|||
<script>
|
||||
import Chat from "./lib/Chat.svelte";
|
||||
import ChatList from "./lib/ChatList.svelte";
|
||||
</script>
|
||||
|
||||
<div class="drawer lg:drawer-open">
|
||||
<input id="drawer-toggle" type="checkbox" class="drawer-toggle" />
|
||||
<div class="drawer-content flex flex-col h-screen">
|
||||
<Chat />
|
||||
</div>
|
||||
|
||||
<div class="drawer-side">
|
||||
<label for="drawer-toggle" class="drawer-overlay"></label>
|
||||
<ChatList />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<script>
|
||||
import ChatMessage from "./ChatMessage.svelte";
|
||||
import { chatStore } from "./chatStore.svelte.js";
|
||||
</script>
|
||||
|
||||
<!-- header -->
|
||||
<header class="p-4">
|
||||
<h1 class="text-5xl font-bold">Multi AI Chat</h1>
|
||||
</header>
|
||||
|
||||
<!-- messages -->
|
||||
<main class="flex-1 p-4 space-y-3 overflow-y-auto">
|
||||
{#each chatStore.messages as m (m.id)}
|
||||
<ChatMessage message={m} />
|
||||
{/each}
|
||||
|
||||
{#if chatStore.loading}
|
||||
<div class="chat chat-start">
|
||||
<div class="chat-bubble chat-bubble-secondary loading"></div>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<!-- input -->
|
||||
<footer class="bg-neutral-content rounded-xl">
|
||||
<div class="flex items-center">
|
||||
<div class="form-control flex-1 m-4">
|
||||
<textarea
|
||||
class="textarea w-full"
|
||||
rows="1"
|
||||
placeholder="Type something…"
|
||||
bind:value={chatStore.input}
|
||||
onkeydown={chatStore.handleKey}
|
||||
disabled={chatStore.loading}
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center m-4">
|
||||
<select
|
||||
class="select select-bordered join-item"
|
||||
bind:value={chatStore.model}
|
||||
disabled={chatStore.loading || chatStore.loadingModels}
|
||||
>
|
||||
{#if chatStore.loadingModels}
|
||||
<option value="" disabled>Loading models...</option>
|
||||
{:else if chatStore.models.length === 0}
|
||||
<option value="" disabled>No model available</option>
|
||||
{:else}
|
||||
{#each chatStore.models as modelOption}
|
||||
<option value={modelOption.id || modelOption}>
|
||||
{modelOption.name || modelOption.id || modelOption}
|
||||
</option>
|
||||
{/each}
|
||||
{/if}
|
||||
</select>
|
||||
<button
|
||||
class="btn btn-primary ml-auto"
|
||||
onclick={chatStore.send}
|
||||
disabled={!chatStore.input.trim() || chatStore.models.length === 0}
|
||||
>
|
||||
{#if chatStore.loading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}Send{/if}
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<script>
|
||||
import { chatStore } from "./chatStore.svelte.js";
|
||||
</script>
|
||||
|
||||
<aside class="menu p-4 w-64 bg-base-200 min-h-full">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<span class="text-lg font-bold">Chats</span>
|
||||
<button
|
||||
class="btn btn-xs btn-primary"
|
||||
onclick={() =>
|
||||
chatStore.selectChat(null) && chatStore.createAndSelect()}
|
||||
>
|
||||
New
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="menu menu-compact">
|
||||
{#each chatStore.history as c}
|
||||
<li>
|
||||
<a
|
||||
href="?chat={c.id}"
|
||||
class={chatStore.chatId === c.id ? "active" : ""}
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
chatStore.selectChat(c.id);
|
||||
}}
|
||||
>
|
||||
{c.title}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</aside>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<script>
|
||||
import { marked } from "marked";
|
||||
let { message } = $props(); // { id, role, text }
|
||||
|
||||
const text = $derived(message.text);
|
||||
const me = $derived(message.role == "user");
|
||||
|
||||
/* optional: allow HTML inside the markdown (default is escaped) */
|
||||
marked.setOptions({ breaks: true, gfm: true });
|
||||
</script>
|
||||
|
||||
{#if me}
|
||||
<div class="chat chat-end">
|
||||
<div class="chat-bele chat-bubble chat-bubble-primary">
|
||||
{text}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="chat chat-start">
|
||||
<div
|
||||
class="chat-bele chat-bubble {message.role === 'error'
|
||||
? 'text-error'
|
||||
: ''} prose max-w-none"
|
||||
>
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html marked(text)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
const API = import.meta.env.CHATSBT_API_URL || "";
|
||||
|
||||
export async function createChat(model = "qwen/qwen3-235b-a22b-2507") {
|
||||
const r = await fetch(`${API}/api/chats`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ model }),
|
||||
});
|
||||
return r.json(); // { chat_id }
|
||||
}
|
||||
|
||||
export async function sendUserMessage(chatId, text, model = "") {
|
||||
const r = await fetch(`${API}/api/chats/${chatId}/messages`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ message: text, model }),
|
||||
});
|
||||
return r.json(); // { message_id }
|
||||
}
|
||||
|
||||
export function openStream(chatId, messageId) {
|
||||
return new EventSource(
|
||||
`${API}/api/chats/${chatId}/stream?message_id=${messageId}`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchModels() {
|
||||
try {
|
||||
const response = await fetch(`${API}/api/models`);
|
||||
const data = await response.json();
|
||||
return data.models || [];
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch models:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
import { createChat, sendUserMessage, openStream, fetchModels } from "./chatApi.svelte.js";
|
||||
|
||||
const STORAGE_KEY = "chatHistory";
|
||||
|
||||
function loadHistory() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveHistory(list) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
|
||||
}
|
||||
|
||||
export const chatStore = (() => {
|
||||
let chatId = $state(null);
|
||||
let messages = $state([]);
|
||||
let loading = $state(false);
|
||||
let input = $state("");
|
||||
let model = $state("qwen/qwen3-235b-a22b-2507"); // default
|
||||
let models = $state([]);
|
||||
let loadingModels = $state(true);
|
||||
|
||||
// public helpers
|
||||
const history = $derived(loadHistory());
|
||||
|
||||
function pushHistory(id, title, msgs) {
|
||||
console.log(`push history: ${id} - ${title}`);
|
||||
const h = history.filter((c) => c.id !== id);
|
||||
h.unshift({ id, title, messages: msgs });
|
||||
saveHistory(h.slice(0, 50)); // keep last 50
|
||||
}
|
||||
|
||||
async function selectChat(id) {
|
||||
if (id === chatId) return;
|
||||
chatId = id;
|
||||
const stored = loadHistory().find((c) => c.id === id);
|
||||
messages = stored?.messages || [];
|
||||
loading = true;
|
||||
loading = false;
|
||||
// Update URL with GET parameter
|
||||
const url = new URL(window.location.href);
|
||||
if (id) {
|
||||
url.searchParams.set('chat', id);
|
||||
} else {
|
||||
url.searchParams.delete('chat');
|
||||
}
|
||||
window.history.replaceState({}, "", url);
|
||||
}
|
||||
|
||||
async function createAndSelect() {
|
||||
const { id } = await createChat(model);
|
||||
console.log(id);
|
||||
selectChat(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
async function send() {
|
||||
if (!input.trim()) return;
|
||||
if (!chatId) await createAndSelect();
|
||||
|
||||
const userMsg = { id: crypto.randomUUID(), role: "user", text: input };
|
||||
messages = [...messages, userMsg];
|
||||
|
||||
pushHistory(chatId, userMsg.text.slice(0, 30), messages);
|
||||
|
||||
loading = true;
|
||||
const { message_id } = await sendUserMessage(chatId, input, model);
|
||||
input = "";
|
||||
|
||||
let assistantMsg = { id: message_id, role: "assistant", text: "" };
|
||||
messages = [...messages, assistantMsg];
|
||||
|
||||
const es = openStream(chatId, message_id);
|
||||
es.onmessage = (e) => {
|
||||
assistantMsg = { ...assistantMsg, text: assistantMsg.text + e.data };
|
||||
messages = [...messages.slice(0, -1), assistantMsg];
|
||||
};
|
||||
es.onerror = () => {
|
||||
es.close();
|
||||
loading = false;
|
||||
};
|
||||
es.addEventListener("done", (e) => {
|
||||
console.log(e);
|
||||
es.close();
|
||||
loading = false;
|
||||
pushHistory(chatId, userMsg.text.slice(0, 30), messages);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadModels() {
|
||||
loadingModels = true;
|
||||
models = await fetchModels();
|
||||
loadingModels = false;
|
||||
|
||||
// Set default model if available and not already set
|
||||
if (models.length > 0 && !model) {
|
||||
model = models[0].id || models[0];
|
||||
}
|
||||
}
|
||||
|
||||
function handleKey(e) {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
send();
|
||||
}
|
||||
}
|
||||
|
||||
// initial route handling - use GET parameter instead of path
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const chatIdFromUrl = params.get('chat');
|
||||
const storedHistory = loadHistory();
|
||||
if (chatIdFromUrl && !storedHistory.find((c) => c.id === chatIdFromUrl)) {
|
||||
createAndSelect();
|
||||
} else if (chatIdFromUrl) {
|
||||
selectChat(chatIdFromUrl);
|
||||
}
|
||||
|
||||
// Load models on initialization
|
||||
loadModels();
|
||||
|
||||
return {
|
||||
get chatId() {
|
||||
return chatId;
|
||||
},
|
||||
get messages() {
|
||||
return messages;
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
},
|
||||
get input() {
|
||||
return input;
|
||||
},
|
||||
set input(v) {
|
||||
input = v;
|
||||
},
|
||||
get model() {
|
||||
return model;
|
||||
},
|
||||
set model(v) {
|
||||
model = v;
|
||||
},
|
||||
get models() {
|
||||
return models;
|
||||
},
|
||||
get loadingModels() {
|
||||
return loadingModels;
|
||||
},
|
||||
get history() {
|
||||
return loadHistory();
|
||||
},
|
||||
selectChat,
|
||||
send,
|
||||
handleKey,
|
||||
createAndSelect,
|
||||
loadModels,
|
||||
};
|
||||
})();
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Parse chat ID from GET parameter
|
||||
export function getChatIdFromUrl() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get('chat');
|
||||
}
|
||||
|
||||
// Update URL with GET parameter
|
||||
export function updateUrlWithChatId(chatId) {
|
||||
const url = new URL(window.location.href);
|
||||
if (chatId) {
|
||||
url.searchParams.set('chat', chatId);
|
||||
} else {
|
||||
url.searchParams.delete('chat');
|
||||
}
|
||||
window.history.replaceState({}, "", url);
|
||||
}
|
||||
|
||||
// which chat is on screen right now
|
||||
export let activeChatId = $state(null);
|
||||
|
||||
export function switchChat(chatId) {
|
||||
activeChatId = chatId;
|
||||
updateUrlWithChatId(chatId);
|
||||
}
|
||||
|
||||
export function newChat() {
|
||||
const id = "chat_" + crypto.randomUUID();
|
||||
switchChat(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
// restore last opened chat (or create first one)
|
||||
(() => {
|
||||
const ids = JSON.parse(localStorage.getItem("chat_ids") || "[]");
|
||||
const urlChatId = getChatIdFromUrl();
|
||||
|
||||
if (urlChatId) {
|
||||
switchChat(urlChatId);
|
||||
} else if (ids.length) {
|
||||
switchChat(ids[0]);
|
||||
} else {
|
||||
newChat();
|
||||
}
|
||||
})();
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app'),
|
||||
})
|
||||
|
||||
export default app
|
||||
2
frontend/src/vite-env.d.ts
vendored
2
frontend/src/vite-env.d.ts
vendored
|
|
@ -1,2 +0,0 @@
|
|||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
export default {
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
dev: true,
|
||||
};
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
svelte(),
|
||||
tailwindcss(),
|
||||
],
|
||||
})
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from masoniteorm.models import Model
|
||||
from masoniteorm.scopes import UUIDPrimaryKeyMixin
|
||||
|
||||
class Chat(Model, UUIDPrimaryKeyMixin):
|
||||
__table__ = "chats"
|
||||
__timestamps__ = False
|
||||
__primary_key__ = "id"
|
||||
__incrementing__ = False
|
||||
|
||||
__fillable__ = ["id", "title", "messages"]
|
||||
59
myservers.py
Normal file
59
myservers.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# /// script
|
||||
# dependencies = [
|
||||
# "paramiko",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import paramiko
|
||||
|
||||
# Server configurations
|
||||
servers = [
|
||||
{
|
||||
"host": "server3.example.com",
|
||||
"user": "root",
|
||||
"password": "your_password"
|
||||
},
|
||||
{
|
||||
"host": "192.168.1.100",
|
||||
"user": "deploy",
|
||||
"key": "~/.ssh/deploy_key",
|
||||
"port": 2222 # Optional: custom SSH port
|
||||
}
|
||||
]
|
||||
|
||||
for server in servers:
|
||||
print(f"\n--- {server['host']} ---")
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
try:
|
||||
# Get connection parameters
|
||||
host = server["host"]
|
||||
user = server["user"]
|
||||
port = server.get("port", 22)
|
||||
|
||||
# Connect based on available auth method
|
||||
if "password" in server:
|
||||
ssh.connect(host, port=port, username=user, password=server["password"])
|
||||
elif "key" in server:
|
||||
ssh.connect(host, port=port, username=user, key_filename=server["key"])
|
||||
else:
|
||||
# Try default key locations
|
||||
ssh.connect(host, port=port, username=user)
|
||||
|
||||
# Execute command
|
||||
stdin, stdout, stderr = ssh.exec_command("docker ps")
|
||||
|
||||
output = stdout.read().decode()
|
||||
error = stderr.read().decode()
|
||||
|
||||
if output:
|
||||
print(output)
|
||||
if error:
|
||||
print(f"Error: {error}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed: {e}")
|
||||
finally:
|
||||
ssh.close()
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
[project]
|
||||
name = "chatsbt"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"jinja2>=3.1.6",
|
||||
"langchain>=0.3.26",
|
||||
"langchain-core>=0.3.68",
|
||||
"langchain-openai>=0.3.28",
|
||||
"starlette>=0.47.1",
|
||||
"uvicorn>=0.35.0",
|
||||
"python-dotenv>=1.1.1",
|
||||
"sse-starlette>=2.4.1",
|
||||
"aiosqlite>=0.21.0",
|
||||
"masonite-orm>=3.0.0",
|
||||
]
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
# Hurl Test Script for Chat Backend API
|
||||
# This script tests the complete flow of the backend API
|
||||
|
||||
# Test 1: Get available models
|
||||
GET http://localhost:8000/api/models
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
models: jsonpath "$.models"
|
||||
|
||||
# Validate models response
|
||||
[Asserts]
|
||||
jsonpath "$.models" count > 0
|
||||
|
||||
# Test 2: Create a new chat
|
||||
POST http://localhost:8000/api/chats
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "qwen/qwen3-235b-a22b-2507"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
chat_id: jsonpath "$.id"
|
||||
model: jsonpath "$.model"
|
||||
# Validate chat creation response
|
||||
[Asserts]
|
||||
jsonpath "$.id" != null
|
||||
jsonpath "$.model" == "qwen/qwen3-235b-a22b-2507"
|
||||
|
||||
# Test 3: Get chat history (should be empty initially)
|
||||
GET http://localhost:8000/api/chats/{{chat_id}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
messages: jsonpath "$.messages"
|
||||
# Validate empty history
|
||||
[Asserts]
|
||||
jsonpath "$.messages" count == 0
|
||||
|
||||
# Test 4: Post a message to the chat
|
||||
POST http://localhost:8000/api/chats/{{chat_id}}/messages
|
||||
Content-Type: application/json
|
||||
{
|
||||
"message": "Hello, this is a test message",
|
||||
"model": "{{model}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
message_id: jsonpath "$.message_id"
|
||||
status: jsonpath "$.status"
|
||||
# Validate message posting
|
||||
[Asserts]
|
||||
jsonpath "$.status" == "queued"
|
||||
jsonpath "$.message_id" != null
|
||||
|
||||
# Test 5: Stream the response (SSE)
|
||||
GET http://localhost:8000/api/chats/{{chat_id}}/stream?message_id={{message_id}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "Content-Type" == "text/event-stream; charset=utf-8"
|
||||
|
||||
# Test 6: Verify chat history now contains messages
|
||||
GET http://localhost:8000/api/chats/{{chat_id}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
updated_messages: jsonpath "$.messages"
|
||||
# Validate messages are stored
|
||||
[Asserts]
|
||||
jsonpath "$.messages" count == 2
|
||||
jsonpath "$.messages[0].role" == "human"
|
||||
jsonpath "$.messages[0].content" == "Hello, this is a test message"
|
||||
jsonpath "$.messages[1].role" == "assistant"
|
||||
|
||||
# Test 7: Post another message to test multi-turn conversation
|
||||
POST http://localhost:8000/api/chats/{{chat_id}}/messages
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "Can you tell me a joke?",
|
||||
"model": "{{model}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
message_id2: jsonpath "$.message_id"
|
||||
|
||||
# Test 8: Stream second response
|
||||
GET http://localhost:8000/api/chats/{{chat_id}}/stream?message_id={{message_id2}}
|
||||
|
||||
HTTP 200
|
||||
|
||||
# Test 9: Verify multi-turn conversation history
|
||||
GET http://localhost:8000/api/chats/{{chat_id}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
final_messages: jsonpath "$.messages"
|
||||
# Validate 4 messages (2 human + 2 assistant)
|
||||
[Asserts]
|
||||
jsonpath "$.messages" count == 4
|
||||
|
||||
# Test 10: Error handling - Invalid model
|
||||
POST http://localhost:8000/api/chats
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "invalid-model-name"
|
||||
}
|
||||
|
||||
HTTP 400
|
||||
[Asserts]
|
||||
jsonpath "$.error" == "Unknown model"
|
||||
|
||||
# Test 11: Error handling - Chat not found
|
||||
GET http://localhost:8000/api/chats/non-existent-chat-id
|
||||
|
||||
HTTP 404
|
||||
[Asserts]
|
||||
jsonpath "$.error" == "Not found"
|
||||
|
||||
# Test 12: Error handling - Invalid chat ID for messages
|
||||
POST http://localhost:8000/api/chats/non-existent-chat-id/messages
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "This should fail",
|
||||
"model": "qwen/qwen3-235b-a22b-2507"
|
||||
}
|
||||
|
||||
HTTP 404
|
||||
[Asserts]
|
||||
jsonpath "$.error" == "Chat not found"
|
||||
|
||||
# Test 13: Error handling - Missing message in post
|
||||
POST http://localhost:8000/api/chats/{{chat_id}}/messages
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "{{model}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
# Note: The backend seems to accept empty messages, so this might not fail
|
||||
|
||||
# Test 14: Create another chat with different model
|
||||
POST http://localhost:8000/api/chats
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/gpt-4.1"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
chat_id2: jsonpath "$.id"
|
||||
model2: jsonpath "$.model"
|
||||
|
||||
# Test 15: Verify second chat has different ID
|
||||
[Asserts]
|
||||
variable "chat_id" != "chat_id2"
|
||||
variable "model2" == "openai/gpt-4.1"
|
||||
Loading…
Add table
Add a link
Reference in a new issue