A Fuzhou University intelligent Q&A system with student authentication and educational system integration, built with LangGraph, FastAPI, and React.
Current tagged release: v7.14
Release notes: CHANGELOG.md
FZU-Chat provides a ChatGPT-style conversation experience for Fuzhou University students. Each student logs in with their student ID to get isolated conversations and access to educational system tools (grade queries, course schedules, etc.); external visitors can use third-party guest login for public Q&A and campus-life guidance.
- Student authentication: Per-student login with conversation isolation
- Third-party guest mode: WeChat, QQ, Microsoft, Apple, and GitHub OAuth can create visitor sessions with public Q&A, knowledge retrieval, web search, and campus-life recommendations, without binding personal grade, schedule, selection, or academic-affairs tools
- Educational system tools: Query grades, courses, exam scores, and student info via the FZU academic affairs system (based on west2-online/jwch)
- Educational session cleanup: The app only keeps educational-system session cookies on the server side and never stores the raw password; logging out clears both the site login state and the cached educational-session cookies
- ChatGPT-style interface: Modern dark UI with sidebar history, quick actions, and streaming replies
- Message editing and regeneration: Icon-only actions for copying replies, regenerating an assistant answer, or editing a sent user message and rebuilding the following response branch
- Accessible interaction polish: Keyboard-friendly focus rings, skip link, screen-reader status updates, dialog focus handling, and live chat-log announcements
- Low-intrusion campus intelligence: After login or educational reconnect, the backend refreshes cached course, exam, selection, and grade-summary snapshots; each new conversation freezes one compact runtime context so the model can decide whether a gentle end-of-answer reminder is useful without changing old messages
- Rich tool cards: Visual display of tool calls with structured data tables for grades and courses
- Multi-model support: Huawei Cloud MaaS GLM-5.1, Kimi K2.6, and DeepSeek V4 Pro selection, with qwen3-30b-a3b for title summarization
- FZU-aware personalized memory: Confirmed long-term preferences for names, answer style, course-selection habits, academic-query presentation, campus-life needs, and dining/campus preferences, while volatile educational facts remain live tool queries
- Knowledge + web search: FAISS retrieval plus Bocha web search fallback
- Launch-ready single-node hardening: Strict SPA fallback, scanner-path 404s, Redis-backed sessions/rate limits, readiness and Prometheus-style metrics
- Docker deployment: Multi-stage build for React frontend + Python backend, plus production Compose with internal Redis
fzu-chat/
├── app/
│ ├── server.py # FastAPI backend with auth + per-user conversations
│ ├── graph.py # LangGraph workflow with edu tools
│ ├── auth.py # Token-based authentication & session management
│ ├── oauth.py # Third-party visitor OAuth helpers
│ ├── jwch_client.py # FZU undergraduate system client (Python port)
│ ├── edu_tools.py # LangGraph tools for educational queries
│ ├── campus_recommendations.py # Contextual dining/study recommendation service
│ ├── campus_dynamic_context.py # Low-latency dynamic context and reminder suppression
│ ├── user_memory_tools.py # Confirmed personalized-memory tools
│ ├── memory_store.py # SQLite-backed long-term memory store
│ ├── data/ # Knowledge base documents
│ ├── faiss/ # FAISS vector database
│ ├── png/ # Static assets
│ └── storage/ # Per-user conversation storage
├── frontend/
│ ├── src/App.jsx # React chat UI with login
│ ├── src/App.css # Modern dark theme styles
│ └── vite.config.js # Vite config with API proxy
├── Dockerfile
├── docker-compose.prod.yml
├── scripts/ # Deploy, rollback, and SQLite backup helpers
├── docker-compose.yml
└── requirements.txt
HUAWEICLOUD_MAAS_API_KEY– Huawei Cloud MaaS OpenAI-compatible API for GLM-5.1, Kimi K2.6, DeepSeek V4 Pro, and qwen3-30b-a3b title summarizationDASHSCOPE_API_KEY– Alibaba Cloud DashScope embeddings for the local knowledge baseBOCHA_API_KEY– Bocha web searchLANGSMITH_API_KEY– LangSmith tracingAMAP_WEB_SERVICE_KEY– Optional AMap Web Service key for reverse geocoding browser location into text, POI supplementation, and walking/bicycling route distance; local development can also useamap_web_service_key.txtorAMAP_WEB_SERVICE_KEY_FILE; requests are throttled to at most 5 QPS by default; reverse-geocoding uses a short timeout and 10-minute in-process cache; when unset, campus recommendations fall back to the built-in FZU place library and estimated distanceFZU_CHAT_OAUTH_PROVIDERS– Optional comma-separated visible visitor-login provider allowlist, e.g.microsoft,apple,githubfor production while keeping WeChat and QQ available in the codebaseFZU_CHAT_WECHAT_CLIENT_ID/FZU_CHAT_WECHAT_CLIENT_SECRET,FZU_CHAT_QQ_CLIENT_ID/FZU_CHAT_QQ_CLIENT_SECRET,FZU_CHAT_MICROSOFT_CLIENT_ID/FZU_CHAT_MICROSOFT_CLIENT_SECRET,FZU_CHAT_APPLE_CLIENT_ID/FZU_CHAT_APPLE_CLIENT_SECRET,FZU_CHAT_GITHUB_CLIENT_ID/FZU_CHAT_GITHUB_CLIENT_SECRET– Optional visitor OAuth credentials; the matching*_REDIRECT_URIvariables can override the default/api/auth/oauth/{provider}/callbackcallback URL. Apple uses the generated client-secret JWT forFZU_CHAT_APPLE_CLIENT_SECRET; Microsoft can also setFZU_CHAT_MICROSOFT_TENANT, defaulting tocommon.
For local development, the backend also reads root-level key files such as huaweicloud_maas_api_key.txt, dashscope_api_key.txt, and bocha_api_key.txt when container secrets and environment variables are not set.
# 1. Install backend dependencies
pip install -r requirements.txt
# 2. Set environment variables
export HUAWEICLOUD_MAAS_API_KEY=...
export DASHSCOPE_API_KEY=...
export BOCHA_API_KEY=...
export LANGSMITH_API_KEY=...
export AMAP_WEB_SERVICE_KEY=... # optional, for text location and campus routes
export FZU_CHAT_OAUTH_PROVIDERS=microsoft,apple,github # optional visible provider allowlist
export FZU_CHAT_GITHUB_CLIENT_ID=... # optional, for visitor GitHub login
export FZU_CHAT_GITHUB_CLIENT_SECRET=...
# 3. Start backend
uvicorn app.server:app --host 0.0.0.0 --port 8000
# 4. Start frontend dev server
cd frontend && npm install && npm run dev
# 5. Open http://localhost:5173# 1. Create API key files
echo "your-key" > huaweicloud_maas_api_key.txt
echo "your-key" > dashscope_api_key.txt
echo "your-key" > bocha_api_key.txt
echo "your-key" > langsmith_api_key.txt
echo "your-key" > amap_web_service_key.txt # optional, for contextual recommendations
# 2. Build and run local Compose
docker compose up -d --build
# 3. Visit http://localhost:80Production deployment can use docker-compose.prod.yml with an internal Redis container. Set a URL-safe REDIS_PASSWORD such as openssl rand -hex 32, then run FZU_CHAT_VERSION=v7.14 ./scripts/deploy-ghcr.sh; if GHCR image pull fails, the script falls back to a local production image build.
Useful production environment variables:
FZU_CHAT_REDIS_URL– Redis URL for session, rate-limit, stream-slot, and background-task dedupe stateFZU_CHAT_PUBLIC_DOCS=false– keep/openapi.json, Swagger, and ReDoc private by defaultFZU_CHAT_GLOBAL_STREAM_LIMIT=80,FZU_CHAT_USER_STREAM_LIMIT=5– active SSE generation caps for initial campus-scale launchFZU_CHAT_STATIC_FALLBACK=strict– return the SPA only for/andindex.htmlFZU_CHAT_METRICS_ENABLED=true– enable restricted/api/metricsFZU_CHAT_OAUTH_PROVIDERS=microsoft,apple,github– show only Microsoft, Apple, and GitHub in production; omit it to show all repository-supported providersFZU_CHAT_WECHAT_CLIENT_ID/FZU_CHAT_WECHAT_CLIENT_SECRET,FZU_CHAT_QQ_CLIENT_ID/FZU_CHAT_QQ_CLIENT_SECRET,FZU_CHAT_MICROSOFT_CLIENT_ID/FZU_CHAT_MICROSOFT_CLIENT_SECRET,FZU_CHAT_APPLE_CLIENT_ID/FZU_CHAT_APPLE_CLIENT_SECRET,FZU_CHAT_GITHUB_CLIENT_ID/FZU_CHAT_GITHUB_CLIENT_SECRET– enable the matching visitor login entry; unconfigured providers are shown as unavailable in the login UI
POST /api/auth/login– Login with student ID + passwordGET /api/auth/oauth/providers– Report visible visitor-login availabilityGET /api/auth/oauth/{provider}/start– Start a visible visitor OAuth redirectGET|POST /api/auth/oauth/{provider}/callback– OAuth callback that creates a visitor session without educational tools; POST is used for Appleform_postPOST /api/auth/logout– Logout and clear both the site login state and the server-side educational-session cookiesGET /api/auth/me– Current user info
GET /api/models– Available chat modelsGET /api/health– Lightweight liveness checkGET /api/ready– Readiness check for Redis, SQLite writeability, and configured runtime limitsGET /api/metrics– Restricted Prometheus-style operational metricsGET /api/conversations?limit=50&cursor=...– Paginated conversation list with preview and message countsPOST /api/conversations– Create new conversationGET /api/conversations/{id}– Conversation detailDELETE /api/conversations/{id}– Delete conversationPOST /api/conversations/{id}/messages– Stream assistant response (SSE)POST /api/conversations/{id}/messageswithrerun_message_id– Edit or regenerate from an existing user message while preserving the SSE event formatPOST /api/conversations/{id}/messagesmay includecontext.location– One-message transient location context for dynamic reminders and campus recommendations, never persisted to chat historyPOST /api/conversations/{id}/feedback– Save feedbackPOST /api/conversations/{id}/memory-proposals/{tool_id}– Confirm or dismiss a memory save/delete proposal
GET /api/recommendations/locations– Built-in manual campus/location optionsPOST /api/recommendations/signal-refresh– Asynchronously refresh non-sensitive academic summary snapshots used by low-intrusion remindersPOST /api/recommendations/contextual– Generate one-time campus recommendations fromscenario, optional browserlocation,manual_location_id, and optionalseen_grade_digest
Low-intrusion reminders no longer render automatic homepage cards and do not synchronously fetch slow educational-system data when a user sends a message. The backend reads cached non-sensitive summaries plus reminder cooldown state, injects a few dynamic events into a second short SystemMessage, and stores that runtime context once per conversation so later turns append messages without rewriting prior prompt content. Conversation history uses LangChain's approximate token counter and is only trimmed around the 200k-token boundary, preserving tool results for long chats. Browser coordinates are transient per message: they are used server-side for campus recommendations and AMap reverse geocoding, while only the resulting text location can be injected into the current model prompt. Coordinates and text locations are not persisted to conversation storage or long-term memory. Grade summaries store only digests, term labels, and recorded counts, never concrete scores. Mobile geolocation requires an HTTPS origin; plain HTTP server URLs will not show the browser permission prompt. The AMap key stays server-side through environment variables or Docker secrets.
The LLM agent can automatically call these tools only in undergraduate educational-login sessions; visitor mode never binds these personal academic-affairs tools:
query_grades– Course grades and GPAquery_courses– Course schedulequery_student_info– Student profilequery_exam_scores– CET and unified exam scoresrecommend_campus_context– Contextual campus academic, dining, and study recommendations when the user supplies or authorizes a location
# Frontend
cd frontend && npm run lint && npm run build
# Backend
python -m compileall app