- Introduced `card_language` attribute in ProjectRecord and Workspace classes to handle multiple languages for project cards. - Updated project creation and renaming methods to accept and store the card language. - Enhanced ZoteroReader to read and return attachment metadata, including language-specific summaries and claims. - Modified LLM client to generate card content based on the specified language, supporting both English and Chinese. - Updated tests to cover new functionality, ensuring correct handling of multilingual card generation and retrieval. - Adjusted UI tests to verify the presence of language selection options and proper rendering of multilingual content.
5.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Zotero KB is a local FastAPI web service that reads from a Zotero SQLite database and generates structured Markdown knowledge cards for academic writing workflows. It provides project management, collection browsing, batch card generation via LLM (DeepSeek), and citation recommendation APIs.
Development Commands
Package manager is uv. All commands run through uv run.
# Install dependencies
UV_CACHE_DIR=/tmp/uv-cache uv sync --extra dev
# Run the server
UV_CACHE_DIR=/tmp/uv-cache uv run python main.py
# Serves on http://127.0.0.1:8000
# Run all tests
UV_CACHE_DIR=/tmp/uv-cache uv run pytest -q
# Run a single test file
UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_api.py -q
# Run a single test
UV_CACHE_DIR=/tmp/uv-cache uv run pytest tests/test_api.py::test_create_project_endpoint -q
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
ZOTERO_DATA_DIR |
/mnt/c/Users/WSX/Zotero |
Path to Zotero data directory (must contain zotero.sqlite) |
ZOTERO_KB_WORKSPACE |
workspace |
Workspace directory for projects, indexes, and cards |
ZOTERO_BRIDGE_FILE |
workspace/bridge/selected-items.json |
Bridge file for Zotero-to-project item import |
DEEPSEEK_API_KEY |
— | Required when projects use llm_provider=deepseek |
Architecture
Layer Overview
api.py → FastAPI routes → Workspace / ProjectService / WritingService / ZoteroReader / CardBuilder
All business logic lives in src/zotero_kb/. There is no database migration system; the app creates directories on demand.
ZoteroReader (zotero_reader.py)
Reads directly from zotero.sqlite via sqlite3. Key capabilities:
read_items(item_keys)— full record with metadata, creators, tags, notes, attachment textsget_collection_tree()/get_collection_items(collection_key)— browse collections hierarchicallysearch_items(query)— title/abstract substring search- PDF text extraction delegates to
pdftotextCLI; other attachments read as plain text - Attachment paths resolve from
storage:<key>relative toZOTERO_DATA_DIR/storage/
Workspace & Project Model (workspace.py, projects.py)
A project is a JSON file plus two companion files under workspace/projects/<project_id>/:
project.json— metadata includingllm.provider,llm.model,card_languageselected-items.json— ordered list of Zotero item keys belonging to the projectproject-index.json— cached denormalized view rebuilt on every read
Workspace creates/renames/deletes projects. ProjectService manages item selection and rebuilds project-index.json by reading three shared indexes:
workspace/library/index/items.json— item metadataworkspace/library/index/cards.json— generated card data (supports per-language variants)workspace/library/index/collections.json— collection metadata
Card Generation (cards.py, llm.py)
CardBuilder.build_or_update(item, language):
- Builds a source bundle from the item record (metadata + notes + attachment texts)
- Hashes and caches the bundle to
library/cache/source-bundles/ - Calls
llm_client.generate_card(source_bundle) - Renders a Markdown file to
library/collections/<collection_path>/<title> [<key>][<lang>].md - Updates
items.json,cards.json, andcollections.json
The LLM layer supports two providers:
deepseek— calls DeepSeek Chat API, requiresDEEPSEEK_API_KEYdeterministic(fallback) — derives card fields from abstract/notes without any API call
Cards support language variants: cards.json stores a map item_key -> {language -> card_data}. The project-index.json selects the variant matching the project's card_language.
Writing Support (writing.py)
recommend_citations(project_id, prompt)— simple term-overlap scoring across project cardsgenerate_plan(project_id, prompt)— returns a single-section plan using the top-scored card
Bridge Import (bridge.py)
The ZOTERO_BRIDGE_FILE (JSON with selected_keys array) is read by the /api/projects/{id}/imports/selected-items endpoint to import items that were pre-selected in Zotero. This is separate from the UI-driven /imports/item-keys endpoint.
Frontend
A single index.html (Jinja2 template rendered as static HTML) provides the full UI. It uses vanilla JS to call the REST API. No build step or JS bundler.
Testing
Tests use fastapi.testclient.TestClient with a FakeLlmClient injected via create_app(config, llm_client=...). The fixture builder (tests/fixtures/build_zotero_fixture.py) creates an in-memory Zotero SQLite schema with sample data. Most tests extract route handlers directly from the FastAPI app rather than using HTTP-level client calls.
Workspace Directory Layout
workspace/
bridge/
selected-items.json # bridge file from Zotero
library/
index/
items.json # item metadata index
cards.json # card data index (language variants)
collections.json # collection metadata index
collections/
<collection_path>/
<title> [<key>][<lang>].md # generated Markdown cards
cache/
source-bundles/
<key>.<lang>.json # cached source bundles
projects/
<project_id>/
project.json # project config
selected-items.json # item keys in this project
project-index.json # denormalized project view
Related Subdirectories
zotcard/— Zotero plugin (separate JS project, not part of the Python service)zotero-rag/— Standalone RAG search service (separate project)zotero-bridge/— Zotero plugin bridge componentskills/zotero-citation-planner/— Claude skill for reading project indexes and generating citation plans