Skip to content
unsolublesugarPublic

About

Fetch, summarize, and publish Raindrop.io articles as a priority-ranked HTML dashboard

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

Tsuyu-mi

日本語

Python License GitHub Actions
Raindrop.io Jinja2 Pydantic

Tsuyu-mi

Periodically fetches articles from a Raindrop.io collection, extracts their content, summarizes them with AI, and outputs a priority-ranked HTML dashboard.

The name Tsuyu-mi comes from: Raindrop → shizuku (雫, droplet) → tsuyu (露, dew) → tsuyu-mi (露見, "seeing the dew").

Purpose

Triage your "read later" articles saved in Raindrop — before reading the full text.

  • Read now — timely or high-value
  • Defer — interesting but not urgent
  • Drop — safe to discard

Setup

1. Clone the repository

git clone https://github.com/unsolublesugar/tsuyu-mi.git
cd tsuyu-mi

2. Prepare the Python environment

Note

Python 3.11 or later is required. uv can install Python itself alongside dependencies.

# Using uv (recommended)
uv venv --python 3.11
source .venv/bin/activate
uv pip install -e ".[dev]"

# Using pip
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

3. Obtain API keys

This tool requires a Raindrop.io API token and an LLM API key.

Raindrop.io test token

  1. Go to Raindrop.io Integrations
  2. Click Create new app under "For Developers"
  3. Enter an app name (e.g. RaindropSummarizer) and create it
  4. Click the app → Create test token
  5. Copy the displayed token

Collection ID

  1. Open Raindrop.io
  2. Navigate to the target collection (e.g. "Unsorted")
  3. Check the URL: https://app.raindrop.io/my/{collection_id} — the numeric part is the collection ID

LLM API key

Obtain an API key from one of the following providers:

Google Gemini (recommended — has a free tier)

  1. Go to Google AI Studio
  2. Create API Key → Create API key in new project
  3. Recommended model: gemini-3.5-flash-lite

Tip

You may need to link a Google Cloud billing account and enable the Gemini API.

OpenAI

  1. Go to OpenAI API Keys
  2. Create new secret key
  3. Recommended model: gpt-5.6-luna

Anthropic

  1. Go to Anthropic Console
  2. Create Key
  3. Recommended model: claude-haiku-4-5

Recommended models (as of September 2026)

This tool does a 3-line summary plus 4-axis scoring, a few to a dozen articles per run. Expensive reasoning models are unnecessary, so the low-cost tier of each provider is recommended.

Provider LLM_MODEL Reference price (input / output, per 1M tokens) Notes
gemini gemini-3.5-flash-lite $0.30 / $2.50 Recommended. Has a free tier
openai gpt-5.6-luna $0.20 / $1.20 Cheapest tier of the GPT-5.6 family
anthropic claude-haiku-4-5 $1.00 / $5.00 Cheapest Claude tier. Oldest model in the current lineup (retirement not sooner than 2026-10-15); switch to claude-sonnet-5 ($2.00 / $10.00) once a retirement notice is published

For better triage quality, swap in a higher tier (gemini-3.8-flash / gpt-5.6-terra / claude-sonnet-5). Only LLM_MODEL changes — no code changes needed.

Note

gemini-3.8-flash (released 2026-09-02) is priced at $0.75 / $3.75 through 2026-12-31 and $1.50 / $7.50 from 2027-01-01. Thinking is on by default (thinking_level: medium) and thinking tokens are billed as output, so the actual cost per article is higher than the list price suggests. gemini-3.7-flash remains supported at the same price.

Note

To cut costs further, Gemini also offers gemini-2.5-flash-lite ($0.10 / $0.40). Triage that requires reading full text and judging relatively tends to get coarse there, so validate with the recommended model first.

4. Configure environment variables

Local execution

cp .env.example .env

Edit .env with your keys:

RAINDROP_TOKEN=your-raindrop-token
RAINDROP_COLLECTION_ID=your-collection-id
LLM_PROVIDER=gemini
LLM_API_KEY=your-llm-api-key
LLM_MODEL=gemini-3.5-flash-lite

Warning

Never commit .env to the repository — it contains secrets.

GitHub Actions

Add the following to your repository: Settings → Secrets and variables → Actions → Repository secrets.

Secret name Value
RAINDROP_TOKEN Raindrop.io API test token
RAINDROP_COLLECTION_ID Target collection ID
LLM_PROVIDER gemini / openai / anthropic
LLM_API_KEY LLM API key
LLM_MODEL Model name (e.g. gemini-3.5-flash-lite)

5. Verify

# Test Raindrop API connectivity only (no LLM required)
python -m src fetch-only

# Summarize a small batch
MAX_SUMMARIZE_PER_RUN=3 python -m src run

# Full run
python -m src run

Usage

