From 932ed022ab3858dcde36d379638de00940edb628 Mon Sep 17 00:00:00 2001 From: skift Date: Thu, 18 Dec 2025 18:30:00 +0800 Subject: [PATCH 01/11] bugfix: restore some bugs on notes --- frontend/package-lock.json | 12 +----------- frontend/src/app/notes/new/page.tsx | 18 +++++++++++++++++- frontend/src/app/notes/page.tsx | 18 +++++++++++++++++- frontend/src/app/smart-reading/page.tsx | 6 ++---- .../src/components/ai-agent/AgentChatPanel.tsx | 18 +++++++++++++++++- .../components/note-editor/NoteEditorPanel.tsx | 12 ++++++++++-- 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 16bf0c4..fd5c990 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1485,7 +1485,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1552,7 +1551,6 @@ "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", @@ -2179,7 +2177,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3309,7 +3306,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3484,7 +3480,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7356,7 +7351,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7366,7 +7360,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -8546,8 +8539,7 @@ "version": "4.1.14", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -8621,7 +8613,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8798,7 +8789,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/frontend/src/app/notes/new/page.tsx b/frontend/src/app/notes/new/page.tsx index a3e64d5..c3f74f7 100644 --- a/frontend/src/app/notes/new/page.tsx +++ b/frontend/src/app/notes/new/page.tsx @@ -17,7 +17,8 @@ import { BACKEND_URL } from "@/lib/config"; export default function NewNotePage() { const router = useRouter(); - const token = getAccessToken(); + const [token, setToken] = useState(null); + const [hydrated, setHydrated] = useState(false); const [papers, setPapers] = useState([]); const [title, setTitle] = useState(""); const [content, setContent] = useState(""); @@ -30,6 +31,12 @@ export default function NewNotePage() { [], ); + // Defer token read to client to avoid SSR/client mismatch + useEffect(() => { + setToken(getAccessToken()); + setHydrated(true); + }, []); + useEffect(() => { if (!token) return; fetchUploadedPapers(token, { page: 1, pageSize: 50 }) @@ -61,6 +68,15 @@ export default function NewNotePage() { } } + // Prevent hydration mismatches: wait until client token is known + if (!hydrated) { + return ( + +
加载中…
+
+ ); + } + if (!token) { return ( diff --git a/frontend/src/app/notes/page.tsx b/frontend/src/app/notes/page.tsx index cf7236d..c138292 100644 --- a/frontend/src/app/notes/page.tsx +++ b/frontend/src/app/notes/page.tsx @@ -40,7 +40,8 @@ function truncate(text?: string | null, length = 140): string { export default function NotesPage() { const router = useRouter(); - const token = getAccessToken(); + const [token, setToken] = useState(null); + const [hydrated, setHydrated] = useState(false); const [notes, setNotes] = useState([]); const [papers, setPapers] = useState([]); const [loading, setLoading] = useState(true); @@ -56,6 +57,12 @@ export default function NotesPage() { [], ); + // Defer token read to client to avoid SSR/CSR mismatch + useEffect(() => { + setToken(getAccessToken()); + setHydrated(true); + }, []); + const paperLookup = useMemo(() => { return papers.reduce>((acc, item) => { acc[item.id] = item; @@ -168,6 +175,15 @@ export default function NotesPage() { const searchInputRef = useRef(null); + // Prevent hydration mismatch by waiting for client token + if (!hydrated) { + return ( + +
加载中…
+
+ ); + } + if (!token) { return ( diff --git a/frontend/src/app/smart-reading/page.tsx b/frontend/src/app/smart-reading/page.tsx index d5ec037..48fbf0a 100644 --- a/frontend/src/app/smart-reading/page.tsx +++ b/frontend/src/app/smart-reading/page.tsx @@ -66,7 +66,7 @@ export default function SmartReadingPage() { }, []); const agentChatPanelRef = useRef<{ setInputText: (text: string) => void } | null>(null); - const noteEditorPanelRef = useRef<{ appendContent: (text: string) => void } | null>(null); + const noteEditorPanelRef = useRef<{ appendContent: (text: string) => void; resetEditor: () => void } | null>(null); const splitContainerRef = useRef(null); const handleExtractText = useCallback((text: string) => { @@ -281,11 +281,9 @@ export default function SmartReadingPage() { setSelectedPdfUrl(null); setUploadedPaper(null); setUploadError(null); - setParseProgress(null); - setParseError(null); - setParsedDocument(null); setMineruResult(null); setIsParsing(false); + noteEditorPanelRef.current?.resetEditor?.(); }} className="flex items-center gap-2 rounded-lg border border-red-200 bg-red-50/80 px-3 py-2 font-medium text-red-700 transition hover:border-red-300 hover:bg-red-100 dark:border-red-800/60 dark:bg-red-900/30 dark:text-red-300 dark:hover:border-red-700 dark:hover:bg-red-900/50" > diff --git a/frontend/src/components/ai-agent/AgentChatPanel.tsx b/frontend/src/components/ai-agent/AgentChatPanel.tsx index 7c3ac78..7b6ed15 100644 --- a/frontend/src/components/ai-agent/AgentChatPanel.tsx +++ b/frontend/src/components/ai-agent/AgentChatPanel.tsx @@ -120,7 +120,23 @@ const AgentChatPanel = forwardRef< } } catch (error) { console.error("加载对话历史失败:", error); - // 如果加载失败,不影响新对话 + // 后端会在类别不匹配或不存在时返回 404;清理本地缓存以便重新创建对话 + const message = error instanceof Error ? error.message : ""; + if (message.includes("不存在") || message.toLowerCase().includes("not found")) { + if (paperId) { + localStorage.removeItem(`conversation_${paperId}`); + } + setConversationId(null); + setMessages([ + { + id: createId(), + role: "assistant", + content: "对话不存在,已为当前文档重置。可以继续提问,我会新建对话并保存历史。", + timestamp: new Date(), + }, + ]); + } + // 其他错误不影响新对话 } finally { setIsLoadingHistory(false); } diff --git a/frontend/src/components/note-editor/NoteEditorPanel.tsx b/frontend/src/components/note-editor/NoteEditorPanel.tsx index af02a7c..3ef83d6 100644 --- a/frontend/src/components/note-editor/NoteEditorPanel.tsx +++ b/frontend/src/components/note-editor/NoteEditorPanel.tsx @@ -25,7 +25,7 @@ interface NoteEditorPanelProps { } const NoteEditorPanel = forwardRef< - { appendContent: (text: string) => void }, + { appendContent: (text: string) => void; resetEditor: () => void }, NoteEditorPanelProps >(({ paperId, mineruResult }, ref) => { const [title, setTitle] = useState(""); @@ -41,7 +41,7 @@ const NoteEditorPanel = forwardRef< const autoSaveTimerRef = useRef | null>(null); const [isDarkMode, setIsDarkMode] = useState(false); - // 暴露appendContent方法给父组件 + // 暴露 appendContent / resetEditor 方法给父组件 useImperativeHandle(ref, () => ({ appendContent: (text: string) => { setContent((prev) => { @@ -49,6 +49,14 @@ const NoteEditorPanel = forwardRef< return prev + separator + text; }); }, + resetEditor: () => { + setTitle(""); + setContent(""); + setNoteId(null); + setLastSaved(null); + setHasAutoFilledTitle(false); + setRelatedNotes([]); + }, })); // 重新加载笔记列表 From aba5143b1ca5806ca47add4979eb51f13d5748cc Mon Sep 17 00:00:00 2001 From: skift Date: Thu, 18 Dec 2025 18:53:32 +0800 Subject: [PATCH 02/11] bugfix: auto summary only appear for the first time --- frontend/src/components/ai-agent/AgentChatPanel.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/components/ai-agent/AgentChatPanel.tsx b/frontend/src/components/ai-agent/AgentChatPanel.tsx index 7b6ed15..f198e04 100644 --- a/frontend/src/components/ai-agent/AgentChatPanel.tsx +++ b/frontend/src/components/ai-agent/AgentChatPanel.tsx @@ -69,6 +69,7 @@ const AgentChatPanel = forwardRef< const [showHeader, setShowHeader] = useState(true); const streamControllerRef = useRef(null); const autoSummaryHistoryRef = useRef>({}); + const savedConversationRef = useRef(false); // 暴露 setInputText 方法给父组件 useImperativeHandle(ref, () => ({ @@ -117,6 +118,7 @@ const AgentChatPanel = forwardRef< timestamp: new Date(msg.created_at), })); setMessages(historyMessages); + savedConversationRef.current = true; } } catch (error) { console.error("加载对话历史失败:", error); @@ -135,6 +137,7 @@ const AgentChatPanel = forwardRef< timestamp: new Date(), }, ]); + savedConversationRef.current = false; } // 其他错误不影响新对话 } finally { @@ -150,6 +153,7 @@ const AgentChatPanel = forwardRef< const convId = parseInt(savedConvId, 10); setConversationId(convId); loadConversationHistory(convId); + savedConversationRef.current = true; } else { // 没有保存的对话,显示欢迎消息 setMessages([ @@ -162,6 +166,7 @@ const AgentChatPanel = forwardRef< ]); setConversationId(null); setQuotedText(null); + savedConversationRef.current = false; } } else if (!mineruResult) { // 重置为初始状态 @@ -175,6 +180,7 @@ const AgentChatPanel = forwardRef< ]); setConversationId(null); setQuotedText(null); + savedConversationRef.current = false; } }, [paperId, mineruResult, loadConversationHistory]); @@ -302,6 +308,9 @@ const AgentChatPanel = forwardRef< if (!paperId || !mineruResult) { return; } + if (conversationId || savedConversationRef.current) { + return; + } const summaryIntro: AgentMessage = { id: createId(), From fae5fde20b03b6c7c65fe12f012a1d39310fa831 Mon Sep 17 00:00:00 2001 From: zhiyuan_2003 <2304022380@qq.com> Date: Sun, 21 Dec 2025 03:14:44 +0000 Subject: [PATCH 03/11] clean: clean needless files --- docker-compose.yml | 2 +- frontend/src/app/library/page.tsx | 4 +- frontend/src/app/notes/[id]/page.tsx | 1 + frontend/src/app/upload/page.tsx | 5 +- redis.conf | 2 +- scripts/deploy.sh | 132 +++++++++++++++++++++++++++ scripts/dev1.sh | 102 --------------------- scripts/docker-infra.sh | 11 --- 8 files changed, 140 insertions(+), 119 deletions(-) create mode 100755 scripts/deploy.sh delete mode 100644 scripts/dev1.sh delete mode 100644 scripts/docker-infra.sh diff --git a/docker-compose.yml b/docker-compose.yml index 61ad785..1e7e8ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,7 +49,7 @@ services: profiles: ["infra", "frontend"] environment: POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_PASSWORD: shizhiyuan_hanxujie_liushiao POSTGRES_DB: papers ports: - "5432:5432" diff --git a/frontend/src/app/library/page.tsx b/frontend/src/app/library/page.tsx index c5a21c5..161ab8c 100644 --- a/frontend/src/app/library/page.tsx +++ b/frontend/src/app/library/page.tsx @@ -512,12 +512,12 @@ export default function LibraryPage() {

你当前未登录,无法查看个人上传的论文。

- 前往登录 - +
); diff --git a/frontend/src/app/notes/[id]/page.tsx b/frontend/src/app/notes/[id]/page.tsx index 7066b77..416ed2a 100644 --- a/frontend/src/app/notes/[id]/page.tsx +++ b/frontend/src/app/notes/[id]/page.tsx @@ -63,6 +63,7 @@ export default function NoteDetailPage() { } async function bootstrap() { + if (!token) return; try { const [noteRes, paperRes] = await Promise.all([ fetchNote(token, noteId), diff --git a/frontend/src/app/upload/page.tsx b/frontend/src/app/upload/page.tsx index ba8876f..671e250 100644 --- a/frontend/src/app/upload/page.tsx +++ b/frontend/src/app/upload/page.tsx @@ -1,5 +1,6 @@ "use client"; +import Link from "next/link"; import { FormEvent, useMemo, useState } from "react"; import DashboardShell from "@/components/layout/dashboard-shell"; @@ -62,12 +63,12 @@ export default function UploadPaperPage() {

你当前未登录,请先前往登录页面。

- 前往登录 - +
); diff --git a/redis.conf b/redis.conf index b79f83b..f4dc752 100644 --- a/redis.conf +++ b/redis.conf @@ -2,7 +2,7 @@ # Bind to local interfaces so services in Docker network # 和宿主机本地开发进程(uvicorn/celery)都能访问 #bind 127.0.0.1 -bind 127.0.0.1 +bind 0.0.0.0 # Disable protected mode because access is restricted by Docker network diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..0d6cc42 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --- 基础配置 --- +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +FRONTEND_DIR="$ROOT_DIR/frontend" +BACKEND_DIR="$ROOT_DIR/backend" +COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" + +# 颜色定义 +info() { printf "\\033[1;34m[info]\\033[0m %s\\n" "$*"; } +warn() { printf "\\033[1;33m[warn]\\033[0m %s\\n" "$*"; } +error() { printf "\\033[1;31m[error]\\033[0m %s\\n" "$*" >&2; } +success() { printf "\\033[1;32m[success]\\033[0m %s\\n" "$*"; } + +# --- 获取公网 IP --- +# 尝试自动获取公网 IP,如果失败则使用你提供的默认 IP +info "正在获取公网 IP 地址..." +PUBLIC_IP=$(curl -s --max-time 3 ifconfig.me || echo "36.103.203.223") +success "检测到公网 IP: $PUBLIC_IP" + +# --- 1) 检查并启动基础设施 (Docker) --- +if command -v docker &>/dev/null; then + info "正在启动 Postgres/Redis (使用 infra profile)..." + # 加上 --profile infra 确保带 profile 的容器能启动 + docker compose -f "$COMPOSE_FILE" --profile infra up -d postgres redis + + info "等待数据库就绪..." + for i in {1..30}; do + if docker compose -f "$COMPOSE_FILE" exec -T postgres pg_isready -U postgres -d papers &>/dev/null; then + success "数据库已就绪。" + break + fi + [ "$i" -eq 30 ] && warn "数据库启动超时,后续迁移可能失败。" + sleep 1 + done +else + error "未找到 Docker,请确保已执行 sudo usermod -aG docker $USER 并生效。" + exit 1 +fi + +if ! command -v npm &>/dev/null; then + error "未找到 npm,请先安装 Node.js 和 npm (例如: sudo apt install nodejs npm)。" + exit 1 +fi + +# --- 2) 并行安装后端与前端依赖 --- +info "开始同步安装前后端依赖 (显示进度)..." + +install_backend() { + cd "$BACKEND_DIR" + [ ! -d ".venv" ] && python3 -m venv .venv + source .venv/bin/activate + pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple + # 显示安装进度 + pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple +} + +install_frontend() { + cd "$FRONTEND_DIR" + if [ -d "node_modules" ]; then + info "前端依赖已存在 (node_modules),跳过安装..." + else + info "正在安装前端依赖 (npm)..." + npm install --registry=https://registry.npmmirror.com + fi +} + +install_backend & +PID_BE=$! +install_frontend & +PID_FE=$! + +wait $PID_BE && success "后端依赖安装完成。" +wait $PID_FE && success "前端依赖安装完成。" + +# --- 3) 启动服务 --- +info "正在启动所有服务..." + +# 环境变量设置 - 显式指定密码和地址 +export POSTGRES_HOST="127.0.0.1" +export POSTGRES_PASSWORD="shizhiyuan_hanxujie_liushiao" # 对应你刚才改的密码 +export REDIS_HOST="127.0.0.1" +export PYTHONPATH="$BACKEND_DIR:${PYTHONPATH:-}" +export HF_ENDPOINT="https://hf-mirror.com" +export HF_HUB_OFFLINE=1 # 既然你权重下好了,强制离线防止卡顿 + +# A. 启动后端 FastAPI +( + cd "$BACKEND_DIR" + source .venv/bin/activate + python -m alembic upgrade head + exec uvicorn app.main:app --host 0.0.0.0 --port 8000 +) & +BACKEND_PID=$! + +# B. 启动 Celery Worker +( + cd "$BACKEND_DIR" + source .venv/bin/activate + CPU_CORES="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + # 解决 macOS/Ubuntu 上 Celery 的某些 fork 安全问题 + export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + exec celery -A app.workers.celery_app.celery_app worker \ + --loglevel=info --queues default --pool threads --concurrency "$CPU_CORES" +) & +CELERY_PID=$! + +# C. 启动前端 Next.js +( + cd "$FRONTEND_DIR" + # 将后端地址注入前端 + # 使用 -H 0.0.0.0 确保对外网开放 + NEXT_PUBLIC_BACKEND_URL="http://$PUBLIC_IP:8000" npm run dev -- -H 0.0.0.0 +) & +FRONTEND_PID=$! + +# --- 4) 最终信息输出 --- +# 清屏并显示最终访问地址 +sleep 2 +success "================================================" +success " InsightReading 服务已全部在公网启动!" +success "================================================" +info "▶ 前端访问地址: http://$PUBLIC_IP:3000" +info "▶ 后端接口地址: http://$PUBLIC_IP:8000/docs" +info "▶ 数据库状态: 已连接 (127.0.0.1:5432)" +info "▶ Redis 状态: 已连接 (127.0.0.1:6379)" +success "================================================" +info "按 Ctrl+C 停止所有后台进程。" + +trap 'info "正在关闭服务..."; kill $BACKEND_PID $FRONTEND_PID $CELERY_PID 2>/dev/null || true; exit' INT TERM +wait \ No newline at end of file diff --git a/scripts/dev1.sh b/scripts/dev1.sh deleted file mode 100644 index 806460b..0000000 --- a/scripts/dev1.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash -# Helper script to bring up InsightReading's dev stack (databases + backend + frontend) -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -FRONTEND_DIR="$ROOT_DIR/frontend" -BACKEND_DIR="$ROOT_DIR/backend" -COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" - -info() { printf "\\033[1;34m[info]\\033[0m %s\\n" "$*"; } -warn() { printf "\\033[1;33m[warn]\\033[0m %s\\n" "$*"; } -error() { printf "\\033[1;31m[error]\\033[0m %s\\n" "$*" >&2; } - -# --- 1) Start infrastructure ------------------------------------------------- -if command -v docker >/dev/null 2>&1; then - info "Starting postgres/redis via docker compose…" - docker compose -f "$COMPOSE_FILE" up -d postgres redis -else - warn "docker not found; skipping postgres/redis startup" -fi - -# --- 2) Backend --------------------------------------------------------------- -info "Ensuring backend virtualenv exists…" -if [ ! -d "$BACKEND_DIR/.venv" ]; then - python -m venv "$BACKEND_DIR/.venv" -fi - -info "Installing backend dependencies (if needed)…" -( - cd "$BACKEND_DIR" - source .venv/bin/activate - - # Check if uv is available for faster installation - if command -v uv >/dev/null 2>&1; then - info "Using uv for fast installation…" - uv pip install -e . --python .venv >/dev/null - else - info "uv not found, falling back to pip…" - pip install --upgrade pip >/dev/null - pip install -e . >/dev/null - fi -) - -# --- 3) Run database migrations ---------------------------------------------- -info "Running database migrations…" -( - cd "$BACKEND_DIR" - source .venv/bin/activate - export POSTGRES_HOST="localhost" - export REDIS_HOST="localhost" - export PYTHONPATH="$BACKEND_DIR:${PYTHONPATH:-}" - alembic upgrade head -) - -# --- 4) Launch Services ------------------------------------------------------ -info "Starting FastAPI backend on :8000…" -( - cd "$BACKEND_DIR" - source .venv/bin/activate - # 本地开发时,backend 运行在宿主机,需要通过 localhost 访问 docker 启动的 postgres/redis - export POSTGRES_HOST="localhost" - export REDIS_HOST="localhost" - export PYTHONPATH="$BACKEND_DIR:${PYTHONPATH:-}" - uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload -) & -BACKEND_PID=$! - -info "Starting Celery worker (MinerU queue)…" -( - cd "$BACKEND_DIR" - source .venv/bin/activate - export POSTGRES_HOST="localhost" - export REDIS_HOST="localhost" - export PYTHONPATH="$BACKEND_DIR:${PYTHONPATH:-}" - # Fixed: removed duplicate .celery_app - celery -A app.workers.celery_app worker --loglevel=info --queues default -) & -CELERY_PID=$! - -# --- 5) Frontend -------------------------------------------------------------- -info "Installing frontend dependencies…" -( - cd "$FRONTEND_DIR" - npm install -) >/dev/null - -info "Starting Next.js frontend on :3000…" -( - cd "$FRONTEND_DIR" - npm run dev -) & -FRONTEND_PID=$! - -info "Backend PID: $BACKEND_PID" -info "Celery PID: $CELERY_PID" -info "Frontend PID: $FRONTEND_PID" -info "Backend API: http://localhost:8000/api/v1" -info "Frontend: http://localhost:3000" -info "Press Ctrl+C to stop all services." - -trap 'info "Shutting down…"; kill $BACKEND_PID $FRONTEND_PID $CELERY_PID 2>/dev/null || true' INT TERM -wait $BACKEND_PID $FRONTEND_PID $CELERY_PID diff --git a/scripts/docker-infra.sh b/scripts/docker-infra.sh deleted file mode 100644 index d10607f..0000000 --- a/scripts/docker-infra.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" - -info() { printf "\033[1;34m[info]\033[0m %s\n" "$*"; } - -info "Starting Postgres and Redis via docker compose (profile=infra)…" -docker compose -f "$ROOT_DIR/docker-compose.yml" --profile infra up -d postgres redis - -info "Postgres + Redis are running. Use 'docker compose --profile infra down' to stop them." From fa870479c3146215d755293c9bd1f9863101097c Mon Sep 17 00:00:00 2001 From: zhiyuan_2003 <2304022380@qq.com> Date: Sun, 21 Dec 2025 03:27:01 +0000 Subject: [PATCH 04/11] change deployment magic-pdf setting --- backend/config/magic-pdf.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/config/magic-pdf.config.json b/backend/config/magic-pdf.config.json index aa7bad7..2596261 100644 --- a/backend/config/magic-pdf.config.json +++ b/backend/config/magic-pdf.config.json @@ -4,7 +4,7 @@ }, "models-dir": "/tmp/magic-pdf/models", "layoutreader-model-dir": "/tmp/magic-pdf/models/Layout", - "device-mode": "cpu", + "device-mode": "cuda", "ocr-config": { "enable": true }, From 3e10bfa6c13da45be910f8b23ea7d3fab2448ea7 Mon Sep 17 00:00:00 2001 From: zhiyuan_2003 <2304022380@qq.com> Date: Sun, 21 Dec 2025 03:41:53 +0000 Subject: [PATCH 05/11] fix parsing frontend bugs --- .../components/ai-agent/AgentChatPanel.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/ai-agent/AgentChatPanel.tsx b/frontend/src/components/ai-agent/AgentChatPanel.tsx index 47187a8..1644987 100644 --- a/frontend/src/components/ai-agent/AgentChatPanel.tsx +++ b/frontend/src/components/ai-agent/AgentChatPanel.tsx @@ -51,6 +51,8 @@ const AgentChatPanel = forwardRef< { setInputText: (text: string) => void }, AgentChatPanelProps >(({ mineruResult, isParsing, paperId, onCopyToNote }, ref) => { + const showWelcome = !mineruResult; + const [messages, setMessages] = useState([ { id: createId(), @@ -87,6 +89,16 @@ const AgentChatPanel = forwardRef< .catch(() => setCurrentUser(null)); }, []); + const handleScroll = useCallback((event: React.UIEvent) => { + setShowHeader(event.currentTarget.scrollTop < 40); + }, []); + + const scrollToBottom = useCallback((behavior: ScrollBehavior = "auto") => { + const el = scrollContainerRef.current; + if (!el) return; + el.scrollTo({ top: el.scrollHeight, behavior }); + }, []); + useEffect(() => { if (showWelcome) return; scrollToBottom("smooth"); @@ -98,16 +110,6 @@ const AgentChatPanel = forwardRef< }; }, []); - const handleScroll = useCallback((event: React.UIEvent) => { - setShowHeader(event.currentTarget.scrollTop < 40); - }, []); - - const scrollToBottom = useCallback((behavior: ScrollBehavior = "auto") => { - const el = scrollContainerRef.current; - if (!el) return; - el.scrollTo({ top: el.scrollHeight, behavior }); - }, []); - // 加载对话历史 const loadConversationHistory = useCallback(async (convId: number) => { if (!convId) return; @@ -404,7 +406,6 @@ const AgentChatPanel = forwardRef< }, [conversationId, mineruResult, paperId, triggerAutoSummary]); const disableInput = !mineruResult || isParsing; - const showWelcome = !mineruResult; return (