| <div class="fw-cards" style="width:100%;margin:14px 0;"></div> |
| <style> |
| .fw-cards { |
| border: 1px solid var(--border-color); |
| border-radius: 12px; |
| background: var(--surface-bg); |
| overflow: hidden; |
| color: var(--text-color); |
| } |
| .fw-cards__head { |
| display: flex; align-items: center; gap: 12px; |
| padding: 8px 12px; |
| border-bottom: 1px solid var(--border-color); |
| background: color-mix(in oklab, var(--muted-color) 4%, transparent); |
| flex-wrap: wrap; |
| } |
| .fw-cards__title { |
| font-size: 10.5px; |
| font-weight: 800; |
| letter-spacing: 1px; |
| text-transform: uppercase; |
| color: var(--muted-color); |
| margin-right: auto; |
| } |
| .fw-cards__view { |
| display: inline-flex; |
| background: var(--surface-bg); |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| padding: 2px; |
| gap: 2px; |
| } |
| .fw-cards__view button { |
| border: 0; |
| background: transparent; |
| color: var(--muted-color); |
| font-size: 10.5px; |
| font-weight: 700; |
| padding: 5px 10px; |
| border-radius: 4px; |
| cursor: pointer; |
| letter-spacing: 0.4px; |
| text-transform: uppercase; |
| } |
| .fw-cards__view button.active { |
| color: var(--text-color); |
| background: color-mix(in oklab, var(--text-color) 8%, transparent); |
| } |
| |
| .fw-cards__body > div { display: none; } |
| .fw-cards__body > div.active { display: block; } |
| |
| |
| .fw-cards__grid { |
| display: grid; |
| grid-template-columns: repeat(3, minmax(0, 1fr)); |
| gap: 12px; |
| padding: 12px; |
| background: color-mix(in oklab, var(--muted-color) 3%, transparent); |
| } |
| @media (max-width: 880px) { .fw-cards__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } |
| @media (max-width: 580px) { .fw-cards__grid { grid-template-columns: minmax(0, 1fr); } } |
| |
| .fw-card { |
| position: relative; |
| border: 1px solid var(--border-color); |
| border-radius: 10px; |
| background: var(--surface-bg); |
| padding: 14px 14px 12px 14px; |
| box-sizing: border-box; |
| display: flex; |
| flex-direction: column; |
| gap: 8px; |
| overflow: hidden; |
| min-width: 0; |
| } |
| .fw-card::before { |
| content: ''; |
| position: absolute; |
| top: 0; left: 0; right: 0; |
| height: 2px; |
| background: var(--fw-accent); |
| } |
| .fw-card__row { |
| display: flex; |
| justify-content: space-between; |
| align-items: flex-start; |
| gap: 10px; |
| min-width: 0; |
| } |
| .fw-card__title-block { flex: 1 1 auto; min-width: 0; } |
| .fw-card__name { |
| font-size: 14.5px; |
| font-weight: 700; |
| color: var(--text-color); |
| line-height: 1.2; |
| } |
| .fw-card__creator { |
| font-size: 11px; |
| color: var(--muted-color); |
| margin-top: 2px; |
| white-space: nowrap; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| } |
| .fw-card__transport { |
| flex-shrink: 0; |
| display: inline-flex; |
| align-items: center; |
| gap: 3px; |
| padding: 2px 7px; |
| border-radius: 999px; |
| border: 1px solid color-mix(in oklab, var(--fw-accent) 30%, var(--border-color)); |
| color: color-mix(in oklab, var(--fw-accent) 90%, var(--text-color)); |
| background: color-mix(in oklab, var(--fw-accent) 10%, transparent); |
| font-size: 9px; |
| font-weight: 700; |
| letter-spacing: 0.3px; |
| text-transform: uppercase; |
| white-space: nowrap; |
| line-height: 1; |
| } |
| .fw-card__transport svg { width: 9px; height: 9px; flex-shrink: 0; } |
| .fw-card__tagline { |
| font-size: 12px; |
| line-height: 1.5; |
| color: var(--muted-color); |
| } |
| .fw-card__meta { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| flex-wrap: wrap; |
| margin-top: auto; |
| } |
| .fw-card__pkg { |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| font-size: 11px; |
| background: color-mix(in oklab, var(--muted-color) 8%, transparent); |
| border: 1px solid var(--border-color); |
| border-radius: 5px; |
| padding: 2px 7px; |
| color: var(--text-color); |
| } |
| .fw-card__pyver { |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| font-size: 10.5px; |
| color: var(--muted-color); |
| } |
| .fw-card__action { |
| display: inline-flex; |
| align-items: center; |
| gap: 5px; |
| color: var(--muted-color); |
| font-size: 11.5px; |
| font-weight: 600; |
| text-decoration: none; |
| transition: color .12s ease; |
| margin-left: auto; |
| } |
| .fw-card__action:hover { color: var(--fw-accent); } |
| .fw-card__action svg { width: 12px; height: 12px; flex-shrink: 0; } |
| |
| |
| .fw-cards__table-wrap { overflow-x: auto; } |
| .fw-cards__table-wrap table { |
| width: 100%; |
| border-collapse: collapse; |
| font-size: 12.5px; |
| min-width: 640px; |
| } |
| .fw-cards__table-wrap thead th { |
| text-align: left; |
| font-size: 10.5px; |
| font-weight: 700; |
| text-transform: uppercase; |
| letter-spacing: 0.6px; |
| color: var(--muted-color); |
| padding: 10px 12px; |
| border-bottom: 1px solid var(--border-color); |
| background: color-mix(in oklab, var(--muted-color) 3%, transparent); |
| white-space: nowrap; |
| } |
| .fw-cards__table-wrap tbody td { |
| padding: 10px 12px; |
| border-bottom: 1px solid color-mix(in oklab, var(--border-color) 60%, transparent); |
| vertical-align: middle; |
| color: var(--text-color); |
| font-size: 12px; |
| line-height: 1.5; |
| } |
| .fw-cards__table-wrap tbody tr:last-child td { border-bottom: 0; } |
| .fw-cards__table-wrap tbody tr:hover td { |
| background: color-mix(in oklab, var(--muted-color) 3%, transparent); |
| } |
| .fw-cards__table-wrap td.idx { |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| color: var(--muted-color); |
| font-size: 11px; |
| width: 28px; |
| } |
| .fw-cards__table-wrap td.fw-name { |
| font-weight: 700; |
| white-space: nowrap; |
| } |
| .fw-cards__table-wrap td.fw-name a { |
| color: var(--text-color); |
| text-decoration: none; |
| border-bottom: 2px solid var(--fw-accent); |
| padding-bottom: 1px; |
| } |
| .fw-cards__table-wrap td.fw-name a:hover { color: var(--fw-accent); } |
| .fw-cards__table-wrap td.fw-name .swatch { |
| width: 8px; height: 8px; border-radius: 50%; |
| background: var(--fw-accent); |
| display: inline-block; |
| margin-right: 7px; |
| vertical-align: middle; |
| } |
| .fw-cards__table-wrap code { |
| background: color-mix(in oklab, var(--muted-color) 10%, transparent); |
| border-radius: 3px; |
| padding: 0 5px; |
| font-size: 11px; |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| } |
| .fwt-pill { |
| display: inline-flex; align-items: center; gap: 4px; |
| padding: 1px 8px; |
| border-radius: 999px; |
| font-size: 10.5px; |
| font-weight: 700; |
| letter-spacing: 0.4px; |
| } |
| .fwt-pill.http { background: color-mix(in oklab, #3b82f6 12%, transparent); color: color-mix(in oklab, #3b82f6 80%, var(--text-color)); border: 1px solid color-mix(in oklab, #3b82f6 30%, transparent); } |
| .fwt-pill.inproc { background: color-mix(in oklab, #ec4899 10%, transparent); color: color-mix(in oklab, #ec4899 80%, var(--text-color)); border: 1px solid color-mix(in oklab, #ec4899 28%, transparent); } |
| </style> |
| <script> |
| (() => { |
| const bootstrap = () => { |
| const scriptEl = document.currentScript; |
| let container = scriptEl ? scriptEl.previousElementSibling : null; |
| if (!(container && container.classList && container.classList.contains('fw-cards'))) { |
| const cands = Array.from(document.querySelectorAll('.fw-cards')) |
| .filter(el => !(el.dataset && el.dataset.mounted === 'true')); |
| container = cands[cands.length - 1] || null; |
| } |
| if (!container || (container.dataset && container.dataset.mounted === 'true')) return; |
| container.dataset.mounted = 'true'; |
| |
| const FRAMEWORKS = [ |
| { name: 'OpenEnv', creator: 'Meta PyTorch', accent: '#3b82f6', transport: 'http', typeLabel: 'HTTP server (MCP)', tagline: 'MCP-based protocol with composable Rubric rewards.', package: 'openenv-core', pkgSrc: 'PyPI', python: '≥3.11', repo: 'https://github.com/meta-pytorch/OpenEnv' }, |
| { name: 'ORS', creator: 'General Reasoning', accent: '#a855f7', transport: 'http', typeLabel: 'HTTP server (REST+SSE)', tagline: 'Per-tool-call rewards via ToolOutput, large catalog.', package: 'ors-sdk', pkgSrc: 'PyPI', python: '≥3.10', repo: 'https://openrewardstandard.io/' }, |
| { name: 'NeMo Gym', creator: 'NVIDIA', accent: '#22c55e', transport: 'http', typeLabel: 'HTTP server (REST)', tagline: 'FastAPI tools + post-episode /verify endpoint.', package: 'nemo_gym', pkgSrc: 'Git', python: '≥3.12', repo: 'https://github.com/NVIDIA-NeMo/Gym' }, |
| { name: 'Verifiers', creator: 'PrimeIntellect', accent: '#ec4899', transport: 'inproc', typeLabel: 'In-process Python', tagline: 'Bundled dataset, tools, Rubric, and trainer.', package: 'verifiers', pkgSrc: 'PyPI', python: '≥3.11', repo: 'https://github.com/PrimeIntellect-ai/verifiers' }, |
| { name: 'SkyRL Gym', creator: 'NovaSky-AI · Berkeley', accent: '#f59e0b', transport: 'inproc', typeLabel: 'In-process (Gym)', tagline: 'Lightweight Gym-style API with ToolGroups.', package: 'skyrl-gym', pkgSrc: 'PyPI', python: '≥3.10', repo: 'https://github.com/NovaSky-AI/SkyRL' }, |
| { name: 'GEM', creator: 'Axon-RL', accent: '#14b8a6', transport: 'inproc', typeLabel: 'In-process (Gymnasium)', tagline: 'Strict Gymnasium 5-tuple, 24+ built-in envs.', package: 'gem-llm', pkgSrc: 'PyPI', python: '≥3.10', repo: 'https://github.com/axon-rl/gem' }, |
| ]; |
| |
| const ICON_HTTP = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19a4.5 4.5 0 1 0-1.4-8.78A6 6 0 0 0 4 12.5 4.5 4.5 0 0 0 6.5 19h11Z"/></svg>`; |
| const ICON_INPROC = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/></svg>`; |
| const ICON_REPO = `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.4-4-1.4-.6-1.4-1.4-1.8-1.4-1.8-1.1-.8.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.4 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11 11 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .3Z"/></svg>`; |
| |
| const cardsHtml = FRAMEWORKS.map(fw => { |
| const transportIcon = fw.transport === 'http' ? ICON_HTTP : ICON_INPROC; |
| const transportLabel = fw.transport === 'http' ? 'HTTP' : 'In-proc'; |
| return ` |
| <div class="fw-card" style="--fw-accent:${fw.accent};"> |
| <div class="fw-card__row"> |
| <div class="fw-card__title-block"> |
| <div class="fw-card__name">${fw.name}</div> |
| <div class="fw-card__creator">${fw.creator}</div> |
| </div> |
| <span class="fw-card__transport">${transportIcon}<span>${transportLabel}</span></span> |
| </div> |
| <div class="fw-card__tagline">${fw.tagline}</div> |
| <div class="fw-card__meta"> |
| <span class="fw-card__pkg">${fw.package}</span> |
| <span class="fw-card__pyver">python ${fw.python}</span> |
| <a class="fw-card__action" href="${fw.repo}" target="_blank" rel="noopener">${ICON_REPO}<span>Repo</span></a> |
| </div> |
| </div> |
| `; |
| }).join(''); |
| |
| const tableRows = FRAMEWORKS.map((fw, i) => ` |
| <tr style="--fw-accent:${fw.accent};"> |
| <td class="idx">${i + 1}</td> |
| <td class="fw-name"><span class="swatch"></span><a href="${fw.repo}" target="_blank" rel="noopener">${fw.name}</a></td> |
| <td>${fw.creator}</td> |
| <td><span class="fwt-pill ${fw.transport}">${fw.typeLabel}</span></td> |
| <td><code>${fw.package}</code> <span style="color:var(--muted-color); font-size:10.5px;">(${fw.pkgSrc})</span></td> |
| </tr> |
| `).join(''); |
| |
| container.innerHTML = ` |
| <div class="fw-cards__head"> |
| <span class="fw-cards__title">Frameworks we implemented and compared</span> |
| <div class="fw-cards__view" role="tablist"> |
| <button type="button" data-view="table" class="active">Table</button> |
| <button type="button" data-view="cards">Cards</button> |
| </div> |
| </div> |
| <div class="fw-cards__body"> |
| <div data-pane="table" class="active"> |
| <div class="fw-cards__table-wrap"> |
| <table> |
| <thead> |
| <tr> |
| <th>#</th> |
| <th>Framework</th> |
| <th>Creator</th> |
| <th>Type</th> |
| <th>Package</th> |
| </tr> |
| </thead> |
| <tbody>${tableRows}</tbody> |
| </table> |
| </div> |
| </div> |
| <div data-pane="cards"> |
| <div class="fw-cards__grid">${cardsHtml}</div> |
| </div> |
| </div> |
| `; |
| |
| const buttons = container.querySelectorAll('.fw-cards__view button'); |
| const panes = container.querySelectorAll('.fw-cards__body > div'); |
| buttons.forEach(b => b.addEventListener('click', () => { |
| const v = b.getAttribute('data-view'); |
| buttons.forEach(x => x.classList.toggle('active', x === b)); |
| panes.forEach(p => p.classList.toggle('active', p.getAttribute('data-pane') === v)); |
| })); |
| }; |
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', bootstrap, { once: true }); |
| } else { |
| bootstrap(); |
| } |
| })(); |
| </script> |
|
|