diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3c06b14 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,137 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + workflow: + name: Workflow lint + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Run actionlint + env: + ACTIONLINT_VERSION: "1.7.12" + run: | + curl -fsSL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" -o actionlint.tar.gz + tar -xzf actionlint.tar.gz actionlint + ./actionlint + + backend: + name: Backend tests + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: requirements.txt + + - name: Install backend dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + python -m pip install ruff==0.15.20 + + - name: Run Ruff + run: python -m ruff check backend tests + + - name: Run backend unit tests + env: + PYTHONPATH: . + run: python -m pytest -q + + - name: Compile backend modules + run: python -m compileall -q backend + + frontend: + name: Frontend tests and build + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + + - name: Run frontend lint + working-directory: frontend + run: npm run lint + + - name: Run frontend unit tests + working-directory: frontend + run: npm test -- --run + + - name: Run frontend typecheck + working-directory: frontend + run: npm run typecheck + + - name: Build frontend + working-directory: frontend + run: npm run build + + frontend-e2e: + name: Frontend Playwright smoke + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + + - name: Install Playwright browser + working-directory: frontend + run: npx playwright install --with-deps chromium + + - name: Run Playwright smoke tests + working-directory: frontend + run: npm run test:e2e -- interactive-artifacts.spec.ts + + - name: Upload Playwright report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: frontend/playwright-report/ + if-no-files-found: ignore diff --git a/README.md b/README.md index 5d72908..e84e925 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # NotebookLM-Lite +[![CI](https://github.com/LRriver/NotebookLM-Lite/actions/workflows/ci.yml/badge.svg)](https://github.com/LRriver/NotebookLM-Lite/actions/workflows/ci.yml) + [中文](./README_zh.md) | [English](./README.md) NotebookLM-Lite is an open-source NotebookLM-style AI knowledge workspace for local files, grounded RAG chat, notes, interactive Studio artifacts, and long-form podcast generation. It is built for people who like the Google NotebookLM workflow but want a hackable, self-hosted project with their own model endpoints, their own document pipeline, and a clear backend architecture. diff --git a/README_zh.md b/README_zh.md index 4f08ecd..c23c448 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,5 +1,7 @@ # NotebookLM-Lite +[![CI](https://github.com/LRriver/NotebookLM-Lite/actions/workflows/ci.yml/badge.svg)](https://github.com/LRriver/NotebookLM-Lite/actions/workflows/ci.yml) + [中文](./README_zh.md) | [English](./README.md) NotebookLM-Lite 是一个开源的 NotebookLM 风格 AI 知识工作台,用于把本地资料变成可问答、可整理、可生成内容的知识库。它面向长文档阅读、课程学习、论文/手册分析、产品资料整理、播客脚本生成、RAG 应用原型等场景,目标是在一个可自托管、可改造、可接入自定义模型的项目里,复刻大部分 NotebookLM 的核心体验。 diff --git a/backend/api/schemas/slide_deck.py b/backend/api/schemas/slide_deck.py index 166997a..e5d7f8f 100644 --- a/backend/api/schemas/slide_deck.py +++ b/backend/api/schemas/slide_deck.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, Field -from ...domain.slide_deck import SlideDeckOutline, SlidePromptPlanSet, SlideRecord +from ...domain.slide_deck import SlideDeckOutline, SlidePromptPlanSet class SlideDeckCreateRequest(BaseModel): diff --git a/backend/core/interfaces/document_parser.py b/backend/core/interfaces/document_parser.py index 297d024..efd5c55 100644 --- a/backend/core/interfaces/document_parser.py +++ b/backend/core/interfaces/document_parser.py @@ -5,7 +5,7 @@ Supports PDF, DOCX, TXT, Markdown, HTML formats. """ from abc import ABC, abstractmethod -from typing import List, Dict, Any, Optional +from typing import List, Dict, Any from enum import Enum diff --git a/backend/core/interfaces/llm_provider.py b/backend/core/interfaces/llm_provider.py index c532340..d041644 100644 --- a/backend/core/interfaces/llm_provider.py +++ b/backend/core/interfaces/llm_provider.py @@ -5,7 +5,7 @@ Supports OpenAI, Google GenAI (Gemini), and OpenAI-compatible APIs. """ from abc import ABC, abstractmethod -from typing import Type, TypeVar, List, Dict, Any, Optional +from typing import Type, TypeVar, List, Optional from pydantic import BaseModel T = TypeVar('T', bound=BaseModel) diff --git a/backend/core/interfaces/tts_provider.py b/backend/core/interfaces/tts_provider.py index 673141e..e0855e4 100644 --- a/backend/core/interfaces/tts_provider.py +++ b/backend/core/interfaces/tts_provider.py @@ -5,7 +5,7 @@ Supports OpenAI TTS, Dashscope CosyVoice, and other TTS services. """ from abc import ABC, abstractmethod -from typing import List, Dict, Any, Optional +from typing import List, Dict, Any from enum import Enum diff --git a/backend/core/services/document_service.py b/backend/core/services/document_service.py index 5f92f1b..5108c66 100644 --- a/backend/core/services/document_service.py +++ b/backend/core/services/document_service.py @@ -9,7 +9,6 @@ from langchain_text_splitters import RecursiveCharacterTextSplitter from ..interfaces.vector_store import VectorStoreInterface -from ..interfaces.document_parser import DocumentParserInterface from ...domain.document import ProcessedDocument, DocumentChunk, DocumentType from ...infrastructure.parsers.parser_factory import ParserFactory diff --git a/backend/dependencies.py b/backend/dependencies.py index b06f89c..e073f71 100644 --- a/backend/dependencies.py +++ b/backend/dependencies.py @@ -5,7 +5,6 @@ Enables easy testing and swapping of implementations. """ from typing import Any, Optional -from functools import lru_cache from .config import ModelProfile, Settings, get_settings from .core.interfaces.vector_store import VectorStoreInterface diff --git a/backend/infrastructure/parsers/docling_parser.py b/backend/infrastructure/parsers/docling_parser.py index 3b0f62a..8de33f7 100644 --- a/backend/infrastructure/parsers/docling_parser.py +++ b/backend/infrastructure/parsers/docling_parser.py @@ -5,7 +5,6 @@ import asyncio import mimetypes from pathlib import Path -from typing import Any class ParsedDocument(dict): diff --git a/backend/infrastructure/parsers/parser_factory.py b/backend/infrastructure/parsers/parser_factory.py index 3c60436..c4cbdc8 100644 --- a/backend/infrastructure/parsers/parser_factory.py +++ b/backend/infrastructure/parsers/parser_factory.py @@ -4,7 +4,7 @@ Factory for creating appropriate document parsers based on file type. """ from typing import Dict, Type -from ...core.interfaces.document_parser import DocumentParserInterface, DocumentType +from ...core.interfaces.document_parser import DocumentParserInterface from .pdf_parser import PDFParser from .docx_parser import DocxParser from .text_parser import TextParser diff --git a/backend/main.py b/backend/main.py index 58968fe..c0df7e8 100644 --- a/backend/main.py +++ b/backend/main.py @@ -10,7 +10,6 @@ from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from fastapi.staticfiles import StaticFiles from .config import get_settings from .api.routes.chat import router as chat_router diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..6329eae --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,32 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist/**', 'node_modules/**', 'coverage/**', 'test-results/**', 'playwright-report/**'], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: { + ...globals.browser, + ...globals.es2020, + }, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + '@typescript-eslint/no-explicit-any': 'warn', + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + }, + }, +); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 07d4535..017c022 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,7 +33,6 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", "vite": "^8.0.15", - "vite-node": "^4.1.8", "vitest": "^4.1.8" } }, diff --git a/frontend/package.json b/frontend/package.json index 56ab714..fdc477a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "test": "vitest run", "test:e2e": "playwright test", "lint": "eslint .", + "typecheck": "tsc -b", "preview": "vite preview" }, "dependencies": { @@ -37,7 +38,6 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", "vite": "^8.0.15", - "vite-node": "^4.1.8", "vitest": "^4.1.8" } } diff --git a/tests/test_slide_deck_domain_repository.py b/tests/test_slide_deck_domain_repository.py index e91ccea..8b85837 100644 --- a/tests/test_slide_deck_domain_repository.py +++ b/tests/test_slide_deck_domain_repository.py @@ -25,7 +25,7 @@ SlideRecord, SlideStatus, ) -from backend.domain.source import Job, JobStatus, KnowledgeChunk +from backend.domain.source import JobStatus, KnowledgeChunk from backend.infrastructure.slide_deck_files import SlideDeckFileStore from backend.infrastructure.repositories.memory_repository import InMemoryKnowledgeRepository from backend.infrastructure.repositories.seekdb_repository import SeekDBRepository diff --git a/tests/test_slide_deck_export.py b/tests/test_slide_deck_export.py index 6109155..9d33140 100644 --- a/tests/test_slide_deck_export.py +++ b/tests/test_slide_deck_export.py @@ -3,7 +3,6 @@ import base64 from pathlib import Path -import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from pptx import Presentation diff --git a/tests/test_slide_deck_model_runtime.py b/tests/test_slide_deck_model_runtime.py index 3c44bb1..d4926ce 100644 --- a/tests/test_slide_deck_model_runtime.py +++ b/tests/test_slide_deck_model_runtime.py @@ -12,7 +12,6 @@ from backend.domain.slide_deck import ( SlideDeckOutline, SlideOutline, - SlidePromptPlan, SlidePromptPlanSet, ) from backend.infrastructure.image_providers.raw_multimodal_provider import (