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
56 changes: 27 additions & 29 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,53 @@
# cp .env.local.example .env.local
#

# -----------------------------------------------------------------------------
# Mintlify analytics (pnpm analytics:fetch*)
# Dashboard: https://app.mintlify.com/settings/organization/api-keys
# Script docs: .github/scripts/analytics/README.md
#
# MINTLIFY_API_KEY — Admin API key (mint_…). Not the Assistant key (mint_dsc_).
# MINTLIFY_PROJECT_ID — Project ID for this docs deployment (docs.comfy.org).
# ANALYTICS_PAGE_LIMIT — optional, rows per page (1–1000, default 200)
# ANALYTICS_PAGE_DELAY_MS — optional, ms between pages (default 36000 ≈ 100 req/h)
# -----------------------------------------------------------------------------

MINTLIFY_API_KEY=
MINTLIFY_PROJECT_ID=
# ANALYTICS_PAGE_LIMIT=200
# ANALYTICS_PAGE_DELAY_MS=36000

# Used by: npm run translate, npm run cms:sync, etc.
# Requires Bun: https://bun.sh
# Used by: pnpm translate, pnpm cms:sync, etc. Requires Bun: https://bun.sh

# -----------------------------------------------------------------------------
# Translation API (translate-i18n.ts)
# OpenAI-compatible endpoint. Works with OpenRouter, DeepSeek, DashScope Qwen-MT, etc.
# Translation API (pnpm translate, pnpm cms:prepare)
# OpenAI-compatible endpoint OpenRouter, DeepSeek, DashScope Qwen-MT, etc.
# -----------------------------------------------------------------------------

# --- OpenRouter ---
# API keys: https://openrouter.ai/keys
# Docs: https://openrouter.ai/docs
# Models: any OpenRouter model id, e.g. deepseek/deepseek-chat, anthropic/claude-sonnet-4
# https://openrouter.ai/keys
# TRANSLATE_API_KEY=
# TRANSLATE_API_BASE_URL=https://openrouter.ai/api/v1
# TRANSLATE_API_MODEL=deepseek/deepseek-chat

# --- DeepSeek ---
# API keys: https://platform.deepseek.com/api_keys
# Docs: https://api-docs.deepseek.com/
# Models: deepseek-v4-pro (quality) | deepseek-v4-flash (faster/cheaper)
# Note: deepseek-chat / deepseek-reasoner are deprecated after 2026-07-24.
# https://platform.deepseek.com/api_keys
# TRANSLATE_API_KEY=
# TRANSLATE_API_BASE_URL=https://api.deepseek.com
# TRANSLATE_API_MODEL=deepseek-v4-pro
# TRANSLATE_API_MODEL=deepseek-v4-flash
# TRANSLATE_CONCURRENCY=5

# --- DashScope Qwen-MT (alternative) ---
# --- DashScope Qwen-MT ---
# TRANSLATE_API_KEY=
# TRANSLATE_API_BASE_URL=https://dashscope-intl.aliyuncs.com/compatible-mode/v1
# TRANSLATE_API_MODEL=qwen-mt-plus

# --- Other fallbacks ---
# TRANSLATE_CJK_API_KEY=
# DASHSCOPE_API_KEY=

# -----------------------------------------------------------------------------
# Translation quality review (review-i18n.ts / npm run translate:review) — optional
# Independent LLM-as-a-judge that scores translations. Use a CHEAP/FAST model —
# evaluation is lighter than translation. Falls back to TRANSLATE_* if unset.
# Translation review (pnpm translate:review) — optional
# Falls back to TRANSLATE_* when unset. Prefer a cheap/fast model.
# -----------------------------------------------------------------------------

# REVIEW_API_KEY=
Expand All @@ -53,30 +58,23 @@ MINTLIFY_API_KEY=
# REVIEW_CONCURRENCY=5

# -----------------------------------------------------------------------------
# Glossary sync (sync-glossary.mjs) — optional
# Path to the ComfyUI frontend locales. Defaults to ../ComfyUI_frontend/src/locales;
# also settable via frontend_locales_path in translation-config.json.
# Glossary sync (pnpm glossary:sync) — optional
# -----------------------------------------------------------------------------

# FRONTEND_LOCALES_PATH=../ComfyUI_frontend/src/locales

# -----------------------------------------------------------------------------
# Optional — external link tracking (track-external-links.py)
# Usually set in GitHub Actions; only needed for local runs
# External link tracking (track-external-links.py) — optional, usually CI only
# -----------------------------------------------------------------------------

# GITHUB_TOKEN=

# -----------------------------------------------------------------------------
# Strapi CMS — changelog sync (see .github/scripts/cms/README.md)
# Create token: Strapi Admin → Settings → API Tokens (find/create/update release-note)
# Strapi CMS (pnpm cms:sync) — see .github/scripts/cms/README.md
# Create token: Strapi Admin → Settings → API Tokens (release-note permissions)
# CMS_SYNC_ALL=1 — optional full backfill (see cms README)
# -----------------------------------------------------------------------------

