Hear every voice in your language
실시간 AI 더빙과 양언어 자막을 제공, 무료·오픈소스·프라이버시 보장
Hermes 모델 리더보드 플러그인
Hermes Model Leaderboard plugin
플러그인TypeScript · JavaScriptGitHub @e-is직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
설치형 프로젝트는 배포 도메인 없이도 확인할 수 있습니다. 개인 저장소는 GitHub 로그인으로, 조직 저장소는 위의 저장소 확인 파일로 직접 확인해 주세요.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A LLM comparison dashboard plugin for Hermes Desktop: compare, score and rank LLM models as a full-page /model-leaderboard route — prices, benchmarks and OpenRouter news, no configuration needed.
Ported from the reference app sumaris-agent/docker/llm-dashboard (live at http://localhost:3004/), first tab only: authentication, dark-mode toggle and the consumption tab are intentionally out of scope — Hermes Desktop already provides the identity (the plugin runs inside the authenticated desktop context) and the theme (via the standard CSS variables).
hermes plugins install e-is/hermes-model-leaderboard --force
hermes plugins enable model-leaderboard
hermes gateway restart
Then reload the desktop window (F5 / Cmd+R): Model Leaderboard appears in the sidebar.
Notes — the three things that trip people up:
--force is expected. The install scanner rates any community plugin CAUTION
(bundled React/recharts regexes read as "obfuscation", localStorage as "persistence").
The verdict is CAUTION, not DANGEROUS, so --force applies — there is no CRITICAL
finding to accept. Two findings were real enough to fix in the build instead:
a SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED string inside bundled prop-types
(now split by scripts/build.mjs) and a zero-width joiner in an emoji.model-leaderboard (from plugin.yaml), not the repository
name hermes-model-leaderboard. hermes plugins enable hermes-model-leaderboard
will not match. Likewise dashboard/manifest.json must carry the same name:
plugin.yaml decides the namespace the renderer's ctx.rest scopes to, while the
manifest decides the path the gateway mounts the router under. A mismatch 404s every
API call (No such API endpoint: /api/plugins/model-leaderboard/...).~/.hermes/plugins/ ships
disabled in Hermes Desktop until you switch it on in Settings → Plugins. That
choice is stored per renderer id: if you enabled an earlier build published as
hermes-model-leaderboard, the model-leaderboard renderer is a new id and starts
off again — no sidebar entry, no page. Enable it once more.enable asks about built-in tool override — "Allow this plugin to replace built-in
tools (e.g. shell_exec, write_file)?". Answer no: this plugin registers no agent
tools at all (it ships a dashboard API router, a desktop renderer and a read-only
skill), so the prompt is not applicable. To skip it:
hermes plugins enable model-leaderboard --no-allow-tool-override.First run seeds the tracked-model list from the main model declared by each installed
Hermes profile (default first), then persists it to dashboard/data/models.json. Delete
that file to re-seed.
default), 0–5 weights on 11 criteria, instant re-ranking + top-5 cards. A profile with no saved criteria starts from a sane default (intelligence 5, price 3, rest 0).ctx.llm.complete_structured, the model the user is already running — no subprocess, no API key in the plugin) to propose weights for that profile; the answer is validated against a JSON schema. Never automatic, always user-triggered.skill_view('model-leaderboard:refresh-benchmarks') walks an agent through refreshing the hand-curated benchmark indices (SWE-bench Verified, LiveCodeBench) from the public leaderboards and writing them back. Read-only, loaded on demand.ConfirmDialog (imported from the plugin SDK), tooltips and labels fully i18n'd (en/fr), styling left to the app's CSS variables.Same conventions as hermes-kanban-gantt:
| Reference (docker/llm-dashboard) | This repo |
|---|---|
backend/main.py (FastAPI :3004, /api/*) | dashboard/plugin_api.py — FastAPI router mounted at /api/plugins/model-leaderboard/ |
backend/data/*.json | dashboard/data/ (shipped seed data + runtime state) |
frontend/src/App.tsx + components (React 18, Recharts) | src/ (TSX) → esbuild → desktop/plugin.js (artifact committed, SDK/react external, Recharts bundled) |
plugin.yaml unified plugin manifest (name: model-leaderboard)
plugin_ctx.py host context (ctx.llm, …) shared with the standalone router
__init__.py register(): publishes ctx.llm + the shipped skills
dashboard/ backend — FastAPI router → /api/plugins/model-leaderboard/
manifest.json name/label/version/api pointer (name must equal plugin.yaml's)
data/ seed: models, benchmarks
skills/ read-only skills → skill_view('model-leaderboard:<name>')
runtime (gitignored): openrouter cache, price history
desktop/ BUILD ARTIFACT — renderer loaded uncompiled
src/ TSX authoring sources (esbuild → desktop/plugin.js)
core/scoring.ts pure scoring helpers (unit-tested)
components/ NewsPanel
hooks/ useResizable
tests/ pytest backend suite + node unit tests (scoring, path guards)
assets/ screenshots + animations/ + catalogue cards (scripts/shots.py, card.py)
npm install
npm run build # src/ → desktop/plugin.js
node --experimental-strip-types --test "tests/unit/*.test.ts" # scoring + path guards
pytest tests/test_backend.py -q # backend
npm test # both
To iterate without reinstalling: copy desktop/plugin.js into
~/.hermes/plugins/model-leaderboard/desktop/ and reload the desktop window. Remove
~/.hermes/desktop-plugins/model-leaderboard if the loader keeps serving the previous
artifact (the lift aborts with EEXIST when that folder already exists — a runtime
bug, not a plugin one).
| Data | Source | Refresh |
|---|---|---|
| Prices, context, modalities, AA indices | OpenRouter GET /api/v1/models | auto (5 min cache) |
| News: promos, price changes, new models | derived from the same cache + local price history | auto |
| SWE-bench Verified | curated in dashboard/data/benchmarks.json | manual |
(The multilingual-coverage criterion was removed: OpenRouter exposes no language field and HF
cardData.language is empty for current models — see the roadmap below.)
Why the benchmark columns are curated rather than synced — measured, not assumed:
raw.githubusercontent.com/Aider-AI/aider/main/aider/website/_data/polyglot_leaderboard.yml)
stops at the gpt-5 / gemini-2.5 / claude-4 generation — 1 correct match out of 19 tracked
models, and fuzzy matching paired deepseek-v3.2-exp with "DeepSeek V3 (0324)" and
grok-4.6 with "grok-4". Wrong numbers are worse than no column.SWE-bench/experiments
(evaluation/verified/<run>/{metadata.yaml,results/}, 4355 files) where the headline
figure depends on the scaffold/agent you pick — an editorial choice, not a derivation.GET /api/v1/benchmarks?source=artificial-analysis (154 models, meta.citation)
covering 12/19 tracked models — usable as a gap-filler. Their own Data API is free
(100–1000 req/day depending on the doc generation you read) but non-redistributable:
fetched values must never be committed, which is why the harvester below writes to the
user's plugin data, not to dashboard/data/.OpenRouter relays Artificial Analysis indices for ~42 % of its catalogue. A tracked model
without one used to show … and score 0 on intelligence, coding and agentic. The plugin now
grades the value it has, in this order, and says where each one came from:
| Source | Marker | Meaning |
|---|---|---|
| manual | ✎ | typed in the table, or filled by the harvester below |
| openrouter | — | relayed by OpenRouter (Artificial Analysis) |
| estimated | ~ | linear fit on the model's other indices (R² 0.81–0.94) |
| inherited | ⤵ | borrowed: exact alias (same price and context) or a published family member |
Inheritance never extrapolates — a variant above every published sibling takes the nearest
one — and only fills fields that are still missing. measured() excludes ~ and ⤵ from
the scale, so a borrowed value cannot set the 100 % mark the others are graded against.
scripts/harvest_aa.py reads the intelligence index from Artificial Analysis. It prefers
their free Data API; without a key it falls back to the public leaderboard page (same
values, brittle parsing — the API is the supported path):
# free key: create an account on artificialanalysis.ai, then API keys
export ARTIFICIAL_ANALYSIS_API_KEY=...
python3 scripts/harvest_aa.py --dry-run # what would change
python3 scripts/harvest_aa.py --write # writes ~/.hermes/plugin-data/model-leaderboard/benchmarks.json
Artificial Analysis no longer publishes a Coding or an Agentic index (..._coding_index
and ..._agentic_index are documented as always null), so the harvester fills intelligence
only; the other two come from OpenRouter or inheritance. Attribution to
artificialanalysis.ai is required by their terms, and the fetched values stay local.
Some models charge more once the prompt passes a threshold — Claude Haiku 5.5 is $0.10/$0.50
per million tokens up to 100k, then ×5. OpenRouter publishes those tiers as
pricing.overrides[].min_prompt_tokens; the plugin reads them and prices each model at the
size the profile declares:
| Mode | What it does |
|---|---|
auto (default) | Follows the profile's Hermes compaction settings: min(threshold_tokens, ratio × window), with ratio floored at 0.75 below a 512K window. It is an estimate of usage, not a guaranteed maximum. |
32k … 1M | Forces that volume everywhere — useful for a "what if I sent 1M?" comparison. |
| don't know | Base prices, with the tier shown as a badge so a cheap-looking model doesn't hide an expensive long context. |
A model that cannot take the declared size is flagged out of context and dropped from the ranking and the Pareto (its price there does not exist). The choice lives in the profile's criteria panel, next to the weights — it is a scenario, not a criterion: it has no weight and does not change how a model is graded on quality.
Done
default included.intelligence: 5 and price 3.ctx.llm.complete_structured on the model the user is running, JSON-schema-validated (replaced the hermes -z subprocess).ConfirmDialog) and full en/fr i18n.refresh-benchmarks (ctx.register_skill).Next
GET /api/v1/benchmarks?source=artificial-analysis as a
gap-filler for the indices (24 h TTL, runtime cache, prefer the plain/"Default
Fallback" AA variant — "Max Effort" inflates the score), and surface source + date
next to the benchmark columns so staleness is visible instead of guessed. The
hand-curated swebench_verified / livecodebench values are covered by the shipped
refresh-benchmarks skill (agent-driven, no API exists for them).cardData.language is empty for 0/5 of the tracked
open-weights models), but LMArena's overall Elo is worth a column: it covers closed models too.
No API exists (arena.ai/api/* → 403 "Route not allowed"; the leaderboard is server-rendered,
and interaction triggers zero XHR), so it needs a DOM extraction guarded by a structure test.
Per-language boards are gone from their public UI, so it cannot serve multilingual data.Later
ctx.socket) instead of polling, if multi-window use ever justifies it.Captured from the real desktop (light theme, English UI, default profile) by
scripts/shots.py — it drives Hermes Desktop over CDP, walks the score views, edits the
criteria for real, and assembles the GIF below with ffmpeg. Every still is clipped to the
plugin's own pane, so no shell chrome (and none of the user's private session list) can
leak into a README image.
| Tracked models | Score bars |
|---|---|
| Histograms (grade × weight) | Heatmap (grades 0–100, provenance on click) |
| Quality / cost (Pareto frontier) | The table (23 tracked models) |
| Tracking profile (weights + Auto-fill) | …and the same profile, re-weighted |
Prices follow the prompt size each profile declares. The same dialog carries Typical prompt
size — auto by default, read from the profile's Hermes compaction settings; a fixed 32k…1M
otherwise. The table then prices every model at that volume, with a ×N badge where a context
tier applies:
Applying those criteria re-ranks the page — the capture run moves Coding to 5, Agentic
to 4, the price weights to 5/4 and the context window to 4, saves, and photographs the
result: the cheap coding models climb (deepseek-v4-flash-1.1, glm-5.3) while the
expensive ones drop. The profile is handed back untouched at the end of the run.
Scoring is per profile — the chip row is the list of installed Hermes profiles, and
each one keeps its own criteria. The capture run creates front-designer, reviewer and
backend (real profiles, removed at the end of the run), gives them opposed weights, and
photographs the same models ranked for two different jobs:
| Front-end designer — coding 5, vision 5 | Reviewer — intelligence 5, context 5 |
|---|---|
Navigation scenario — score views, the tracked table, the criteria dialog opened, edited and applied, then the profile chips re-ranking the page:
Regenerate them (the debug port is gated, hence the platform flag):
pkill -f 'release/linux-unpacked/Hermes'
~/.hermes/hermes-agent/apps/desktop/release/linux-unpacked/Hermes \
--ozone-platform=wayland --remote-debugging-port=9222
python3 scripts/shots.py # --no-gif to skip the animation, --keep-open to leave the page up
Drawn in HTML/CSS and rasterised with the Chromium Hermes ships (scripts/card.py), same
as hermes-kanban-gantt's card — no image
model, no desktop, no CDP port:
python3 scripts/card.py --variant podium # podium | podium-light | bars | scatter
python3 scripts/card.py --all # every alternative + assets/card-alternatives.html
GPL-3.0 — see LICENSE.