Open-source LLM eval workbench: generate verifiable prompts from parametric templates, compare any model on text or image, settle quality with blind preference, evolve with GEPA, and serve self-hosted vLLM on your GPU.
4
stars
124
commits
TypeScript
primary language
Aug 31, 2026
updated
Open-source LLM evaluation workbench (Next.js App Router, Apache 2.0): generate verifiable evaluation prompts from parametric templates, compare any model from any source on your own task, settle quality by blind human preference, evolve configurations under a quality floor, and serve self-hosted models on your GPU.
The product is five modules plus settings: Generate · Compare · Analyze · Evolve · Deploy. The primary workflow is Generate a deterministic text benchmark, Compare hosted APIs or your own vLLM endpoints, then Analyze the evidence. Deploy is an optional model source and Evolve starts the improvement loop. Image comparison is intentionally out of the product surface for now; its backend adapters remain for later wiring. Costs, where they appear at all, are % of a baseline, never absolute currency. Provider keys stay server-side.
docs/learnings.md; dual-eval corpora stay under gitignored eval/routing-runs/.)demos_requested and demos_fitted diverge and the effective search space narrows.Regenerate offline with yarn export:samples (no API keys). Full files live in exports/samples/.
Baseline vs evolved (excerpt):
Dataset: in22-gen-hi-en · Optimizer: gepa · Quality floor: 0.5
Baseline val quality 0.6000 · val tokens 200 · demos 3/2
Evolved val quality 0.7000 · val tokens 120 · demos 3/3
Token Δ (val): -80
Quality Δ (val): +0.1000
Relative cost vs baseline: 62.5%
Port is fixed at 3939.
yarn via Corepack is fine)git clone https://github.com/redrob-labs/redrob-eval.git
cd redrob-eval
cp .env.example .env
# Edit .env and set OPENROUTER_API_KEY=...
yarn install
yarn verify:phase1
yarn verify:gepa
yarn verify:phase3
yarn export:samples
yarn dev
Open http://localhost:3939, which lands on Compare. The other pages are /evolve, /deploy, /generate and /settings. Restart yarn dev after editing .env.
The workbench follows your operating system's light or dark setting, and changes with it. To pin one instead, use the icon at the right of the title bar or the Appearance card in Settings; the choice is stored in the browser, not in .env. yarn verify:theme prints the contrast of every colour pairing in both palettes, and yarn test fails if dark falls below what light manages.
Nothing else is required for a clean checkout - evaluation runs offline against vendored datasets; only provider API calls leave the machine. CI runs every yarn verify:* plus yarn export:samples and yarn build on each push.
| Module | Path | Purpose |
|---|---|---|
| Compare | / or /compare | Run any text model from any source live on a catalog dataset or generated/custom reference set; rank by measured quality, latency, TTFT and throughput; retain every sample for Analyze. Task Tool routing evaluates the picked models with one shared JSON contract on six-, eighteen-, and fifty-tool scenarios across selectable languages (fertility optional). |
| Evolve | /evolve | GEPA search over instruction / demos / model / script_policy / frame_policy under a quality floor; catalog datasets or custom goal+rubric (LLM judge or checklist QWK); export baseline-vs-evolved report |
| Deploy | /deploy | Serve self-hosted S+L on your GPU host over SSH - measure, start, health, benchmark, resumable terminal |
| Generate | /generate | Browse parametric task templates and their locales, sample instances from content-derived seeds, and run a cross-locale study to a validated results artifact; export either, or hand the prompts straight to Compare |
| Settings | /settings | Provider keys and GPU host config, written to the gitignored root .env; also the light / dark / system theme |
Typical loop: Generate a verifiable prompt set (or pick a catalog dataset) → Compare to pick a model → Evolve under a quality floor → Deploy what you chose, then compare the served endpoint against the frontier again. For tool-routing SLMs: Deploy (install / measure / serve) → Compare with modality Tool routing.
Generate is parametric generation of verifiable evaluation prompts together with their
verifiers. An item is a template plus a seed rather than a row in a file, and the seed is derived
from the generator version, the template id and the instance index rather than chosen. That buys
two things a static set cannot have. The items cannot have leaked into pretraining, because they
did not exist until someone ran the generator. And cherry-picking becomes structurally impossible
rather than discouraged, because any third party can recompute the same seeds from published values
and check them, which a conventional seed such as 42 does not allow. Scoring is done by
deterministic verifiers, not by a judge model.
Underneath the page are the Redrob Verifiable Task Spec v2, a Python generator, verifiers implemented natively in both languages, and a cross-language conformance suite that fails CI if the two ever disagree.
Python stays optional. yarn install && yarn dev is unchanged and still works on a machine
with no interpreter installed; CI has a job that builds with Python removed from PATH to keep it
that way. The TypeScript side reads and audits generated sets and runs every declarative verifier
natively, and it reaches for Python only through an explicit subprocess call that reports absence
with an actionable message instead of failing. /generate behaves the same way: without the CLI it
still lists the template catalog, which is read from disk, and says plainly that sampling needs an
interpreter rather than failing one button at a time.
# optional: only needed to sample instances or run a study
python -m venv .venv-generate
.venv-generate/Scripts/python -m pip install -e packages/generate # Windows
# .venv-generate/bin/pip install -e packages/generate # macOS / Linux
# point the workbench at the venv CLI (repo-root .env; not on PATH by default)
# REDROB_GENERATE_CMD=/absolute/path/to/.venv-generate/.../redrob-generate
redrob-generate emit --template templates/math/linear-equation --count 20 --out /tmp/set
redrob-generate verify --set /tmp/set --outputs answers.jsonl --json
yarn test # the TypeScript half of the conformance suite
See packages/generate/README.md for a worked example,
packages/generate/STUDY.md for running a study, and
templates/README.md for the template layout. Templates are English-only in
substance: hi, hi-Latn and ko locale files exist so the pipeline can be exercised end to end,
but they hold the English prompt verbatim and are marked untranslated. A real translation needs
native-speaker review, none has had one, and a publishable artifact refuses to build over them.
eval/tournaments/{runId}/votes.jsonl.small label, the rest escalate. These land in the same RoutingExample corpus the metric-derived collector fills, so /api/routing/export and the training path are unchanged - the supervision is just human now instead of metric.Offline check for the bracket and label logic: yarn verify:tournament.
Checklist / video skill scoring (custom goal mode: "checklist" or datasets/video-local/ manifests) evolves a judging prompt + frame_policy for agreement with human graders (QWK), not task accuracy. Frames are sampled in memory only - no video bytes are persisted.
Shared rules:
.env only (never sent to the browser){ score, feedback } text alongside the number| Path | Role |
|---|---|
apps/web | Next.js UI + API routes |
packages/harness | Optimizer, eval, metrics, datasets, providers (@redrob/harness) |
packages/tokenizers | Fertility via HF AutoTokenizer (@redrob/tokenizers) |
datasets/ | Vendored eval subsets (Apache-compatible licenses only); video-local/ for non-redistributable checklist manifests |
exports/samples/ | Committed, regenerable sample Evolve report + Pareto SVG |
scripts/parity/ | Optional research comparison vs reference GEPA - not needed to run the app |
spec/ | Redrob Verifiable Task Spec v2 + the cross-language conformance suite |
templates/ | Generate templates, one directory per family, with locale layers |
packages/generate | Optional Python generator redrob-generate - not on the yarn install && yarn dev path |
train/ | Optional Python router training - not on the yarn install && yarn dev path |
Workspace packages are marked "private": true (consumed in-repo; not published to npm).
This repo reimplements GEPA (Genetic-Pareto) in TypeScript from the paper (arXiv:2507.19457). Cite as agrawal2025gepa. See NOTICE. Implementation: packages/harness/src/lib/optimizer/gepa/ - not a file-by-file port of src/gepa/.
On Evolve, pick a catalog dataset or Custom goal (goal + rubric + input-only JSONL; LLM-as-judge). Seed model runs the candidate prompt; reflect model rewrites it from failure feedback; judge (custom mode) scores answers against your rubric. Offline: yarn verify:gepa, yarn verify:phase3, yarn verify:custom-goal. Export reports via GET /api/optimize/runs/:id?export=md.
main is production and only
takes releases, develop is what you branch from and targetyarn runs gives you, and why storage is a configuration valueyarn failures shows, and which failures a prompt fix would clearredrob-generate study, a worked example against the mock provider, and why the stub locales
cannot be published| Variable | Provider |
|---|---|
OPENROUTER_API_KEY | OpenRouter (recommended) |
OPENAI_API_KEY | OpenAI |
ANTHROPIC_API_KEY | Anthropic |
GOOGLE_API_KEY | Google Gemini |
TOGETHER_API_KEY | Together |
FIREWORKS_API_KEY | Fireworks |
HF_TOKEN | Hugging Face (dataset fetcher; required for GPU deploy downloads) |
VLLM_API_KEY | Self-hosted vLLM bearer (auto-issued by /deploy if unset) |
VLLM_PORT | Port exposed by Deploy (default: vLLM convention 8000) |
VLLM_BASE_URL | OpenAI-compatible endpoint for the deployed model (default: http://localhost:8000/v1; set to the GPU host for a remote deploy) |
REDROB_GENERATE_CMD | Optional absolute path to the redrob-generate executable (e.g. inside .venv-generate); when unset the workbench looks for it on PATH |
Set every key at /settings in the app, or via the environment. GPU deploy details (GPU_HOST, GPU_USER, GPU_SSH_KEY, …) never leave the gitignored root .env. See deploy/README.md. Do not put hostnames, usernames, key paths, or API keys in the repo.
Self-hosted models have no API $/token. Relative cost uses measured throughput:
relativeCostWeight(m) = 100 * (tok_per_sec_large / tok_per_sec_m)
A large model alone = 100 (GPU-time per token). Run Benchmark on /deploy once the model is serving; it records MEASURED_TOK_PER_SEC on the GPU host and the app attributes it to that model and no other. One model is served at a time, so comparing candidates means deploying each in turn. FP8 vs bf16 must appear in result caveats, so never mix precisions in one table without that note.
Indic large-candidate A/B (Gemma 4 31B vs Qwen3.6 27B): compare on the IN22-Gen slice (in22-gen-hi-en).
Keep .env at the repo root. Next loads it via apps/web/next.config.ts.
Optimize / Evolve
POST /api/optimize → { runId }GET /api/optimize/runs/:id/events - SSEGET /api/optimize/runs/:id - meta + result + reportGET /api/optimize/runs/:id?export=md|json - downloadable reportCompare
POST /api/compare/run - SSE; dispatches on modality (text | image)POST /api/compare/tournament - build one bracket per prompt from a run's answersGET /api/compare/tournament/:id - meta + brackets + votes + standingsPOST /api/compare/tournament/:id/vote - record a blind vote and advancePOST /api/compare/tournament/:id/judge - let the modality's model judge decide one matchPOST /api/compare/tournament/:id/route-policy - preference labels, save rate, optional corpus writeRouting collection
POST /api/routing/collect → { runId }GET /api/routing/runs/:id/events - SSEGET /api/routing/export?format=chat|flat - training JSONLHeadless
POST /api/eval - SSE text eval with the optional router baseline (includeRouter)POST /api/preference/runs - K×M preference generation, no UI neededGET|POST /api/score - metric fixtures and one-off scoringAlso: /api/models, /api/datasets, /api/image/suites, /api/generate/*, /api/status, /api/probe, …
yarn verify:phase1 # datasets, splits, relative cost helpers
yarn verify:gepa # GEPA unit checks (offline)
yarn verify:phase3 # script_policy, demo fit, report (offline)
yarn verify:custom-goal # custom goal parse + judge JSON (offline)
yarn verify:video # frame_policy, QWK, rubric lint (offline)
yarn verify:tournament # preference brackets + preference-derived routing labels (offline)
yarn verify:selfhosted # self-hosted catalog + relative cost, no currency (offline)
yarn verify:preference-gen # preference run planning + matrix (offline)
yarn export:samples # write exports/samples report + Pareto SVG
yarn test # TypeScript conformance suite for the Generate spec (offline)
yarn generate:spec-types # regenerate TS types from spec/verifiable-task-v2.schema.json
yarn typecheck
yarn lint
yarn build
yarn probe
yarn datasets:fetch # regenerate vendored JSON (not needed at runtime)
yarn export:routing
pip install -r train/requirements-mlp.txt
yarn train:mlp
yarn train:event-detect
See train/README.md.
0.99demos_requested vs demos_fitted on Evolve reportsBuilt by Janghoon Lee (이장훈)
TypeScript
81.4%
Python
12.7%
CSS
4.3%
Shell
1.5%
Open-source LLM eval workbench: generate verifiable prompts from parametric templates, compare any model on text or image, settle quality with blind preference, evolve with GEPA, and serve self-hosted vLLM on your GPU.
4
stars
124
commits
TypeScript
primary language
Aug 31, 2026
updated
Open-source LLM evaluation workbench (Next.js App Router, Apache 2.0): generate verifiable evaluation prompts from parametric templates, compare any model from any source on your own task, settle quality by blind human preference, evolve configurations under a quality floor, and serve self-hosted models on your GPU.
The product is five modules plus settings: Generate · Compare · Analyze · Evolve · Deploy. The primary workflow is Generate a deterministic text benchmark, Compare hosted APIs or your own vLLM endpoints, then Analyze the evidence. Deploy is an optional model source and Evolve starts the improvement loop. Image comparison is intentionally out of the product surface for now; its backend adapters remain for later wiring. Costs, where they appear at all, are % of a baseline, never absolute currency. Provider keys stay server-side.
docs/learnings.md; dual-eval corpora stay under gitignored eval/routing-runs/.)demos_requested and demos_fitted diverge and the effective search space narrows.Regenerate offline with yarn export:samples (no API keys). Full files live in exports/samples/.
Baseline vs evolved (excerpt):
Dataset: in22-gen-hi-en · Optimizer: gepa · Quality floor: 0.5
Baseline val quality 0.6000 · val tokens 200 · demos 3/2
Evolved val quality 0.7000 · val tokens 120 · demos 3/3
Token Δ (val): -80
Quality Δ (val): +0.1000
Relative cost vs baseline: 62.5%
Port is fixed at 3939.
yarn via Corepack is fine)git clone https://github.com/redrob-labs/redrob-eval.git
cd redrob-eval
cp .env.example .env
# Edit .env and set OPENROUTER_API_KEY=...
yarn install
yarn verify:phase1
yarn verify:gepa
yarn verify:phase3
yarn export:samples
yarn dev
Open http://localhost:3939, which lands on Compare. The other pages are /evolve, /deploy, /generate and /settings. Restart yarn dev after editing .env.
The workbench follows your operating system's light or dark setting, and changes with it. To pin one instead, use the icon at the right of the title bar or the Appearance card in Settings; the choice is stored in the browser, not in .env. yarn verify:theme prints the contrast of every colour pairing in both palettes, and yarn test fails if dark falls below what light manages.
Nothing else is required for a clean checkout - evaluation runs offline against vendored datasets; only provider API calls leave the machine. CI runs every yarn verify:* plus yarn export:samples and yarn build on each push.
| Module | Path | Purpose |
|---|---|---|
| Compare | / or /compare | Run any text model from any source live on a catalog dataset or generated/custom reference set; rank by measured quality, latency, TTFT and throughput; retain every sample for Analyze. Task Tool routing evaluates the picked models with one shared JSON contract on six-, eighteen-, and fifty-tool scenarios across selectable languages (fertility optional). |
| Evolve | /evolve | GEPA search over instruction / demos / model / script_policy / frame_policy under a quality floor; catalog datasets or custom goal+rubric (LLM judge or checklist QWK); export baseline-vs-evolved report |
| Deploy | /deploy | Serve self-hosted S+L on your GPU host over SSH - measure, start, health, benchmark, resumable terminal |
| Generate | /generate | Browse parametric task templates and their locales, sample instances from content-derived seeds, and run a cross-locale study to a validated results artifact; export either, or hand the prompts straight to Compare |
| Settings | /settings | Provider keys and GPU host config, written to the gitignored root .env; also the light / dark / system theme |
Typical loop: Generate a verifiable prompt set (or pick a catalog dataset) → Compare to pick a model → Evolve under a quality floor → Deploy what you chose, then compare the served endpoint against the frontier again. For tool-routing SLMs: Deploy (install / measure / serve) → Compare with modality Tool routing.
Generate is parametric generation of verifiable evaluation prompts together with their
verifiers. An item is a template plus a seed rather than a row in a file, and the seed is derived
from the generator version, the template id and the instance index rather than chosen. That buys
two things a static set cannot have. The items cannot have leaked into pretraining, because they
did not exist until someone ran the generator. And cherry-picking becomes structurally impossible
rather than discouraged, because any third party can recompute the same seeds from published values
and check them, which a conventional seed such as 42 does not allow. Scoring is done by
deterministic verifiers, not by a judge model.
Underneath the page are the Redrob Verifiable Task Spec v2, a Python generator, verifiers implemented natively in both languages, and a cross-language conformance suite that fails CI if the two ever disagree.
Python stays optional. yarn install && yarn dev is unchanged and still works on a machine
with no interpreter installed; CI has a job that builds with Python removed from PATH to keep it
that way. The TypeScript side reads and audits generated sets and runs every declarative verifier
natively, and it reaches for Python only through an explicit subprocess call that reports absence
with an actionable message instead of failing. /generate behaves the same way: without the CLI it
still lists the template catalog, which is read from disk, and says plainly that sampling needs an
interpreter rather than failing one button at a time.
# optional: only needed to sample instances or run a study
python -m venv .venv-generate
.venv-generate/Scripts/python -m pip install -e packages/generate # Windows
# .venv-generate/bin/pip install -e packages/generate # macOS / Linux
# point the workbench at the venv CLI (repo-root .env; not on PATH by default)
# REDROB_GENERATE_CMD=/absolute/path/to/.venv-generate/.../redrob-generate
redrob-generate emit --template templates/math/linear-equation --count 20 --out /tmp/set
redrob-generate verify --set /tmp/set --outputs answers.jsonl --json
yarn test # the TypeScript half of the conformance suite
See packages/generate/README.md for a worked example,
packages/generate/STUDY.md for running a study, and
templates/README.md for the template layout. Templates are English-only in
substance: hi, hi-Latn and ko locale files exist so the pipeline can be exercised end to end,
but they hold the English prompt verbatim and are marked untranslated. A real translation needs
native-speaker review, none has had one, and a publishable artifact refuses to build over them.
eval/tournaments/{runId}/votes.jsonl.small label, the rest escalate. These land in the same RoutingExample corpus the metric-derived collector fills, so /api/routing/export and the training path are unchanged - the supervision is just human now instead of metric.Offline check for the bracket and label logic: yarn verify:tournament.
Checklist / video skill scoring (custom goal mode: "checklist" or datasets/video-local/ manifests) evolves a judging prompt + frame_policy for agreement with human graders (QWK), not task accuracy. Frames are sampled in memory only - no video bytes are persisted.
Shared rules:
.env only (never sent to the browser){ score, feedback } text alongside the number| Path | Role |
|---|---|
apps/web | Next.js UI + API routes |
packages/harness | Optimizer, eval, metrics, datasets, providers (@redrob/harness) |
packages/tokenizers | Fertility via HF AutoTokenizer (@redrob/tokenizers) |
datasets/ | Vendored eval subsets (Apache-compatible licenses only); video-local/ for non-redistributable checklist manifests |
exports/samples/ | Committed, regenerable sample Evolve report + Pareto SVG |
scripts/parity/ | Optional research comparison vs reference GEPA - not needed to run the app |
spec/ | Redrob Verifiable Task Spec v2 + the cross-language conformance suite |
templates/ | Generate templates, one directory per family, with locale layers |
packages/generate | Optional Python generator redrob-generate - not on the yarn install && yarn dev path |
train/ | Optional Python router training - not on the yarn install && yarn dev path |
Workspace packages are marked "private": true (consumed in-repo; not published to npm).
This repo reimplements GEPA (Genetic-Pareto) in TypeScript from the paper (arXiv:2507.19457). Cite as agrawal2025gepa. See NOTICE. Implementation: packages/harness/src/lib/optimizer/gepa/ - not a file-by-file port of src/gepa/.
On Evolve, pick a catalog dataset or Custom goal (goal + rubric + input-only JSONL; LLM-as-judge). Seed model runs the candidate prompt; reflect model rewrites it from failure feedback; judge (custom mode) scores answers against your rubric. Offline: yarn verify:gepa, yarn verify:phase3, yarn verify:custom-goal. Export reports via GET /api/optimize/runs/:id?export=md.
main is production and only
takes releases, develop is what you branch from and targetyarn runs gives you, and why storage is a configuration valueyarn failures shows, and which failures a prompt fix would clearredrob-generate study, a worked example against the mock provider, and why the stub locales
cannot be published| Variable | Provider |
|---|---|
OPENROUTER_API_KEY | OpenRouter (recommended) |
OPENAI_API_KEY | OpenAI |
ANTHROPIC_API_KEY | Anthropic |
GOOGLE_API_KEY | Google Gemini |
TOGETHER_API_KEY | Together |
FIREWORKS_API_KEY | Fireworks |
HF_TOKEN | Hugging Face (dataset fetcher; required for GPU deploy downloads) |
VLLM_API_KEY | Self-hosted vLLM bearer (auto-issued by /deploy if unset) |
VLLM_PORT | Port exposed by Deploy (default: vLLM convention 8000) |
VLLM_BASE_URL | OpenAI-compatible endpoint for the deployed model (default: http://localhost:8000/v1; set to the GPU host for a remote deploy) |
REDROB_GENERATE_CMD | Optional absolute path to the redrob-generate executable (e.g. inside .venv-generate); when unset the workbench looks for it on PATH |
Set every key at /settings in the app, or via the environment. GPU deploy details (GPU_HOST, GPU_USER, GPU_SSH_KEY, …) never leave the gitignored root .env. See deploy/README.md. Do not put hostnames, usernames, key paths, or API keys in the repo.
Self-hosted models have no API $/token. Relative cost uses measured throughput:
relativeCostWeight(m) = 100 * (tok_per_sec_large / tok_per_sec_m)
A large model alone = 100 (GPU-time per token). Run Benchmark on /deploy once the model is serving; it records MEASURED_TOK_PER_SEC on the GPU host and the app attributes it to that model and no other. One model is served at a time, so comparing candidates means deploying each in turn. FP8 vs bf16 must appear in result caveats, so never mix precisions in one table without that note.
Indic large-candidate A/B (Gemma 4 31B vs Qwen3.6 27B): compare on the IN22-Gen slice (in22-gen-hi-en).
Keep .env at the repo root. Next loads it via apps/web/next.config.ts.
Optimize / Evolve
POST /api/optimize → { runId }GET /api/optimize/runs/:id/events - SSEGET /api/optimize/runs/:id - meta + result + reportGET /api/optimize/runs/:id?export=md|json - downloadable reportCompare
POST /api/compare/run - SSE; dispatches on modality (text | image)POST /api/compare/tournament - build one bracket per prompt from a run's answersGET /api/compare/tournament/:id - meta + brackets + votes + standingsPOST /api/compare/tournament/:id/vote - record a blind vote and advancePOST /api/compare/tournament/:id/judge - let the modality's model judge decide one matchPOST /api/compare/tournament/:id/route-policy - preference labels, save rate, optional corpus writeRouting collection
POST /api/routing/collect → { runId }GET /api/routing/runs/:id/events - SSEGET /api/routing/export?format=chat|flat - training JSONLHeadless
POST /api/eval - SSE text eval with the optional router baseline (includeRouter)POST /api/preference/runs - K×M preference generation, no UI neededGET|POST /api/score - metric fixtures and one-off scoringAlso: /api/models, /api/datasets, /api/image/suites, /api/generate/*, /api/status, /api/probe, …
yarn verify:phase1 # datasets, splits, relative cost helpers
yarn verify:gepa # GEPA unit checks (offline)
yarn verify:phase3 # script_policy, demo fit, report (offline)
yarn verify:custom-goal # custom goal parse + judge JSON (offline)
yarn verify:video # frame_policy, QWK, rubric lint (offline)
yarn verify:tournament # preference brackets + preference-derived routing labels (offline)
yarn verify:selfhosted # self-hosted catalog + relative cost, no currency (offline)
yarn verify:preference-gen # preference run planning + matrix (offline)
yarn export:samples # write exports/samples report + Pareto SVG
yarn test # TypeScript conformance suite for the Generate spec (offline)
yarn generate:spec-types # regenerate TS types from spec/verifiable-task-v2.schema.json
yarn typecheck
yarn lint
yarn build
yarn probe
yarn datasets:fetch # regenerate vendored JSON (not needed at runtime)
yarn export:routing
pip install -r train/requirements-mlp.txt
yarn train:mlp
yarn train:event-detect
See train/README.md.
0.99demos_requested vs demos_fitted on Evolve reportsBuilt by Janghoon Lee (이장훈)
TypeScript
81.4%
Python
12.7%
CSS
4.3%
Shell
1.5%