WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
# Temporary offline build stage: reuse the migration binary already present in
# the last production image when the Go proxy is unavailable.
FROM yawan-api:public-20260921-053200 AS migrate-prebuilt
# Build stage
FROM golang:1.26-bookworm AS builder
WORKDIR /app
# 通过构建参数接收敏感信息
ARG GOPRIVATE_ARG
ARG GOPROXY_ARG
ARG GOSUMDB_ARG=off
ARG APK_MIRROR_ARG
# 设置Go环境变量
ENV GOPRIVATE=${GOPRIVATE_ARG}
ENV GOPROXY=${GOPROXY_ARG}
ENV GOSUMDB=${GOSUMDB_ARG}
# Install dependencies
RUN if [ -n "$APK_MIRROR_ARG" ]; then \
sed -i "s@deb.debian.org@${APK_MIRROR_ARG}@g" /etc/apt/sources.list.d/debian.sources; \
fi && \
apt-get update && \
apt-get install -y git build-essential libsqlite3-dev curl
# Install migrate tool. The prebuilt binary keeps release builds working when
# the public Go proxy is temporarily unreachable.
COPY --from=migrate-prebuilt /usr/local/bin/migrate /go/bin/migrate
# Copy go mod files. go.mod replace-points anydoc at ./third_party/anydoc-go,
# so that module's go.mod must exist before `go mod download`.
COPY go.mod go.sum ./
COPY third_party/anydoc-go/go.mod third_party/anydoc-go/go.mod
RUN go mod download
COPY cmd/download cmd/download
RUN go run cmd/download/duckdb/duckdb.go
COPY . .
# Get version and commit info for build injection
ARG VERSION_ARG
ARG COMMIT_ID_ARG
ARG BUILD_TIME_ARG
ARG GO_VERSION_ARG
# Set build-time variables
ENV VERSION=${VERSION_ARG}
ENV COMMIT_ID=${COMMIT_ID_ARG}
ENV BUILD_TIME=${BUILD_TIME_ARG}
ENV GO_VERSION=${GO_VERSION_ARG}
# Link the anydoc parser engine (office docs converted in-process, no
# Python docreader). Default on so Hub / compose images ship a working
# engine; pass WITH_ANYDOC=0 to skip the Rust toolchain (~few minutes and
# ~1 GB of build-stage layers).
ARG WITH_ANYDOC=1
ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo
ENV PATH=/usr/local/cargo/bin:$PATH
RUN if [ "$WITH_ANYDOC" = "1" ]; then \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --profile minimal --default-toolchain stable && \
./scripts/build-anydoc-lib.sh; \
fi
# Build the application with version info
RUN if [ "$WITH_ANYDOC" = "1" ]; then \
make build-prod GO_BUILD_TAGS=anydoc; \
else \
make build-prod; \
fi
RUN cp -r /go/pkg/mod/github.com/yanyiwu/ /app/yanyiwu/
# Final stage
FROM debian:12.12-slim
WORKDIR /app
ARG APK_MIRROR_ARG
# Create a non-root user first
RUN useradd -m -s /bin/bash appuser
# First, install ca-certificates without mirror to ensure HTTPS works
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Then switch to mirror if specified and install other packages
RUN if [ -n "$APK_MIRROR_ARG" ]; then \
sed -i "s@deb.debian.org@${APK_MIRROR_ARG}@g" /etc/apt/sources.list.d/debian.sources; \
fi && \
apt-get update && \
apt-get install -y --no-install-recommends \
build-essential postgresql-client default-mysql-client tzdata sed curl bash vim wget \
libsqlite3-0 \
python3 python3-pip python3-dev libffi-dev libssl-dev \
nodejs npm \
gosu \
ffmpeg && \
python3 -m pip install --break-system-packages --upgrade pip setuptools wheel && \
mkdir -p /home/appuser/.local/bin && \
curl -LsSf https://astral.sh/uv/install.sh | CARGO_HOME=/home/appuser/.cargo UV_INSTALL_DIR=/home/appuser/.local/bin sh && \
chown -R appuser:appuser /home/appuser && \
ln -sf /home/appuser/.local/bin/uvx /usr/local/bin/uvx && \
chmod +x /usr/local/bin/uvx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Create data directories and set permissions
RUN mkdir -p /data/files && \
chown -R appuser:appuser /app /data/files
# Copy migrate tool from builder stage
COPY --from=builder /go/bin/migrate /usr/local/bin/
COPY --from=builder /app/yanyiwu/ /go/pkg/mod/github.com/yanyiwu/
# Copy the binary from the builder stage
COPY --from=builder /app/config ./config
COPY --from=builder /app/scripts ./scripts
COPY --from=builder /app/migrations ./migrations
COPY --from=builder /app/dataset/samples ./dataset/samples
COPY --from=builder /app/skills/preloaded ./skills/preloaded
# Keep a read-only backup so bind-mount cannot erase built-in skills
COPY --from=builder /app/skills/preloaded ./skills/_builtin
COPY --from=builder /root/.duckdb /home/appuser/.duckdb
COPY --from=builder /app/WeKnora .
# Copy and make entrypoint script executable
COPY --from=builder /app/scripts/docker-entrypoint.sh ./scripts/docker-entrypoint.sh
# Make scripts executable
RUN chmod +x ./scripts/*.sh
# Expose ports
EXPOSE 8080
ENTRYPOINT ["./scripts/docker-entrypoint.sh"]
CMD ["./WeKnora"]
+159
View File
@@ -0,0 +1,159 @@
# =========================
# 构建阶段(轻量化:仅文档解析 + 图片提取,无 OCR/VLM)
# =========================
FROM python:3.10.18-bookworm AS builder
ARG APT_MIRROR=""
RUN if [ -n "$APT_MIRROR" ]; then \
sed -i "s@http://deb.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources && \
sed -i "s@http://security.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources; \
fi
WORKDIR /app
# 安装构建依赖
RUN apt-get update && apt-get install -y \
gcc \
python3-dev \
libjpeg-dev \
zlib1g-dev \
libffi-dev \
libgl1 \
libglib2.0-0 \
wget \
antiword \
curl \
unzip \
&& rm -rf /var/lib/apt/lists/*
# 检查是否存在本地protoc安装包,如果存在则离线安装,否则在线安装
ARG TARGETARCH
COPY packages/ /app/packages/
RUN ls -lah /app/packages/
# 兼容 BuildKit 和传统构建:如果 TARGETARCH 为空,使用 uname 检测
RUN if [ -z "$TARGETARCH" ]; then \
case $(uname -m) in \
"x86_64") TARGETARCH="amd64" ;; \
"aarch64") TARGETARCH="arm64" ;; \
"armv7l") TARGETARCH="arm" ;; \
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
esac; \
echo "[自动检测] 架构: $TARGETARCH"; \
else \
echo "[BuildKit] 架构: $TARGETARCH"; \
fi && \
case $TARGETARCH in \
"amd64") PROTOC_ARCH="x86_64" ;; \
"arm64") PROTOC_ARCH="aarch_64" ;; \
"arm") PROTOC_ARCH="arm" ;; \
*) echo "Unsupported architecture for protoc: $TARGETARCH" && exit 1 ;; \
esac && \
PROTOC_PACKAGE="protoc-3.19.4-linux-${PROTOC_ARCH}.zip" && \
if [ -f "/app/packages/${PROTOC_PACKAGE}" ]; then \
echo "发现本地protoc安装包,将进行离线安装"; \
cp /app/packages/${PROTOC_PACKAGE} /app/ && \
unzip -o /app/${PROTOC_PACKAGE} -d /usr/local && \
chmod +x /usr/local/bin/protoc && \
rm -f /app/${PROTOC_PACKAGE}; \
else \
echo "未发现本地protoc安装包,将进行在线安装"; \
curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/${PROTOC_PACKAGE} && \
unzip -o ${PROTOC_PACKAGE} -d /usr/local && \
chmod +x /usr/local/bin/protoc && \
rm -f ${PROTOC_PACKAGE}; \
fi
# 复制依赖文件
COPY docreader/pyproject.toml docreader/uv.lock ./
RUN pip install uv --break-system-packages && \
python -m uv sync --locked --no-dev
# 复制源代码和生成脚本
COPY docreader docreader
# 生成 protobuf 代码(使用 venv 中的 grpc_tools)
ENV PATH="/app/.venv/bin:${PATH}"
RUN chmod +x docreader/scripts/generate_proto.sh && \
bash docreader/scripts/generate_proto.sh
# =========================
# 运行阶段(轻量化)
# =========================
FROM python:3.10.18-bookworm AS runner
ARG APT_MIRROR=""
RUN if [ -n "$APT_MIRROR" ]; then \
sed -i "s@http://deb.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources && \
sed -i "s@http://security.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list.d/debian.sources; \
fi
WORKDIR /app
# 安装运行时依赖(已移除 OCR/PaddleOCR 相关依赖)
RUN apt-get update && apt-get install -y \
libjpeg62-turbo \
wget \
gnupg \
openjdk-17-jre-headless \
libgl1 \
libglib2.0-0 \
antiword \
tar \
dpkg \
libxinerama1 \
libfontconfig1 \
libdbus-glib-1-2 \
libcairo2 \
libcups2 \
libglu1-mesa \
libsm6 \
libreoffice \
curl \
&& rm -rf /var/lib/apt/lists/*
# 安装 grpc_health_probe
ARG TARGETARCH
# 兼容 BuildKit 和传统构建:如果 TARGETARCH 为空,使用 uname 检测
RUN if [ -z "$TARGETARCH" ]; then \
case $(uname -m) in \
"x86_64") TARGETARCH="amd64" ;; \
"aarch64") TARGETARCH="arm64" ;; \
"armv7l") TARGETARCH="arm" ;; \
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
esac; \
echo "[自动检测] 架构: $TARGETARCH"; \
else \
echo "[BuildKit] 架构: $TARGETARCH"; \
fi && \
GRPC_HEALTH_PROBE_VERSION=v0.4.24 && \
case $TARGETARCH in \
"amd64") ARCH="amd64" ;; \
"arm64") ARCH="arm64" ;; \
"arm") ARCH="arm" ;; \
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
esac && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-${ARCH} && \
chmod +x /bin/grpc_health_probe
# 从构建阶段复制已安装的依赖和生成的代码
ENV VIRTUAL_ENV=/app/.venv
COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
COPY --from=builder /usr/local/bin /usr/local/bin
# 安装 Playwright 浏览器(网页解析)
RUN python -m playwright install webkit
RUN python -m playwright install-deps webkit
COPY docreader/pyproject.toml docreader/uv.lock ./
COPY --from=builder /app/docreader docreader
# 创建共享临时图片目录
RUN mkdir -p /tmp/docreader
# 暴露 gRPC 端口
EXPOSE 50051
# 直接运行 Python 服务(日志输出到 stdout/stderr)
CMD ["uv", "run", "-m", "docreader.main"]
+29
View File
@@ -0,0 +1,29 @@
# OpenDataLoader PDF hybrid backend (Docling). Pre-install deps so the
# container listens on :5002 immediately instead of pip install on every start.
#
# Default --no-ocr: digital PDFs already have a text layer; Docling layout/table
# still runs without EasyOCR (avoids libGL + heavy OCR stack in slim images).
# For scanned PDFs use builtin docreader OCR, MinerU, or override with
# ODL_HYBRID_EXTRA_ARGS="--force-ocr" (requires extra system/Python deps).
FROM python:3.10.18-bookworm
# Docling table/layout models import cv2 (OpenCV), which needs libGL at runtime
# even when hybrid runs with --no-ocr.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
libgl1 \
libglib2.0-0 \
libgomp1 \
libsm6 \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir "opendataloader-pdf[hybrid]>=2.4.7"
EXPOSE 5002
ENV ODL_HYBRID_EXTRA_ARGS="--no-ocr"
HEALTHCHECK --interval=30s --timeout=10s --retries=5 --start-period=120s \
CMD curl -f http://localhost:5002/health || exit 1
CMD ["bash", "-c", "exec opendataloader-pdf-hybrid --host 0.0.0.0 --port 5002 ${ODL_HYBRID_EXTRA_ARGS}"]
+100
View File
@@ -0,0 +1,100 @@
# WeKnora Sandbox Image
# Pre-built environment for executing agent skill scripts in Docker sandbox
# Multi-stage build, minimal dependencies
#
# Two targets ship from this file:
# runtime (default) - the plain environment. Used by the Docker backend and
# as the base image of E2B templates, whose builder
# injects its own envd.
# cube - the same environment plus Cube's envd daemon. Cube
# builds templates straight from the image and probes
# :49983/health, so an image without envd can only ever
# fail. See docs/sandbox-cluster.md.
# Stage 1: Get Node.js binaries
FROM node:20-slim AS node-base
# Stage 2: Runtime image
FROM python:3.11-slim AS runtime
# Copy Node.js from node image (avoids NodeSource install overhead)
COPY --from=node-base /usr/local/bin/node /usr/local/bin/
COPY --from=node-base /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \
ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
# Python package manager: uv (fast pip replacement). Preinstalled because each
# skill gets its own venv at install time, and building that from a cold pip is
# what dominates an install's wall clock.
COPY --from=ghcr.io/astral-sh/uv:0.12.4 /uv /usr/local/bin/uv
RUN ln -s /usr/local/bin/uv /usr/local/bin/uvx
# Node package manager: pnpm
RUN npm install -g pnpm
# Install minimal CLI tools (bash/grep/sed/coreutils/findutils already in slim
# image). `file` is not in slim; models reach for it after a script-type error.
# curl is not optional: the sandbox connectivity check probes egress with it,
# and skills that fetch a URL expect it to be there. The compression tools are
# here because skill bundles and their assets arrive archived.
RUN apt-get update && apt-get install -y --no-install-recommends \
jq \
curl \
ca-certificates \
file \
zip \
unzip \
bzip2 \
xz-utils \
zstd \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# Note: Current preloaded skills only use Python stdlib
# Add packages here when skills actually need them:
# Create non-root user (UID 1000) for sandbox execution. The account is named
# "user" because that is the account name E2B templates are expected to expose
# and the one WeKnora names when running scripts and file operations against an
# E2B-protocol backend; the Docker backend addresses it numerically as
# 1000:1000, so both paths land on the same account.
RUN groupadd -g 1000 user && \
useradd -u 1000 -g user -m -s /bin/bash user
# Pre-create the two directories every backend writes to and hand them to the
# sandbox account. WeKnora otherwise has to create and chown them as root on
# each execution, which needs capabilities a hardened container should not
# have to keep.
RUN mkdir -p /workspace/input /workspace/output && \
chown -R user:user /workspace
WORKDIR /workspace
USER user
# Stage 3: Cube template image
#
# envd is what Cube talks to for everything — the readiness probe that decides
# whether a template build succeeds, plus every later exec and file call. It
# runs as root and drops to the account named in each request, which is why
# this variant does not keep the runtime's USER user.
#
# This target is linux/amd64 only: cubesandbox-base publishes no arm64 image,
# which matches Cube itself (PVM is x86_64-only).
FROM ghcr.io/tencentcloud/cubesandbox-base:2026.16 AS cube-base
FROM runtime AS cube
USER root
COPY --from=cube-base /usr/bin/envd /usr/bin/envd
COPY --from=cube-base /usr/local/bin/cube-entrypoint.sh /usr/local/bin/cube-entrypoint.sh
EXPOSE 49983
# The entrypoint backgrounds envd and then waits on it, since this image
# defines no CMD of its own.
ENTRYPOINT ["/usr/local/bin/cube-entrypoint.sh"]
# Stage 4: default target
#
# Docker builds the last stage when none is named, and that must stay the plain
# runtime: a bare `docker build -f docker/Dockerfile.sandbox .` is expected to
# produce the image the Docker backend runs, not the Cube variant.
FROM runtime AS sandbox
+31
View File
@@ -0,0 +1,31 @@
[supervisord]
nodaemon=true
logfile=/var/log/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
user=root
[program:WeKnora]
command=/app/WeKnora
directory=/app
autostart=true
autorestart=true
startretries=5
redirect_stderr=true
stdout_logfile=/var/log/WeKnora.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
environment=CGO_ENABLED=1
user=appuser
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
+49
View File
@@ -0,0 +1,49 @@
use_default_settings: true
general:
instance_name: "WeKnora SearXNG"
privacypolicy_url: false
donation_url: false
contact_url: false
enable_metrics: false
server:
# Override via env SEARXNG_SECRET; the entrypoint script substitutes ultrasecretkey.
secret_key: "ultrasecretkey"
base_url: false
image_proxy: true
http_protocol_version: "1.0"
method: "GET"
# Disabled so WeKnora's backend can call /search without IP-based throttling.
# Re-enable and add WeKnora's egress IP to a limiter allowlist if you expose
# this instance to the public internet.
limiter: false
public_instance: false
ui:
static_use_hash: true
default_locale: ""
query_in_title: false
infinite_scroll: false
center_alignment: false
theme_args:
simple_style: auto
search_on_category_select: true
hotkeys: default
search:
safe_search: 0
autocomplete: ""
default_lang: "auto"
ban_time_on_fail: 5
max_ban_time_on_fail: 120
formats:
- html
- json
outgoing:
request_timeout: 6.0
max_request_timeout: 10.0
pool_connections: 100
pool_maxsize: 20
enable_http2: true