# Full pipeline (fetch → extract → summarize → generate HTML)
python -m src run

# Dry run — preview target articles without processing
python -m src run --dry-run

# Verbose logging
python -m src run --verbose

# Fetch from Raindrop only
python -m src fetch-only

# Regenerate HTML
python -m src build-html

# Reprocess a specific article
python -m src reprocess --id 123456789

# Retry all failed articles
python -m src reprocess-failed

Output

An article dashboard is generated at docs/index.html. Open it in a browser to review.

  • Color-coded by priority (HIGH = red / MEDIUM = yellow / LOW = gray)
  • Filter buttons to narrow by priority
  • Each article shows a 3-line summary, read-now reason, defer reason, and keywords
  • A score (8/12) sits next to the priority badge; hover it for the per-axis breakdown

How priority is decided

Asking the LLM to pick high / medium / low directly skews everything toward high — every article here was bookmarked, so all of them are at least somewhat interesting. Instead the LLM only scores four axes from 0 to 3, and the conversion to a priority happens deterministically in code (src/priority.py).

Axis What it measures
novelty Is this a rehash of known material, or first-hand / original information?
relevance How close is the topic to day-to-day engineering work and writing?
depth Does the 3-line summary suffice, or is the full text needed?
actionability Are there concrete steps, settings, or numbers you can use as-is?

The 0–12 total maps to a priority with these thresholds:

Priority Condition
high Total ≥ 10 and depth ≥ 2
medium Total 8–9
low Total ≤ 7
Drop candidate Total ≤ 2

The depth condition means an article can be novel and highly relevant yet still land in medium if the summary already covers it.

The medium floor is 8 rather than the theoretical midpoint (6) because of measured behavior: the LLM gravitates to 2 on every axis, so real totals cluster in 5–10 instead of spreading across 0–12 (median 8 over 15 articles, per-axis mean 1.8–2.2). With the midpoint as the boundary, nothing ever landed in low.

To make triage stricter or looser, tune the threshold constants in src/priority.py (HIGH_TOTAL_MIN / MEDIUM_TOTAL_MIN / HIGH_DEPTH_MIN / DROP_TOTAL_MAX) rather than editing the prompt. Changing the prompt's scoring rubric shifts the distribution too, so tune the two together.

Note

Older data without scores, or a response where the LLM failed to emit them, falls back to the priority the LLM returned.

Configuration

Environment variable Description Default
RAINDROP_TOKEN Raindrop.io API test token (required)
RAINDROP_COLLECTION_ID Target collection ID (required)
LLM_PROVIDER gemini / openai / anthropic gemini
LLM_API_KEY LLM API key (required)
LLM_MODEL Model name (required)
MAX_SUMMARIZE_PER_RUN Max articles to summarize per run 10
REQUEST_TIMEOUT_SECONDS HTTP request timeout (seconds) 20
USER_AGENT HTTP User-Agent header Tsuyu-mi/0.1
OUTPUT_DIR HTML output directory docs
SITE_URL Public site URL used for absolute OGP URLs https://unsolublesugar.github.io/tsuyu-mi/
DATA_DIR Data storage directory data
STATE_DIR State management directory state
LOG_LEVEL Log level INFO

Automated operation with GitHub Actions

1. Set up GitHub Secrets

See "4. Configure environment variables → GitHub Actions" above.

2. Enable GitHub Pages

Settings → Pages → Source: GitHub Actions

Important

Private repositories require GitHub Pro or higher to use GitHub Pages.

3. Execution schedule

  • Automatic: Every 3 days at JST 7:00 (UTC 22:00)
  • Manual: Run on demand from the Actions tab via "Run workflow"

Changes are auto-committed and pushed only when new content is generated.

Testing

pytest
ruff check src/ tests/
ruff format --check src/ tests/

The same checks run in CI (.github/workflows/ci.yml) on every push and pull request.

To run them automatically before each commit, install the pre-commit hooks:

pip install pre-commit
pre-commit install

If you use VS Code, the bundled .vscode/settings.json enables format-on-save with the recommended Ruff extension.

Contributing

Contributions are welcome! A few things that help keep the project healthy:

  • Please make sure pytest passes before opening a pull request. PRs are merged once the test suite is green.
  • When you change or fix behavior, please add or update tests to cover it.
  • Please run ruff check src/ tests/ and ruff format src/ tests/ before pushing — main is kept lint- and format-clean, and CI will fail otherwise.

The suite includes some regression tests that guard against previously-fixed bugs — for example, tests/unit/test_main_fallback.py covers the fallback summarization path (X/Twitter posts, articles where extraction failed). Please fix the underlying code rather than removing these if they fail.

License

MIT

About

Fetch, summarize, and publish Raindrop.io articles as a priority-ranked HTML dashboard

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages