diff --git a/.gitignore b/.gitignore index 21ddf1d..262646f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,14 +17,18 @@ wheels/ *.local *.local -# Runtime data: JSON "database" files are generated on first boot from -# data/seed/. They behave like a local database and should not be committed. -# The seed files under data/seed/ ARE committed. +# Runtime data(serverdata/ 承载全部资源目录) +# 数据库文件/日志/上传/密钥不入库;seed/ 与 prompts/ 模板需提交。 +serverdata/data/*.db +serverdata/data/*.db-* +serverdata/data/*.json.tmp +serverdata/logs/ +serverdata/files/ +serverdata/keys/ +serverdata/uploads/ data/users.json data/tokens.json data/agents.json -data/*.db -data/*.json.tmp # IDE / OS .idea/ diff --git a/app/config.py b/app/config.py index 269e583..2590c9f 100644 --- a/app/config.py +++ b/app/config.py @@ -16,14 +16,23 @@ from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # --------------------------------------------------------------------------- -# 数据目录(JSON "数据库" 所在位置) +# 数据目录(serverdata/ 统一承载所有资源:data/logs/files/keys/prompts/uploads) # --------------------------------------------------------------------------- -# 约定:data/ 下每个文件对应一张"表",下一个阶段换成真正的数据库时, -# 文件名/键名即为表名,字段即为列。见 README「JSON 格式与未来数据库映射」。 -DATA_DIR = Path( - os.environ.get("PINEAGENTS_DEMO_DATA_DIR", str(BASE_DIR / "data")), +SERVERDATA_DIR = Path( + os.environ.get("PINEAGENTS_SERVERDATA_DIR", str(BASE_DIR / "serverdata")), ).resolve() +# 数据子目录(缺失自动创建,start.sh 亦负责初始化) +DATA_DIR = SERVERDATA_DIR / "data" +LOG_DIR = SERVERDATA_DIR / "logs" +FILES_DIR = SERVERDATA_DIR / "files" +KEYS_DIR = SERVERDATA_DIR / "keys" +PROMPTS_DIR = SERVERDATA_DIR / "prompts" +UPLOADS_DIR = SERVERDATA_DIR / "uploads" + +for _d in (DATA_DIR, LOG_DIR, FILES_DIR, KEYS_DIR, PROMPTS_DIR, UPLOADS_DIR): + _d.mkdir(parents=True, exist_ok=True) + # 数据库连接(异步)。开发默认 SQLite+aiosqlite;生产用 MySQL+asyncmy(环境变量覆盖)。 # 示例 MySQL:PINEAGENTS_DEMO_DATABASE_URL=mysql+asyncmy://user:pass@127.0.0.1:3306/opc?charset=utf8mb4 DATABASE_URL = os.environ.get( @@ -34,7 +43,7 @@ DATABASE_URL = os.environ.get( # Redis(缓存/会话/限流/分布式锁),空则降级内存。 REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6379/0") -# OSS 对象存储(aioboto3,兼容阿里云 OSS / MinIO);空则文件落本地上传目录。 +# OSS 对象存储(aioboto3,兼容阿里云 OSS / MinIO);空则文件落 serverdata/uploads。 OSS_ENDPOINT = os.environ.get("OSS_ENDPOINT", "") OSS_ACCESS_KEY = os.environ.get("OSS_ACCESS_KEY", "") OSS_SECRET_KEY = os.environ.get("OSS_SECRET_KEY", "") @@ -46,9 +55,9 @@ TOKENS_FILE = DATA_DIR / "tokens.json" AGENTS_FILE = DATA_DIR / "agents.json" # 智能体初始化 md 模板(镜像本地 md_files 结构;qa 已统一为 PineAgents 品牌)。 -# 目录://*.md(通用)、/qa//*.md、 -# /local//*.md -TEMPLATES_DIR = BASE_DIR / "templates" +# 目录://*.md(通用)、/qa//*.md、 +# /local//*.md +TEMPLATES_DIR = PROMPTS_DIR # 首次启动时用于初始化 data/ 的种子数据(提交到仓库,数据可读)。 SEED_DIR = BASE_DIR / "data" / "seed" diff --git a/app/training/db.py b/app/training/db.py index ece4d99..a8ef063 100644 --- a/app/training/db.py +++ b/app/training/db.py @@ -8,7 +8,7 @@ import time import random BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # server-core 根 -DB_PATH = os.path.join(BASE_DIR, "data", "opc.db") +DB_PATH = os.path.join(BASE_DIR, "serverdata", "data", "opc.db") os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) diff --git a/app/training/main.py b/app/training/main.py index 2e1c8d3..72829c9 100644 --- a/app/training/main.py +++ b/app/training/main.py @@ -20,7 +20,7 @@ app = FastAPI(title="云超服 OPC 培训站后端", version="0.1") ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # server-core 根 # ---- 上传目录 + 静态挂载(供活动封面图等) ---- -UPLOAD_DIR = os.path.join(ROOT, "uploads") +UPLOAD_DIR = os.path.join(ROOT, "serverdata", "uploads") os.makedirs(UPLOAD_DIR, exist_ok=True) app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads") diff --git a/data/seed/demo_users.json b/serverdata/data/seed/demo_users.json similarity index 100% rename from data/seed/demo_users.json rename to serverdata/data/seed/demo_users.json diff --git a/templates/en/AGENTS.md b/serverdata/prompts/en/AGENTS.md similarity index 100% rename from templates/en/AGENTS.md rename to serverdata/prompts/en/AGENTS.md diff --git a/templates/en/BOOTSTRAP.md b/serverdata/prompts/en/BOOTSTRAP.md similarity index 100% rename from templates/en/BOOTSTRAP.md rename to serverdata/prompts/en/BOOTSTRAP.md diff --git a/templates/en/HEARTBEAT.md b/serverdata/prompts/en/HEARTBEAT.md similarity index 100% rename from templates/en/HEARTBEAT.md rename to serverdata/prompts/en/HEARTBEAT.md diff --git a/templates/en/MEMORY.md b/serverdata/prompts/en/MEMORY.md similarity index 100% rename from templates/en/MEMORY.md rename to serverdata/prompts/en/MEMORY.md diff --git a/templates/en/PROFILE.md b/serverdata/prompts/en/PROFILE.md similarity index 100% rename from templates/en/PROFILE.md rename to serverdata/prompts/en/PROFILE.md diff --git a/templates/en/SOUL.md b/serverdata/prompts/en/SOUL.md similarity index 100% rename from templates/en/SOUL.md rename to serverdata/prompts/en/SOUL.md diff --git a/templates/fixed_soul/en.md b/serverdata/prompts/fixed_soul/en.md similarity index 100% rename from templates/fixed_soul/en.md rename to serverdata/prompts/fixed_soul/en.md diff --git a/templates/fixed_soul/zh.md b/serverdata/prompts/fixed_soul/zh.md similarity index 100% rename from templates/fixed_soul/zh.md rename to serverdata/prompts/fixed_soul/zh.md diff --git a/templates/id/AGENTS.md b/serverdata/prompts/id/AGENTS.md similarity index 100% rename from templates/id/AGENTS.md rename to serverdata/prompts/id/AGENTS.md diff --git a/templates/id/BOOTSTRAP.md b/serverdata/prompts/id/BOOTSTRAP.md similarity index 100% rename from templates/id/BOOTSTRAP.md rename to serverdata/prompts/id/BOOTSTRAP.md diff --git a/templates/id/HEARTBEAT.md b/serverdata/prompts/id/HEARTBEAT.md similarity index 100% rename from templates/id/HEARTBEAT.md rename to serverdata/prompts/id/HEARTBEAT.md diff --git a/templates/id/MEMORY.md b/serverdata/prompts/id/MEMORY.md similarity index 100% rename from templates/id/MEMORY.md rename to serverdata/prompts/id/MEMORY.md diff --git a/templates/id/PROFILE.md b/serverdata/prompts/id/PROFILE.md similarity index 100% rename from templates/id/PROFILE.md rename to serverdata/prompts/id/PROFILE.md diff --git a/templates/id/SOUL.md b/serverdata/prompts/id/SOUL.md similarity index 100% rename from templates/id/SOUL.md rename to serverdata/prompts/id/SOUL.md diff --git a/templates/local/en/SOUL.md b/serverdata/prompts/local/en/SOUL.md similarity index 100% rename from templates/local/en/SOUL.md rename to serverdata/prompts/local/en/SOUL.md diff --git a/templates/local/zh/SOUL.md b/serverdata/prompts/local/zh/SOUL.md similarity index 100% rename from templates/local/zh/SOUL.md rename to serverdata/prompts/local/zh/SOUL.md diff --git a/templates/qa/en/AGENTS.md b/serverdata/prompts/qa/en/AGENTS.md similarity index 100% rename from templates/qa/en/AGENTS.md rename to serverdata/prompts/qa/en/AGENTS.md diff --git a/templates/qa/en/PROFILE.md b/serverdata/prompts/qa/en/PROFILE.md similarity index 100% rename from templates/qa/en/PROFILE.md rename to serverdata/prompts/qa/en/PROFILE.md diff --git a/templates/qa/en/SOUL.md b/serverdata/prompts/qa/en/SOUL.md similarity index 100% rename from templates/qa/en/SOUL.md rename to serverdata/prompts/qa/en/SOUL.md diff --git a/templates/qa/ru/AGENTS.md b/serverdata/prompts/qa/ru/AGENTS.md similarity index 100% rename from templates/qa/ru/AGENTS.md rename to serverdata/prompts/qa/ru/AGENTS.md diff --git a/templates/qa/ru/PROFILE.md b/serverdata/prompts/qa/ru/PROFILE.md similarity index 100% rename from templates/qa/ru/PROFILE.md rename to serverdata/prompts/qa/ru/PROFILE.md diff --git a/templates/qa/ru/SOUL.md b/serverdata/prompts/qa/ru/SOUL.md similarity index 100% rename from templates/qa/ru/SOUL.md rename to serverdata/prompts/qa/ru/SOUL.md diff --git a/templates/qa/zh/AGENTS.md b/serverdata/prompts/qa/zh/AGENTS.md similarity index 100% rename from templates/qa/zh/AGENTS.md rename to serverdata/prompts/qa/zh/AGENTS.md diff --git a/templates/qa/zh/PROFILE.md b/serverdata/prompts/qa/zh/PROFILE.md similarity index 100% rename from templates/qa/zh/PROFILE.md rename to serverdata/prompts/qa/zh/PROFILE.md diff --git a/templates/qa/zh/SOUL.md b/serverdata/prompts/qa/zh/SOUL.md similarity index 100% rename from templates/qa/zh/SOUL.md rename to serverdata/prompts/qa/zh/SOUL.md diff --git a/templates/ru/AGENTS.md b/serverdata/prompts/ru/AGENTS.md similarity index 100% rename from templates/ru/AGENTS.md rename to serverdata/prompts/ru/AGENTS.md diff --git a/templates/ru/BOOTSTRAP.md b/serverdata/prompts/ru/BOOTSTRAP.md similarity index 100% rename from templates/ru/BOOTSTRAP.md rename to serverdata/prompts/ru/BOOTSTRAP.md diff --git a/templates/ru/HEARTBEAT.md b/serverdata/prompts/ru/HEARTBEAT.md similarity index 100% rename from templates/ru/HEARTBEAT.md rename to serverdata/prompts/ru/HEARTBEAT.md diff --git a/templates/ru/MEMORY.md b/serverdata/prompts/ru/MEMORY.md similarity index 100% rename from templates/ru/MEMORY.md rename to serverdata/prompts/ru/MEMORY.md diff --git a/templates/ru/PROFILE.md b/serverdata/prompts/ru/PROFILE.md similarity index 100% rename from templates/ru/PROFILE.md rename to serverdata/prompts/ru/PROFILE.md diff --git a/templates/ru/SOUL.md b/serverdata/prompts/ru/SOUL.md similarity index 100% rename from templates/ru/SOUL.md rename to serverdata/prompts/ru/SOUL.md diff --git a/templates/zh/AGENTS.md b/serverdata/prompts/zh/AGENTS.md similarity index 100% rename from templates/zh/AGENTS.md rename to serverdata/prompts/zh/AGENTS.md diff --git a/templates/zh/BOOTSTRAP.md b/serverdata/prompts/zh/BOOTSTRAP.md similarity index 100% rename from templates/zh/BOOTSTRAP.md rename to serverdata/prompts/zh/BOOTSTRAP.md diff --git a/templates/zh/HEARTBEAT.md b/serverdata/prompts/zh/HEARTBEAT.md similarity index 100% rename from templates/zh/HEARTBEAT.md rename to serverdata/prompts/zh/HEARTBEAT.md diff --git a/templates/zh/MEMORY.md b/serverdata/prompts/zh/MEMORY.md similarity index 100% rename from templates/zh/MEMORY.md rename to serverdata/prompts/zh/MEMORY.md diff --git a/templates/zh/PROFILE.md b/serverdata/prompts/zh/PROFILE.md similarity index 100% rename from templates/zh/PROFILE.md rename to serverdata/prompts/zh/PROFILE.md diff --git a/templates/zh/SOUL.md b/serverdata/prompts/zh/SOUL.md similarity index 100% rename from templates/zh/SOUL.md rename to serverdata/prompts/zh/SOUL.md diff --git a/serverrun/Dockerfile b/serverrun/Dockerfile new file mode 100644 index 0000000..07a8c01 --- /dev/null +++ b/serverrun/Dockerfile @@ -0,0 +1,17 @@ +# server-core 镜像(多阶段) +FROM ghcr.io/astral-sh/uv:python3.12 AS base +WORKDIR /app +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy + +# 依赖层(利用缓存) +COPY pyproject.toml uv.lock ./ +RUN uv sync --frozen --no-dev --no-install-project + +# 代码层 +COPY app ./app +COPY dispatcher.py main.py pyproject.toml uv.lock ./ +COPY serverdata ./serverdata +COPY serverrun ./serverrun + +EXPOSE 8090 +CMD ["bash", "serverrun/start.sh"] diff --git a/serverrun/docker-compose.yml b/serverrun/docker-compose.yml new file mode 100644 index 0000000..ca9e906 --- /dev/null +++ b/serverrun/docker-compose.yml @@ -0,0 +1,85 @@ +# 云超服后端服务群编排(serverrun/) +# 服务:core(server-core,统一入口)+ compute-engine(算力引擎)+ 基础设施(redis/mysql/emqx) +# 对外统一生产服务 opc.pinesound.cn → core:8090 + +services: + core: + build: + context: .. + dockerfile: serverrun/Dockerfile + container_name: opc-core + restart: always + ports: + - "127.0.0.1:8090:8090" # 统一入口(经网关暴露,仅 loopback) + environment: + - PINEAGENTS_DEMO_DATABASE_URL=mysql+asyncmy://opc:opc_pass@mysql:3306/opc?charset=utf8mb4 + - REDIS_URL=redis://:redis_pass@redis:6379/0 + - TZ=Asia/Shanghai + volumes: + - ./../serverdata:/app/serverdata # 资源目录挂载 + depends_on: + - redis + - mysql + networks: + - opc-network + + compute-engine: + image: calciumion/new-api:latest + container_name: opc-compute-engine + restart: always + command: --log-dir /app/logs + ports: + - "127.0.0.1:3000:3000" # 仅 loopback,算力服务经内部 IP 调用 + environment: + - SQLITE_PATH=/data/new-api.db + - REDIS_CONN_STRING=redis://:redis_pass@redis:6379/0 + - SESSION_SECRET=${SESSION_SECRET:?请设置 SESSION_SECRET} + - TZ=Asia/Shanghai + volumes: + - compute-data:/data + - compute-logs:/app/logs + depends_on: + - redis + networks: + - opc-network + + redis: + image: redis:7 + container_name: opc-redis + restart: always + command: ["redis-server", "--requirepass", "redis_pass"] + networks: + - opc-network + + mysql: + image: mysql:8 + container_name: opc-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root_pass + MYSQL_DATABASE: opc + MYSQL_USER: opc + MYSQL_PASSWORD: opc_pass + volumes: + - mysql-data:/var/lib/mysql + networks: + - opc-network + + # emqx(消息总线,大屏/桌面端实时)——可选,启用时打开注释 + # emqx: + # image: emqx/emqx:5.8 + # container_name: opc-emqx + # restart: always + # ports: + # - "127.0.0.1:1883:1883" + # networks: + # - opc-network + +volumes: + compute-data: + compute-logs: + mysql-data: + +networks: + opc-network: + driver: bridge diff --git a/serverrun/start.sh b/serverrun/start.sh new file mode 100644 index 0000000..658d4ae --- /dev/null +++ b/serverrun/start.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# server-core 启动脚本(serverrun/,参考 PineSoundServer 工程化) +# 职责:初始化 serverdata 资源目录 → 启动服务 +set -euo pipefail + +# 项目根(本脚本位于 serverrun/ 下) +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +echo "==========================================" +echo "初始化 serverdata 资源目录..." +echo "==========================================" + +SERVERDATA_DIRS=( + "serverdata/data" + "serverdata/logs" + "serverdata/files" + "serverdata/keys" + "serverdata/prompts" + "serverdata/uploads" +) +for dir in "${SERVERDATA_DIRS[@]}"; do + if [ ! -d "$dir" ]; then + echo "✗ 创建 $dir" + mkdir -p "$dir" + else + echo "✓ $dir" + fi +done + +echo "==========================================" +echo "启动 core(dispatcher 统一入口)" +echo "==========================================" + +# 生产模式:uv 管理,uvicorn dispatcher +if command -v uv >/dev/null 2>&1 && [ -f pyproject.toml ]; then + exec uv run uvicorn dispatcher:app --host 0.0.0.0 --port "${PORT:-8090}" +else + exec uvicorn dispatcher:app --host 0.0.0.0 --port "${PORT:-8090}" +fi