diff --git a/frameworks/ragflow/0.24.0/.env b/frameworks/ragflow/0.24.0/.env new file mode 100644 index 0000000000000000000000000000000000000000..1c8183b60025cf32627251a3d92e755770392688 --- /dev/null +++ b/frameworks/ragflow/0.24.0/.env @@ -0,0 +1,32 @@ +DOC_ENGINE=elasticsearch +DEVICE=cpu +COMPOSE_PROFILES=${DOC_ENGINE},${DEVICE} +STACK_VERSION=8.11.3 +ES_HOST=es01 +ES_PORT=1200 +ELASTIC_PASSWORD=infini_rag_flow +MEM_LIMIT=2147483648 +MYSQL_PASSWORD=infini_rag_flow +MYSQL_HOST=mysql +MYSQL_DBNAME=rag_flow +MYSQL_PORT=3306 +EXPOSE_MYSQL_PORT=5455 +MYSQL_MAX_PACKET=1073741824 +MINIO_HOST=minio +MINIO_CONSOLE_PORT=9001 +MINIO_PORT=9000 +MINIO_USER=rag_flow +MINIO_PASSWORD=infini_rag_flow +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=infini_rag_flow +SVR_WEB_HTTP_PORT=80 +SVR_WEB_HTTPS_PORT=443 +SVR_HTTP_PORT=9380 +ADMIN_SVR_HTTP_PORT=9381 +SVR_MCP_PORT=9382 +RAGFLOW_IMAGE=oc9-ragflow:0.24.0 +TZ=Asia/Shanghai +HF_ENDPOINT=https://hf-mirror.com +DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 +REGISTER_ENABLED=1 diff --git a/frameworks/ragflow/0.24.0/Dockerfile b/frameworks/ragflow/0.24.0/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4ed817b003a7b56463ac1d9f20b7e7812a8f1d5f --- /dev/null +++ b/frameworks/ragflow/0.24.0/Dockerfile @@ -0,0 +1,212 @@ +# RAGFlow 0.24.0 on OpenCloudOS 9 (CPU) +# Based on: https://github.com/infiniflow/ragflow/blob/v0.24.0/Dockerfile +# Adapted for opencloudos/opencloudos9-minimal + +# base stage +FROM opencloudos/opencloudos9-minimal:latest AS base + +LABEL maintainer="OpenCloudOS Community" +LABEL org.opencontainers.image.source="https://gitee.com/OpenCloudOS/ai-agent-container" +LABEL org.opencontainers.image.description="RAGFlow 0.24.0 (CPU) on OpenCloudOS 9" + +USER root +SHELL ["/bin/bash", "-c"] + +WORKDIR /ragflow + +# Install system dependencies +# opencv-python: glib2 mesa-libGL +# python-pptx: java-17-openjdk +# Building C extensions: python3-devel nss openssl-devel +RUN dnf install -y --setopt=install_weak_deps=False --skip-broken \ + ca-certificates \ + glib2 \ + mesa-libGL \ + pkgconfig \ + libicu-devel \ + java-17-openjdk \ + atk-devel \ + python3-devel \ + gtk4-devel \ + nss \ + xdg-utils \ + mesa-libgbm-devel \ + gnupg2 \ + unzip \ + curl \ + wget \ + git \ + vim-minimal \ + less \ + fontconfig \ + google-noto-sans-cjk-ttc-fonts \ + postgresql \ + libpq-devel \ + gcc-c++ \ + make \ + openssl-devel \ + libffi-devel \ + bzip2 \ + xz \ + sqlite-devel \ + readline-devel \ + tk-devel \ + gdbm-devel \ + jemalloc-devel && \ + dnf clean all && \ + rm -rf /var/cache/yum/* + +# Install document processing tools separately (optional, may fail on transient mirror issues) +RUN dnf install -y --setopt=install_weak_deps=False --skip-broken \ + ghostscript pandoc texlive || true && \ + dnf clean all && \ + rm -rf /var/cache/yum/* + +# Install nginx and hostname (needed by entrypoint.sh) +RUN dnf install -y --setopt=install_weak_deps=False nginx hostname && \ + dnf clean all && \ + rm -rf /var/cache/yum/* + +# Install uv +RUN arch="$(uname -m)" && \ + if [ "$arch" = "x86_64" ]; then uv_arch="x86_64"; else uv_arch="aarch64"; fi && \ + curl -fsSL "https://github.com/astral-sh/uv/releases/download/0.6.0/uv-${uv_arch}-unknown-linux-gnu.tar.gz" -o /tmp/uv.tar.gz && \ + tar xzf /tmp/uv.tar.gz -C /usr/local/bin/ --strip-components=1 && \ + rm /tmp/uv.tar.gz && \ + uv python install 3.12 + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 +ENV PATH=/root/.local/bin:/usr/local/bin:$PATH + +# Install Node.js 20 directly +RUN arch="$(uname -m)" && \ + if [ "$arch" = "x86_64" ]; then node_arch="x64"; else node_arch="arm64"; fi && \ + curl -fsSL "https://nodejs.org/dist/v20.19.0/node-v20.19.0-linux-${node_arch}.tar.xz" -o /tmp/node.tar.xz && \ + tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 && \ + rm /tmp/node.tar.xz && \ + node -v && npm -v + +# builder stage +FROM base AS builder + +WORKDIR /ragflow + +# Clone RAGFlow v0.24.0 source +RUN git clone --depth 1 --branch v0.24.0 https://github.com/infiniflow/ragflow.git /tmp/ragflow-src + +# Copy source directories +RUN cp -r /tmp/ragflow-src/web /ragflow/web && \ + cp -r /tmp/ragflow-src/api /ragflow/api && \ + cp -r /tmp/ragflow-src/docs /ragflow/docs && \ + cp -r /tmp/ragflow-src/conf /ragflow/conf && \ + cp -r /tmp/ragflow-src/deepdoc /ragflow/deepdoc && \ + cp -r /tmp/ragflow-src/rag /ragflow/rag && \ + cp -r /tmp/ragflow-src/agent /ragflow/agent && \ + cp -r /tmp/ragflow-src/mcp /ragflow/mcp && \ + cp -r /tmp/ragflow-src/admin /ragflow/admin && \ + cp -r /tmp/ragflow-src/common /ragflow/common && \ + cp -r /tmp/ragflow-src/memory /ragflow/memory && \ + cp -r /tmp/ragflow-src/sdk /ragflow/sdk && \ + cp -r /tmp/ragflow-src/tools /ragflow/tools && \ + cp /tmp/ragflow-src/pyproject.toml /ragflow/pyproject.toml && \ + cp /tmp/ragflow-src/uv.lock /ragflow/uv.lock && \ + cp /tmp/ragflow-src/LICENSE /ragflow/LICENSE && \ + cp /tmp/ragflow-src/README.md /ragflow/README.md && \ + cp /tmp/ragflow-src/docker/entrypoint.sh /ragflow/entrypoint.sh && \ + cp /tmp/ragflow-src/docker/service_conf.yaml.template /ragflow/conf/service_conf.yaml.template && \ + mkdir -p /ragflow/docker/nginx && \ + cp /tmp/ragflow-src/docker/nginx/nginx.conf /ragflow/docker/nginx/nginx.conf && \ + cp /tmp/ragflow-src/docker/nginx/ragflow.conf /ragflow/docker/nginx/ragflow.conf && \ + cp /tmp/ragflow-src/docker/nginx/proxy.conf /ragflow/docker/nginx/proxy.conf && \ + rm -rf /tmp/ragflow-src + +# Fix nginx include path: official uses relative 'proxy.conf', we need absolute path for /etc/nginx/conf.d/ +RUN sed -i 's|include proxy.conf;|include /etc/nginx/conf.d/proxy.conf;|g' /ragflow/docker/nginx/ragflow.conf + +# Fix library path mismatch: OC9 uses /usr/lib64, original entrypoint hardcodes /usr/lib/x86_64-linux-gnu/ +RUN ln -s /usr/lib64 /usr/lib/x86_64-linux-gnu + +# Fix pyproject.toml: remove non-existent packages +RUN sed -i "s/'graphrag',//" /ragflow/pyproject.toml && \ + sed -i "s/'intergrations.chatgpt-on-wechat.plugins',//" /ragflow/pyproject.toml + +# Install Python dependencies from uv.lock +# Override mirror index to use official PyPI +RUN sed -i 's|pypi.tuna.tsinghua.edu.cn|pypi.org|g' /ragflow/uv.lock && \ + sed -i 's|mirrors.tuna.tsinghua.edu.cn|pypi.org|g' /ragflow/uv.lock && \ + sed -i 's|tsinghua.edu.cn|pypi.org|g' /ragflow/uv.lock && \ + uv venv --python 3.12 && \ + .venv/bin/python3 -m ensurepip --upgrade && \ + .venv/bin/python3 -m pip install -i https://pypi.org/simple --upgrade pip && \ + .venv/bin/python3 -m pip install -i https://pypi.org/simple hatchling hatch-vcs cython && \ + uv sync --python 3.12 --frozen --no-build-isolation && \ + .venv/bin/python3 -m ensurepip --upgrade + +# Build frontend +RUN cd /ragflow/web && npm install && npm run build + +# Generate VERSION +RUN echo "v0.24.0" > /ragflow/VERSION + +RUN echo $(date +"%Y-%m-%dT%H:%M:%S%z") > /opencloudos_build_date.txt + +# production stage +FROM base AS production + +USER root +WORKDIR /ragflow + +# Copy Python environment and packages +ENV VIRTUAL_ENV=/ragflow/.venv +COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV} +ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" +ENV PYTHONPATH=/ragflow/ + +# Copy source code +COPY --from=builder /ragflow/web /ragflow/web +COPY --from=builder /ragflow/api /ragflow/api +COPY --from=builder /ragflow/docs /ragflow/docs +COPY --from=builder /ragflow/conf /ragflow/conf +COPY --from=builder /ragflow/deepdoc /ragflow/deepdoc +COPY --from=builder /ragflow/rag /ragflow/rag +COPY --from=builder /ragflow/agent /ragflow/agent +COPY --from=builder /ragflow/mcp /ragflow/mcp +COPY --from=builder /ragflow/admin /ragflow/admin +COPY --from=builder /ragflow/common /ragflow/common +COPY --from=builder /ragflow/memory /ragflow/memory +COPY --from=builder /ragflow/sdk /ragflow/sdk +COPY --from=builder /ragflow/tools /ragflow/tools +COPY --from=builder /ragflow/pyproject.toml /ragflow/pyproject.toml +COPY --from=builder /ragflow/uv.lock /ragflow/uv.lock +COPY --from=builder /ragflow/LICENSE /ragflow/LICENSE +COPY --from=builder /ragflow/README.md /ragflow/README.md +COPY --from=builder /ragflow/VERSION /ragflow/VERSION + +# Copy entrypoint and config +COPY --from=builder /ragflow/entrypoint.sh /ragflow/entrypoint.sh +COPY --from=builder /ragflow/conf/service_conf.yaml.template /ragflow/conf/service_conf.yaml.template +RUN chmod +x /ragflow/entrypoint.sh + +# Copy compiled web pages +COPY --from=builder /ragflow/web/dist /ragflow/web/dist + +# Install nginx configuration +RUN rm -f /etc/nginx/nginx.conf && \ + rm -f /etc/nginx/conf.d/default.conf +COPY --from=builder /ragflow/docker/nginx/nginx.conf /etc/nginx/nginx.conf +COPY --from=builder /ragflow/docker/nginx/ragflow.conf /etc/nginx/conf.d/ragflow.conf +COPY --from=builder /ragflow/docker/nginx/proxy.conf /etc/nginx/conf.d/proxy.conf + +# Install additional runtime dependencies +RUN python -m pip install --no-cache-dir ollama redis + +# Download NLTK data +RUN python -m nltk.downloader punkt_tab 2>/dev/null || python -m nltk.downloader punkt && \ + python -m nltk.downloader wordnet + +ENV HF_ENDPOINT=https://hf-mirror.com + +EXPOSE 80 443 9380 + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/frameworks/ragflow/0.24.0/README.md b/frameworks/ragflow/0.24.0/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7abf33256463454fa396b6bc82ef3d6870030f6 --- /dev/null +++ b/frameworks/ragflow/0.24.0/README.md @@ -0,0 +1,68 @@ +# RAGFlow on OpenCloudOS 9 + +## 基本信息 +- **框架版本**:v0.24.0 +- **基础镜像**:opencloudos/opencloudos9-minimal:latest +- **Python 版本**:3.12 +- **CUDA 版本**:N/A(CPU 版本) +- **架构**:amd64 + +## 关于 RAGFlow + +RAGFlow 是一个基于深度文档理解的开源 RAG(Retrieval-Augmented Generation)引擎。它提供了一套完整的流水线,涵盖文档解析、文本分块、向量检索和 LLM 生成等核心能力。 + +## 构建 + +```bash +docker build -t oc9-ragflow:0.24.0 frameworks/ragflow/0.24.0/ +``` + +## 使用示例 + +```bash +# 验证 Python 环境 +docker run --rm --entrypoint python oc9-ragflow:0.24.0 -c "import sys; print(sys.version)" + +# 验证 RAGFlow 模块可导入 +docker run --rm --entrypoint python oc9-ragflow:0.24.0 -c "from rag.utils import redis_conn; print('OK')" +docker run --rm --entrypoint python oc9-ragflow:0.24.0 -c "from deepdoc.parser import DocParser; print('OK')" +``` + +## 依赖服务 + +RAGFlow 运行时需要以下外部服务,请自行部署并配置: + +| 服务 | 用途 | 默认端口 | +|------|------|---------| +| MySQL | 元数据存储 | 3306 | +| Redis | 缓存和消息队列 | 6379 | +| Elasticsearch | 全文检索引擎 | 9200 | +| MinIO | 对象存储 | 9000 | + +> 可通过 `/ragflow/conf/service_conf.yaml` 配置上述服务的连接信息。 + +## 测试验证 + +```bash +# 执行完整端到端测试(自动拉起 MySQL、Redis、ES、MinIO 依赖服务) +bash frameworks/ragflow/0.24.0/test.sh oc9-ragflow:0.24.0 + +# 仅执行基础检查(不启动依赖服务) +bash frameworks/ragflow/0.24.0/test.sh oc9-ragflow:0.24.0 9380 180 true +``` + +测试脚本会验证以下内容: +- Python 3.12 环境可用 +- rag 核心模块可导入 +- deepdoc 模块可导入 +- nginx 已安装 +- 前端构建产物存在 +- **端到端测试(默认启用)**:自动以 Docker 容器方式拉起 MySQL、Redis、Elasticsearch、MinIO,启动 RAGFlow 并验证 `/v1/version` 等 API 接口 + +## 注意事项 + +- RAGFlow 是多服务全栈应用,镜像仅包含主服务(Python 后端 + Node.js 前端 + Nginx),不包含 MySQL、Redis、Elasticsearch、MinIO 等依赖服务。 +- 首次启动前需修改 `/ragflow/conf/service_conf.yaml` 中的服务连接配置。 +- 默认使用 Hugging Face 镜像站(hf-mirror.com)加速模型下载,可通过 `HF_ENDPOINT` 环境变量修改。 +- 构建 CPU 版本时不包含 GPU 加速的文档解析功能。 +- Dockerfile 参考 RAGFlow 官方 Dockerfile.scratch.oc9,适配了 Python 3.12(v0.24.0 pyproject.toml 要求 >=3.12)。 diff --git a/frameworks/ragflow/0.24.0/build.conf b/frameworks/ragflow/0.24.0/build.conf new file mode 100644 index 0000000000000000000000000000000000000000..45bea5d0907e53939f9d1b3fb0b6e29262fa7e48 --- /dev/null +++ b/frameworks/ragflow/0.24.0/build.conf @@ -0,0 +1,4 @@ +# RAGFlow 0.24.0 on OpenCloudOS 9 (CPU) +IMAGE_NAME=oc9-ragflow +IMAGE_TAG=0.24.0 +GPU_TEST=false diff --git a/frameworks/ragflow/0.24.0/docker-compose.test.yml b/frameworks/ragflow/0.24.0/docker-compose.test.yml new file mode 100644 index 0000000000000000000000000000000000000000..4e2426ae193ded53c6140eec693cfa3e17a97ec2 --- /dev/null +++ b/frameworks/ragflow/0.24.0/docker-compose.test.yml @@ -0,0 +1,107 @@ +services: + mysql: + image: mysql:8.0.39 + container_name: ragflow-test-mysql + environment: + MYSQL_ROOT_PASSWORD: infini_rag_flow + MYSQL_DATABASE: rag_flow + command: > + --max_connections=1000 + --character-set-server=utf8mb4 + --collation-server=utf8mb4_unicode_ci + --default-authentication-plugin=mysql_native_password + --init-file /data/application/init.sql + volumes: + - ./docker/init.sql:/data/application/init.sql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-uroot", "-pinfini_rag_flow"] + interval: 10s + timeout: 10s + retries: 120 + networks: + - ragflow-test-net + + redis: + image: valkey/valkey:8 + container_name: ragflow-test-redis + command: ["redis-server", "--requirepass", "infini_rag_flow", "--maxmemory", "128mb", "--maxmemory-policy", "allkeys-lru"] + healthcheck: + test: ["CMD", "redis-cli", "-a", "infini_rag_flow", "ping"] + interval: 10s + timeout: 10s + retries: 120 + networks: + - ragflow-test-net + + es: + image: elasticsearch:8.11.3 + container_name: ragflow-test-es + environment: + - discovery.type=single-node + - xpack.security.enabled=false + - xpack.security.http.ssl.enabled=false + - xpack.security.transport.ssl.enabled=false + - cluster.routing.allocation.disk.watermark.low=1gb + - cluster.routing.allocation.disk.watermark.high=500mb + - cluster.routing.allocation.disk.watermark.flood_stage=200mb + - ES_JAVA_OPTS=-Xms512m -Xmx512m + healthcheck: + test: ["CMD-SHELL", "curl http://localhost:9200"] + interval: 10s + timeout: 10s + retries: 120 + networks: + - ragflow-test-net + + minio: + image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z + container_name: ragflow-test-minio + command: ["server", "--console-address", ":9001", "/data"] + environment: + MINIO_ROOT_USER: rag_flow + MINIO_ROOT_PASSWORD: infini_rag_flow + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 10s + timeout: 10s + retries: 120 + networks: + - ragflow-test-net + + ragflow: + image: "${RAGFLOW_IMAGE:?ERROR: RAGFLOW_IMAGE 环境变量未设置}" + container_name: ragflow-test-svc + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + es: + condition: service_healthy + minio: + condition: service_healthy + ports: + - "${RAGFLOW_HTTP_PORT:-80}:80" + environment: + - MYSQL_HOST=ragflow-test-mysql + - MYSQL_PORT=3306 + - MYSQL_USER=root + - MYSQL_PASSWORD=infini_rag_flow + - MYSQL_DBNAME=rag_flow + - REDIS_HOST=ragflow-test-redis + - REDIS_PASSWORD=infini_rag_flow + - ES_HOST=ragflow-test-es + - ELASTIC_PASSWORD=infini_rag_flow + - MINIO_HOST=ragflow-test-minio + - MINIO_USER=rag_flow + - MINIO_PASSWORD=infini_rag_flow + - RAGFLOW_HOST=0.0.0.0 + - SVR_HTTP_PORT=9380 + - HF_ENDPOINT=https://hf-mirror.com + networks: + - ragflow-test-net + +networks: + ragflow-test-net: + name: ragflow-test-net + driver: bridge diff --git a/frameworks/ragflow/0.24.0/docker/init.sql b/frameworks/ragflow/0.24.0/docker/init.sql new file mode 100644 index 0000000000000000000000000000000000000000..340c0ad0a84c4324fa65f822a34b359a9c68cac7 --- /dev/null +++ b/frameworks/ragflow/0.24.0/docker/init.sql @@ -0,0 +1,2 @@ +CREATE DATABASE IF NOT EXISTS rag_flow; +USE rag_flow; diff --git a/frameworks/ragflow/0.24.0/test.sh b/frameworks/ragflow/0.24.0/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..8f5838b72447bff9e60fdc0c6aa2b8b91c6b1f19 --- /dev/null +++ b/frameworks/ragflow/0.24.0/test.sh @@ -0,0 +1,249 @@ +#!/bin/bash +set -euo pipefail + +IMAGE="${1:?ERROR: 缺少镜像参数。用法: bash test.sh [port] [health_timeout] [skip_e2e]}" +PORT_HTTP="${2:-80}" +HEALTH_TIMEOUT="${3:-300}" +SKIP_E2E="${4:-false}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# 统一退出码:0=全部通过,1=有测试失败 +TEST_RESULT=0 + +# 记录已创建的资源,确保清理时能完整移除 +E2E_RESOURCES_CREATED=false + +# 自动寻找可用端口(若默认端口被占用) +find_free_port() { + local p=$1 + while true; do + if ! (netstat -tln 2>/dev/null | awk '{print $4}' | grep -q ":${p}$") && \ + ! (ss -tln 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"); then + echo "$p" + return + fi + p=$((p + 1)) + done +} + +# 统一清理函数:无论成功/失败/中断,都清理所有测试资源 +cleanup() { + local saved_exit=$? + echo "" + echo "=== 清理测试资源 ===" + + # 如果端到端资源曾被创建(或可能被创建),一律清理 + if [[ "$E2E_RESOURCES_CREATED" == "true" ]]; then + if [[ -f "$SCRIPT_DIR/docker-compose.test.yml" ]]; then + RAGFLOW_IMAGE="$IMAGE" RAGFLOW_HTTP_PORT="$PORT_HTTP" \ + docker compose -f "$SCRIPT_DIR/docker-compose.test.yml" \ + down -v --remove-orphans >/dev/null 2>&1 || true + fi + fi + + echo "=== 清理完成 ===" + + if [[ $TEST_RESULT -ne 0 ]]; then + echo "" + echo "=== 测试失败 ===" + exit $TEST_RESULT + fi + + if [[ $saved_exit -ne 0 ]]; then + exit $saved_exit + fi + + exit 0 +} + +trap cleanup EXIT + +# 确保端口可用 +ORIG_PORT_HTTP=$PORT_HTTP +PORT_HTTP=$(find_free_port "$PORT_HTTP") +if [[ "$PORT_HTTP" != "$ORIG_PORT_HTTP" ]]; then + echo "HTTP 端口 ${ORIG_PORT_HTTP} 已被占用,自动切换到 ${PORT_HTTP}" +fi + +echo "=== RAGFlow 功能测试 ===" + +# --------------------------------------------------------------------------- +# 辅助函数:执行单项测试,失败时标记 TEST_RESULT=1 +# --------------------------------------------------------------------------- +run_test() { + local name="$1" + shift + echo -n "${name}... " + if "$@" >/dev/null 2>&1; then + echo "✓ 通过" + return 0 + else + echo "✗ 失败" + TEST_RESULT=1 + return 1 + fi +} + +# --------------------------------------------------------------------------- +# 1-5. 基础检查 +# --------------------------------------------------------------------------- + +# 1. 验证 Python 环境可用 +echo -n "[1/9] 检查 Python 3.12... " +if docker run --rm --entrypoint python "$IMAGE" -c " +import sys +assert sys.version_info >= (3, 12), f'Python version too low: {sys.version}' +print(f'Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}') +" 2>/dev/null; then + echo "✓ 通过" +else + echo "✗ 失败" + TEST_RESULT=1 +fi + +# 2. 验证 RAGFlow 核心模块可导入 +echo -n "[2/9] 检查 rag 模块导入... " +if docker run --rm --entrypoint python "$IMAGE" -c " +from rag.utils import redis_conn +print('rag.utils.redis_conn 可用') +" 2>/dev/null; then + echo "✓ 通过" +else + echo "✗ 失败" + TEST_RESULT=1 +fi + +# 3. 验证 deepdoc 模块可导入 +echo -n "[3/9] 检查 deepdoc 模块导入... " +if docker run --rm --entrypoint python "$IMAGE" -c " +from deepdoc.parser import DocxParser +print('deepdoc.parser.DocxParser 可用') +" 2>/dev/null; then + echo "✓ 通过" +else + echo "✗ 失败" + TEST_RESULT=1 +fi + +# 4. 验证 nginx 已安装 +echo -n "[4/9] 检查 nginx... " +if docker run --rm --entrypoint nginx "$IMAGE" -v 2>/dev/null; then + echo "✓ 通过" +else + echo "✗ 失败" + TEST_RESULT=1 +fi + +# 5. 验证前端构建产物存在 +echo -n "[5/9] 检查前端构建产物... " +if docker run --rm --entrypoint bash "$IMAGE" -c " +test -d /ragflow/web/dist || test -d /ragflow/web/build +" 2>/dev/null; then + echo "✓ 通过" +else + echo "✗ 失败" + TEST_RESULT=1 +fi + +# 基础检查失败则直接退出(端到端测试依赖镜像功能完整) +if [[ $TEST_RESULT -ne 0 ]]; then + echo "" + echo "=== 基础检查未通过,跳过端到端测试 ===" + exit $TEST_RESULT +fi + +# --------------------------------------------------------------------------- +# 6-9. 端到端测试(依赖服务 + API 验证) +# --------------------------------------------------------------------------- +if [[ "$SKIP_E2E" == "true" ]]; then + echo "" + echo "跳过端到端测试(SKIP_E2E=true)" + echo "=== 所有基础测试通过 ===" + exit 0 +fi + +echo "" +echo "=== 端到端测试:启动依赖服务 ===" + +RAGFLOW_C="ragflow-test-svc" + +# 标记端到端资源开始创建 +E2E_RESOURCES_CREATED=true + +# 启动所有服务(依赖 + RAGFlow) +echo -n "[6/9] 启动所有服务(MySQL / Redis / ES / MinIO / RAGFlow)... " +if RAGFLOW_IMAGE="$IMAGE" RAGFLOW_HTTP_PORT="$PORT_HTTP" \ + docker compose -f "$SCRIPT_DIR/docker-compose.test.yml" up -d >/dev/null 2>&1; then + echo "✓" +else + echo "✗ docker compose 启动失败" + TEST_RESULT=1 + exit $TEST_RESULT +fi + +# 等待 RAGFlow API 就绪(以 /v1/system/ping 返回 200 为准,该端点无需认证) +echo -n " 等待 RAGFlow 就绪 (最多 ${HEALTH_TIMEOUT}s)" +SERVICE_STARTED=false +for i in $(seq 1 $HEALTH_TIMEOUT); do + sleep 1 + echo -n "." + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT_HTTP}/v1/system/ping" 2>/dev/null || echo "000") + if [[ "$HTTP_CODE" == "200" ]]; then + echo " ✓" + SERVICE_STARTED=true + break + fi + if [[ $i -eq $HEALTH_TIMEOUT ]]; then + echo "" + fi +done + +if [[ "$SERVICE_STARTED" != "true" ]]; then + echo "✗ RAGFlow 启动超时,无法通过 /v1/system/ping 检测到服务就绪" + echo "" + echo "RAGFlow 容器日志(最后 30 行):" + docker logs "$RAGFLOW_C" 2>&1 | tail -30 || true + TEST_RESULT=1 + exit $TEST_RESULT +fi + +# 7. 版本接口(/v1/system/version 需要认证,此处检查路由存在即可) +echo -n "[7/9] 检查 API 版本接口... " +VERSION_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT_HTTP}/v1/system/version" 2>/dev/null || echo "000") +if [[ "$VERSION_CODE" == "401" ]] || [[ "$VERSION_CODE" == "200" ]]; then + echo "✓ 版本接口正常 (HTTP ${VERSION_CODE})" +else + echo "✗ 版本接口返回非预期状态码: ${VERSION_CODE}" + TEST_RESULT=1 +fi + +# 8. 额外 API 验证 +echo -n "[8/9] 检查 API 响应能力... " +HEALTH_RESP=$(curl -s "http://localhost:${PORT_HTTP}/v1/system/healthz" 2>/dev/null || echo "") +if echo "$HEALTH_RESP" | grep -qE '"retcode"|"code"|"success"|"status"'; then + echo "✓ API 返回正确 JSON 格式: $HEALTH_RESP" +else + echo "✗ API 响应格式异常: $HEALTH_RESP" + TEST_RESULT=1 +fi + +# 9. 验证 80 端口前端页面 +echo -n "[9/9] 检查 80 端口前端页面... " +HTTP_CODE_80=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT_HTTP}" 2>/dev/null || echo "000") +if [[ "$HTTP_CODE_80" == "200" ]]; then + echo "✓ 80 端口正常 (HTTP ${HTTP_CODE_80})" +else + echo "✗ 80 端口返回非预期状态码: ${HTTP_CODE_80}" + TEST_RESULT=1 +fi + +# 最终判定 +echo "" +if [[ $TEST_RESULT -eq 0 ]]; then + echo "=== 所有测试通过 ===" +else + echo "=== 测试失败 ===" +fi + +exit $TEST_RESULT