Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/
dist/
build/

# Git
.git/
.gitignore

# IDE
.vscode/
.idea/

# Environment
.env

# Logs
*.log

# Test
.pytest_cache/
htmlcov/
.coverage

# Docker
Dockerfile
.dockerignore
docker-compose.yml

# OS
.DS_Store
Thumbs.db

# Project specific
data/chroma_db/*.sqlite3
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ EMBEDDING_MODEL=BAAI/bge-m3
# Cross-Encoder for relevance scoring
RERANKER_MODEL=BAAI/bge-reranker-v2-m3

# ===== PostgreSQL(Docker Compose 内自动配置)=====
# 本地开发(非 Docker):用自己的 PG 实例
# DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/rednote_insight
#
# Docker Compose 部署:自动通过 docker-compose.yml 注入
# DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/rednote_insight

# ===== Redis(Docker Compose 内自动配置)=====
# 本地开发(非 Docker):
# REDIS_URL=redis://localhost:6379/0
#
# Docker Compose 部署:自动通过 docker-compose.yml 注入
# REDIS_URL=redis://redis:6379/0

# ===== LangFuse 可观测性(可选)=====
# 注册 https://cloud.langfuse.com 获取
# LANGFUSE_PUBLIC_KEY=pk-...
# LANGFUSE_SECRET_KEY=sk-...
# LANGFUSE_HOST=https://cloud.langfuse.com

# ===== 限流配置(可选)=====
# RATE_LIMIT_ENABLED=true
# RATE_LIMIT_INSIGHT=10/minute
# RATE_LIMIT_QA=20/minute
# RATE_LIMIT_CRAWL=5/minute

# ===== 小红书 Cookie(Streamlit Cloud 部署用)=====
# 在本地运行: uv run python scripts/export_cookies.py
# 复制输出的 JSON 粘贴到 Streamlit Cloud Secrets → XHS_COOKIES
Expand Down
133 changes: 133 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# =============================================================================
# RedNote Insight — CI Pipeline
# =============================================================================
# 触发条件:push 到 main 或 PR 到 main
# 自动运行:lint → test → build
# =============================================================================

name: CI

on:
push:
branches: [main, master, refactor/production]
pull_request:
branches: [main, master]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
PYTHON_VERSION: "3.11"
UV_VERSION: "0.6.x"

jobs:
# ===== 1. Lint(代码风格检查)=====
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: ${{ env.UV_VERSION }}

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: uv sync --frozen --no-dev

- name: Install dev dependencies
run: uv sync --frozen

- name: Run ruff check
run: uv run ruff check src/ tests/

- name: Run ruff format check
run: uv run ruff format --check src/ tests/

# ===== 2. Test(单元测试 + 集成测试)=====
test:
name: Test (pytest)
runs-on: ubuntu-latest
timeout-minutes: 15
needs: lint
services:
# PostgreSQL + pgvector(用于向量检索测试)
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: rednote_insight_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: ${{ env.UV_VERSION }}

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: uv sync --frozen

- name: Generate demo data
run: |
# 创建 .env 用于 generate_data.py
echo "OPENAI_API_KEY=ci-test-key" > .env
echo "OPENAI_BASE_URL=https://api.siliconflow.cn/v1" >> .env
uv run python generate_data.py
continue-on-error: true

- name: Run tests
run: |
uv run pytest tests/ \
-v \
--tb=short \
--timeout=60 \
-p no:warnings
env:
OPENAI_API_KEY: ci-test-key
OPENAI_BASE_URL: https://api.siliconflow.cn/v1

# ===== 3. Build(Docker 镜像构建验证)=====
build:
name: Build (Docker)
runs-on: ubuntu-latest
timeout-minutes: 15
needs: lint
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: rednote-insight:ci-test
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Verify image
run: |
docker images rednote-insight:ci-test
echo "✅ Docker 镜像构建成功"
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ __pycache__/
*.egg-info/
dist/
build/
.pytest_cache/

# 环境变量
.env
Expand All @@ -14,6 +15,10 @@ sample_data.csv

# 运行时日志
app.log
server.log

# 缓存文件
data/trending_cache.json

# Vector DB index files are small — we COMMIT them so Streamlit Cloud
# doesn't have to rebuild from scratch (saves 30-60s cold start).
Expand All @@ -29,8 +34,6 @@ Thumbs.db

# Streamlit
# .streamlit/secrets.toml — 提交此文件以在免费版 Streamlit Cloud 使用
*.pyc
__pycache__/

# 小红书登录 Cookie(敏感信息,不要提交)
data/cookies.json
Expand Down
20 changes: 0 additions & 20 deletions .streamlit/config.toml

This file was deleted.

61 changes: 61 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# =============================================================================
# RedNote Insight — 多阶段 Docker 构建
# =============================================================================
# 用法:
# docker build -t rednote-insight .
# docker run -p 8000:8000 --env-file .env rednote-insight
# =============================================================================

# ===== Stage 1: Builder — 装依赖 =====
FROM python:3.11-slim AS builder

WORKDIR /build

# 系统依赖(ChromaDB 需要 sqlite3, jieba 等)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/*

# 安装 uv
RUN pip install --no-cache-dir uv

# 先复制依赖文件(利用 Docker 缓存层)
COPY pyproject.toml uv.lock ./

# 安装生产依赖到虚拟环境
RUN uv sync --frozen --no-dev --no-editable

# ===== Stage 2: Runtime — 最小镜像 =====
FROM python:3.11-slim AS runtime

WORKDIR /app

# 运行时系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
libsqlite3-0 \
&& rm -rf /var/lib/apt/lists/*

# 从 builder 复制虚拟环境
COPY --from=builder /build/.venv /app/.venv

# 复制项目源码和配置
COPY pyproject.toml ./
COPY src/ ./src/
COPY static/ ./static/
COPY data/ ./data/
COPY .env.example ./

# 创建数据目录(如果不存在)
RUN mkdir -p /app/data/raw /app/data/chroma_db

# 暴露端口
EXPOSE 8000

# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8000/api/health || exit 1

# 启动命令
CMD [".venv/bin/uv", "run", "uvicorn", "src.api.main:app", \
"--host", "0.0.0.0", "--port", "8000", "--log-level", "info"]
Loading
Loading