# CMS_BASE_URL=https://cms.example.com
# CMS_API_TOKEN=
# CMS_PROJECT=comfyui

# Translation (cms:prepare — same keys as pnpm translate)
# TRANSLATE_API_KEY=
# TRANSLATE_API_BASE_URL=
# TRANSLATE_API_MODEL=qwen-mt-plus
107 changes: 107 additions & 0 deletions .github/scripts/analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Mintlify analytics cache

Local cache of Mintlify AI assistant, search, and feedback data for docs gap analysis.

**Credentials:** [`.env.local.example`](../../../.env.local.example)

## Design

### Goal

Find where the docs AI assistant fails (`unanswered`), what users search for, and negative page feedback — before editing content.

### Data sources (Mintlify Admin API)

Three endpoints, fetched in order:

| Phase | API | What you get |
|-------|-----|--------------|
| **assistant** | `/v1/analytics/{projectId}/assistant` | User question, response, sources, `resolutionStatus` (`answered` / `unanswered`) |
| **searches** | `/v1/analytics/{projectId}/searches` | Search terms, hit counts, CTR, top clicked page |
| **feedback** | `/v1/analytics/{projectId}/feedback` | Page ratings and comments |

There is no CSV export API — only paginated JSON. The dashboard “Export to CSV” is email-based and not scriptable. This CLI paginates, merges, and writes lean local reports.

### Fetch model

```
CLI → 7-day date chunks (configurable) → paginated API pages → store/ merge → by-day/ + summary files
```

- **Incremental:** if `manifest.json` exists, only fetch since last `dateTo` (1-day overlap).
- **Checkpoint:** `checkpoint.json` + `store/` survive Ctrl+C, 504, or 429; re-run the same command to resume.
- **Flush:** every 10 API pages and after each chunk; assistant reports are written before searches start.
- **Rate limit:** 100 requests/org/hour shared across all analytics endpoints. Default 36s between pages.

### Output layout (gitignored: `tmp/analytics-cache/`)

| Path | Purpose |
|------|---------|
| `assistant-summary.md` | **Start here** — index linking to daily files |
| `by-day/YYYY-MM-DD.md` | That day's conversations (unanswered first) |
| `by-day/YYYY-MM-DD.json` | Slim JSON per day |
| `unanswered-index.json` | Days with unanswered questions |
| `searches-top.json` | Top 100 search terms (lean mode) |
| `feedback-negative.json` | Negative feedback only (lean mode) |
| `store/` | Raw merge state for resume/incremental |
| `checkpoint.json` | In-progress run state (removed on success) |
| `manifest.json` | Last completed run metadata |

Use `--full` for monolithic JSON exports. Use `--assistant-only` to skip searches and feedback.

### Recommended workflows

| Task | Command |
|------|---------|
| Regular docs tuning | `pnpm analytics:fetch` (30 days, incremental) |
| AI Q&A only | `pnpm analytics:fetch:assistant` |
| One year of history | `pnpm analytics:fetch:all` |
| Custom dates | `pnpm analytics:fetch -- --date-from YYYY-MM-DD --date-to YYYY-MM-DD --fresh` |

After a run, read `assistant-summary.md` → `by-day/YYYY-MM-DD.md` → `unanswered-index.json`.

---

## Setup

```bash
cp .env.local.example .env.local
# Fill MINTLIFY_API_KEY + MINTLIFY_PROJECT_ID — see .env.local.example
```

## Commands

```bash
pnpm analytics:fetch # incremental if cache exists, else last 30 days
pnpm analytics:fetch:assistant # AI Q&A only (30 days; add --all for 1 year)
pnpm analytics:fetch:all # ~1 year, all three datasets; auto-resume
pnpm analytics:fetch -- --fresh # ignore cache, refetch window
pnpm analytics:fetch -- --resume # resume interrupted run only
pnpm analytics:fetch -- --days 14
pnpm analytics:fetch -- --date-from 2025-01-01 --date-to 2025-12-31
pnpm analytics:fetch -- --assistant-only
pnpm analytics:fetch -- --full
pnpm analytics:fetch:dry-run
```

### Date range

| Flag | Meaning |
|------|---------|
| (default) | Last **30 days** |
| `--all` | Last **365 days** (1 year) |
| `--days N` | Last **N days** |
| `--date-from` + `--date-to` | Custom range; **both required**; last day is **inclusive** |

Use `--fresh` when changing the date window so old checkpoint/store does not mix with the new range.

### Checkpoint resume

1. Progress in `tmp/analytics-cache/checkpoint.json`
2. Re-run the same command — finished chunks are skipped
3. `pnpm analytics:fetch -- --assistant-only --resume` — stop after assistant if stuck in searches phase

### Resilience

- **504 / 414:** 7-day chunks; page 2+ sends cursor only; auto-bisect on 414
- **429:** backoff + resume; override throttle via `ANALYTICS_PAGE_LIMIT` / `ANALYTICS_PAGE_DELAY_MS` (see `.env.local.example`)
Loading
Loading