
基于 Laya 的 System One HTTP 服务与单管理员控制台。
-本仓库不跟踪 Laya 源码。构建前在仓库根目录检出固定的 v0.3.7 版本: +## 功能 -```sh -git clone https://github.com/NandhaKishorM/laya.git laya -git -C laya checkout --detach 010bacef009c855ccba814b51f7c8e1d38ab5e3f -sh scripts/check-upstream.sh -``` - -`laya/` 已加入 `.gitignore`,但会进入 Docker 构建上下文。Dockerfile 验证完整 SHA 和干净工作区,最终镜像只包含运行所需的上游包和许可证,不包含 `.git`。 - -## 管理员与配置 - -Docker Compose 只传入当前 shell 或部署平台设置的 `LAYA_ADMIN_USERNAME` 和 `LAYA_ADMIN_PASSWORD`,不需要 `.env` 文件。密码至少 10 个字符;服务启动时会在内存中生成 Argon2id 哈希用于登录校验。管理操作仍要求会话和 CSRF token。其他运行参数使用后端默认值。`.env.example` 仅供本地热更新脚本使用,不要提交实际密码。 - -```sh -python3.12 -m venv .venv -.venv/bin/python -m pip install -e 'backend[test]' -``` - -## 模型文件 - -仓库不跟踪模型权重。Dockerfile 在构建阶段从 Hugging Face 固定提交 `1c5edc17a7acd8701df6fc341c0d179f1c62c982` 下载 **multilingual** checkpoint,只把该模型文件复制到最终镜像的 `/opt/models/multilingual`。构建时在离线模式下分别执行英文和中文推理,失败则不会发布镜像。运行容器无需下载模型,也无需挂载模型卷。`GET /health/ready` 检查镜像中的模型文件。 - -发布镜像的 `LAYA_MODEL_PROFILE=multilingual`:`model=auto` 和 `model=multilingual` 都使用此模型;显式请求 `english` 或 `typed-decisions` 返回 `422 MODEL_NOT_AVAILABLE`。Playground 只列出镜像支持的模型。一个应用进程默认最多驻留一个模型,可用 `LAYA_MAX_LOADED_MODELS` 调整;该值控制模型缓存数量,不限制同时处理的请求数。实际并发能力取决于运行时线程池、模型和机器资源。发布镜像使用 PyTorch CPU wheel,当前 Action 构建 `linux/amd64`。 - -本地先安装上游运行依赖与被忽略的 Laya 检出,再下载三个固定版本模型,运行包含英文、中文显式选型、中文自动路由和 typed-decisions 的真实请求冒烟测试: - -```sh -.venv/bin/python -m pip install torch==2.5.1 transformers==4.48.3 safetensors==0.5.3 huggingface-hub==0.29.3 numpy==1.26.4 -.venv/bin/python -m pip install --no-deps -e ./laya -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model english -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model multilingual -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model typed-decisions -LAYA_MODEL_DIR=models .venv/bin/python scripts/smoke-real-model.py -``` - -模型下载与运行均依赖上游的 PyTorch、Transformers、Safetensors、Hugging Face Hub 和 NumPy。 - -## 启动 - -在 GitHub 仓库的 **Settings → Secrets and variables → Actions** 配置 `DOCKERHUB_USERNAME` 和 `DOCKERHUB_TOKEN`(需要有 `1panel/laya-server` 的推送权限)。在 **Actions → Build and push LAYA SERVER → Run workflow** 输入版本标签。正式发布时可同时勾选 `latest`;测试标签保持关闭。工作流会检出被忽略的上游 v0.3.7 源码并校验 SHA,运行后端测试,再构建及推送镜像。 - -在当前 shell 中设置变量,拉取已发布镜像并启动: - -```sh -export LAYA_ADMIN_USERNAME=admin -export LAYA_ADMIN_PASSWORD='replace-with-a-password-of-at-least-10-characters' -export LAYA_IMAGE_TAG=dev -docker compose pull -docker compose up -d -``` - -也可用本地已检出的上游源码构建: +- 通过 `POST /v1/systemone` 和 Bearer API Key 调用 Laya 推理,支持 `noul`、`choice` 和 `score` 问题。 +- 在控制台创建、撤销 API Key,使用 Playground 调试请求,并查看 token 用量与每日趋势。 +- FastAPI、React 和 SQLite 部署在同一容器中;控制台支持简体中文、英语和繁体中文。 -```sh -sh scripts/check-upstream.sh -docker build -t 1panel/laya-server:dev . -export LAYA_IMAGE_TAG=dev -docker compose up -d -``` +## 快速开始 -如使用 `latest`,直接执行: +镜像发布后,安装 Docker 并运行(如需固定版本,将 `latest` 换成对应标签): ```sh -unset LAYA_IMAGE_TAG -docker compose pull -docker compose up -d +docker run -d --name laya-server --init --restart unless-stopped \ + -p 127.0.0.1:8080:8080 \ + -v laya-data:/data \ + -e LAYA_ADMIN_USERNAME=admin \ + -e LAYA_ADMIN_PASSWORD='replace-with-a-password-of-at-least-10-characters' \ + 1panel/laya-server:latest ``` -Compose 从启动它的进程环境传入管理员配置,只启动一个应用服务并将 `127.0.0.1:8080` 暴露给宿主机。公网入口需由外部反向代理提供 HTTPS,并将请求转发到该端口。SQLite 数据在 `laya-data` 卷;更新容器不会丢失数据库。构建机器需要能访问 PyPI、PyTorch CPU 包索引、npm registry 和 Hugging Face;已构建镜像启动时无需拉取源码、依赖或模型。 - -如果由现有的 1Panel 反向代理提供公网 HTTPS,将域名请求转发到宿主机的 `127.0.0.1:8080`。登录时服务根据请求是否为 HTTPS 设置 Cookie 的 `Secure` 标记;请让反向代理正确转发协议。反向代理不属于本项目的应用容器。 - -### 本地开发(热更新) +将示例密码换成至少 10 个字符的密码。打开 `http://127.0.0.1:8080` 登录,在 **API Keys** 页面创建密钥;完整密钥只显示一次。`/health/ready` 可用于检查模型文件。数据保存在 `laya-data` 卷中。 -第一次先执行 `cp .env.example .env`,将 `.env` 中的 `LAYA_ADMIN_USERNAME` 和 `LAYA_ADMIN_PASSWORD` 填写好。若使用哈希配置,则将 `LAYA_ADMIN_PASSWORD` 留空并填写 `LAYA_ADMIN_PASSWORD_HASH='...'`(保留单引号)。`scripts/dev-backend.sh` 会把本地来源、SQLite 路径和模型路径设为开发值。确保上面的 Python 依赖与三个模型已经准备好;前端开发建议使用 Node.js 24 和 pnpm 11.19.0,与 Dockerfile 的构建环境一致。 +容器端口只绑定本机。如需公网访问,可用现有反向代理转发到 `127.0.0.1:8080`,并在公网入口配置 HTTPS。 -分别打开两个终端,在仓库根目录运行: +## 调用 API ```sh -# 终端 1:FastAPI 与 SQLite -sh scripts/dev-backend.sh -``` - -```sh -# 终端 2:React/Vite -cd frontend -# 首次运行时安装依赖 -pnpm install --frozen-lockfile -pnpm dev -``` - -打开 `http://127.0.0.1:5173` 登录。Vite 将 `/internal` 和 `/v1` 代理到本地 8000 端口;生产环境仍由同一个 FastAPI 容器提供构建后的前端。若只想用一个本地进程,可先在 `frontend/` 运行 `pnpm build`,再启动 Uvicorn 并打开 8000 端口。 - -控制台支持简体中文、English 和繁體中文。登录页及登录后的顶部栏均可切换语言;首次访问按浏览器语言选择,手动选择会保存在当前浏览器中。 - -## 调用接口 - -登录控制台后在 **API Keys** 页面创建密钥。完整密钥只在创建响应中显示一次。 - -```sh -curl -X POST https://console.example.com/v1/systemone \ +curl -X POST http://127.0.0.1:8080/v1/systemone \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"state":{"message":"I was charged twice"},"questions":{"refund":{"type":"noul","instructions":"Does the customer ask for a refund?"}}}' ``` -请求中的 `state` 可为字符串、JSON 对象或数组;`questions` 是非空问题 ID 映射,支持 `noul`、`choice` 和 `score`。发布镜像只内置 multilingual,`model` 可选 `auto`(默认)或 `multilingual`;显式请求 `english` 或 `typed-decisions` 返回 `422 MODEL_NOT_AVAILABLE`。本地源码开发默认仍可使用全部三个模型,前提是已下载对应权重。返回结果保留上游 `answers`、`model` 和 `usage`。错误使用 `detail.code` 和 `detail.message`;无效密钥为 401,请求校验失败为 422,模型不可用为 503。控制台 Playground 使用管理员会话,记录为单独用量来源。 - -## 数据与维护 - -SQLite 开启 WAL 模式;备份时应先停止应用,再复制数据库文件或使用 SQLite backup API,避免遗漏 WAL 中的数据。恢复时停止应用,替换持久化卷内的数据库,再启动。升级 Laya 时更新 `scripts/check-upstream.sh` 和 Dockerfile 中的完整 SHA,重新检出 `laya/`,运行测试并做真实模型冒烟测试。 - -```sh -.venv/bin/python -m pytest backend/tests -q -git ls-files laya/ -``` +成功响应包含 `answers`、`model` 和实际的 `usage` token 计数。发布镜像内置 multilingual 模型;`model=auto`(默认)和 `model=multilingual` 均可使用。更多请求格式见控制台 **Documentation** 页面。 -第二条命令应无输出。 +## 许可与反馈 -部署完成后,建议先访问 `GET /health/ready` 确认模型文件已就绪,再登录控制台通过 Playground 发起一次测试请求,检查实际推理和响应是否正常。 +本项目采用 [Apache License 2.0](LICENSE) 许可证。问题和建议请提交至 [GitHub Issues](https://github.com/1Panel-dev/laya-server/issues)。 diff --git a/README_es.md b/README_es.md deleted file mode 100644 index 0f9b153..0000000 --- a/README_es.md +++ /dev/null @@ -1,106 +0,0 @@ -# Laya Server - -Servicio HTTP independiente de Laya System One y consola de un solo administrador. El backend usa FastAPI + SQLite; el frontend usa React + Vite + TypeScript con shadcn/ui. Tras la compilación, un único contenedor de aplicación sirve tanto la interfaz web como la API. - -## Código fuente upstream - -Este repositorio no rastrea el código fuente de Laya. Antes de compilar, chequea la versión fija v0.3.7 en el directorio raíz del repositorio: - -```sh -git clone https://github.com/NandhaKishorM/laya.git laya -git -C laya checkout --detach 010bacef009c855ccba814b51f7c8e1d38ab5e3f -sh scripts/check-upstream.sh -``` - -`laya/` está añadido a `.gitignore`, pero sí forma parte del contexto de compilación de Docker. El Dockerfile verifica el SHA completo y un árbol de trabajo limpio; la imagen final solo incluye los paquetes upstream y las licencias necesarios para ejecutar, sin `.git`. - -## Administrador y configuración - -Docker Compose recibe solo `LAYA_ADMIN_USERNAME` y `LAYA_ADMIN_PASSWORD` del shell o de la plataforma de despliegue; no necesita un archivo `.env`. La contraseña debe tener al menos 10 caracteres. Las operaciones administrativas siguen requiriendo sesión y token CSRF. Los demás parámetros usan los valores predeterminados del backend. `.env.example` se usa solo para el script de desarrollo local; no confirmes contraseñas reales. - -```sh -python3.12 -m venv .venv -.venv/bin/python -m pip install -e 'backend[test]' -``` - -## Archivos de modelo - -El repositorio no incluye pesos. El Dockerfile descarga el checkpoint **multilingual** desde el commit fijo de Hugging Face `1c5edc17a7acd8701df6fc341c0d179f1c62c982` durante la compilación y lo incorpora a `/opt/models/multilingual` en la imagen final. La compilación comprueba la inferencia sin conexión en inglés y chino. Al arrancar el contenedor no hay que descargar ni montar modelos. `GET /health/ready` comprueba los archivos incluidos. - -La imagen publicada usa `LAYA_MODEL_PROFILE=multilingual`: `model=auto` y `model=multilingual` utilizan ese checkpoint; solicitar `english` o `typed-decisions` devuelve `422 MODEL_NOT_AVAILABLE`. `LAYA_MAX_LOADED_MODELS` controla cuántos modelos permanecen en memoria, no el número de solicitudes simultáneas. La imagen usa PyTorch para CPU y el workflow construye `linux/amd64`. - -En local, instala primero las dependencias de ejecución upstream y el checkout de Laya ignorado por Git, descarga los tres modelos en sus versiones fijas y ejecuta una prueba de humo con solicitudes reales que cubra inglés, selección explícita en chino, enrutado automático en chino y typed-decisions: - -```sh -.venv/bin/python -m pip install torch==2.5.1 transformers==4.48.3 safetensors==0.5.3 huggingface-hub==0.29.3 numpy==1.26.4 -.venv/bin/python -m pip install --no-deps -e ./laya -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model english -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model multilingual -LAYA_MODEL_DIR=models .venv/bin/python scripts/download-models.py --model typed-decisions -LAYA_MODEL_DIR=models .venv/bin/python scripts/smoke-real-model.py -``` - -La descarga de modelos y la ejecución dependen de PyTorch, Transformers, Safetensors, Hugging Face Hub y NumPy upstream. - -## Arranque - -Exporta las variables desde el shell y arranca la imagen publicada: - -```sh -export LAYA_ADMIN_USERNAME=admin -export LAYA_ADMIN_PASSWORD='replace-with-a-password-of-at-least-10-characters' -export LAYA_IMAGE_TAG=dev -docker compose pull -docker compose up -d -``` - -Compose transmite las variables del proceso que lo ejecuta, inicia un único servicio y expone `127.0.0.1:8080` al host. Un proxy inverso externo debe proporcionar HTTPS y reenviar las solicitudes a ese puerto. Los datos de SQLite se conservan en el volumen `laya-data`. La máquina de compilación necesita acceso a PyPI, al índice de PyTorch para CPU, al registry de npm y a Hugging Face; la imagen ya compilada no descarga código, dependencias ni modelos al arrancar. - -Si un proxy inverso 1Panel existente provee el HTTPS público, reenvía las solicitudes del dominio a `127.0.0.1:8080` del host. Durante el inicio de sesión, el servicio establece el atributo `Secure` de las cookies según si la solicitud usa HTTPS; configura el proxy para que transmita correctamente el protocolo. El proxy inverso no forma parte del contenedor de aplicación de este proyecto. - -### Desarrollo local (recarga en caliente) - -La primera vez ejecuta `cp .env.example .env` y rellena `LAYA_ADMIN_USERNAME` y `LAYA_ADMIN_PASSWORD` en `.env`. Si usas la configuración por hash, deja `LAYA_ADMIN_PASSWORD` vacío y rellena `LAYA_ADMIN_PASSWORD_HASH='...'` (conserva las comillas simples). `scripts/dev-backend.sh` establece el origen local, la ruta de SQLite y la ruta de modelos en valores de desarrollo. Asegúrate de que las dependencias de Python/frontend anteriores y los tres modelos ya estén listos. - -Abre dos terminales y ejecuta en el directorio raíz del repositorio: - -```sh -# Terminal 1: FastAPI y SQLite -sh scripts/dev-backend.sh -``` - -```sh -# Terminal 2: React/Vite -cd frontend -pnpm dev -``` - -Abre `http://127.0.0.1:5173` e inicia sesión. Vite redirige `/internal` y `/v1` al puerto local 8000; en producción el frontend compilado lo sigue sirviendo el mismo contenedor de FastAPI. Si solo quieres un proceso local, ejecuta primero `pnpm build` en `frontend/`, arranca Uvicorn y abre el puerto 8000. - -La consola admite chino simplificado, inglés y chino tradicional. Puedes cambiar el idioma tanto en la página de inicio de sesión como en la barra superior una vez dentro; en la primera visita se elige el idioma del navegador y la elección manual se guarda en el navegador actual. - -## Llamar a la API - -Crea una clave en la página **API Keys** tras iniciar sesión en la consola. La clave completa solo se muestra una vez, en la respuesta de creación. Sustituye `TU_CLAVE_API` en el siguiente ejemplo por esa clave. - -```sh -curl -X POST https://console.example.com/v1/systemone \ - -H 'Authorization: Bearer TU_CLAVE_API' \ - -H 'Content-Type: application/json' \ - -d '{"state":{"message":"I was charged twice"},"questions":{"refund":{"type":"noul","instructions":"Does the customer ask for a refund?"}}}' -``` - -En la solicitud, `state` puede ser una cadena, un objeto JSON o un array; `questions` es un mapa no vacío que admite `noul`, `choice` y `score`. En la imagen publicada, `model` admite `auto` (predeterminado) o `multilingual`; pedir otro modelo devuelve `422 MODEL_NOT_AVAILABLE`. El desarrollo local admite los tres modelos si se han descargado. La respuesta conserva `answers`, `model` y `usage` de upstream. Los errores usan `detail.code` y `detail.message`. Una clave no válida devuelve 401, un fallo de validación 422 y un modelo no disponible 503. El Playground de la consola usa la sesión de administrador y se registra como una fuente de consumo independiente. - -## Datos y mantenimiento - -SQLite está en modo WAL; al hacer copias de seguridad debes detener primero la aplicación y después copiar el archivo de base de datos o usar la API de copia de seguridad de SQLite, para no perder los datos del WAL. Para restaurar, detén la aplicación, sustituye la base de datos dentro del volumen persistente y vuelve a iniciar. Al actualizar Laya, actualiza el SHA completo en `scripts/check-upstream.sh` y en el Dockerfile, vuelve a chekear `laya/`, ejecuta las pruebas y haz una prueba de humo con modelos reales. - -```sh -.venv/bin/python -m pytest backend/tests -q -git ls-files laya/ -``` - -El segundo comando no debe producir salida. - -Tras el despliegue, se recomienda visitar primero `GET /health/ready` para confirmar que los archivos de modelo están listos y, a continuación, iniciar sesión en la consola y lanzar una solicitud de prueba desde el Playground para comprobar que la inferencia real y las respuestas funcionan correctamente. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d4f0f27..5c3408f 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,6 +1,7 @@ [project] name = "laya-server" version = "0.1.0" +license = { text = "Apache-2.0" } requires-python = ">=3.12" dependencies = [ "fastapi==0.141.1", diff --git a/frontend/package.json b/frontend/package.json index ff19965..90974d8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,6 +2,7 @@ "name": "laya-server", "private": true, "version": "0.1.0", + "license": "Apache-2.0", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1",