-
Notifications
You must be signed in to change notification settings - Fork 223
docker, cli, smoke, bench and autotune first-run dx #226
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
135 commits
Select commit
Hold shift + click to select a range
5b67cf2
feat(dflash): align server props and thinking controls
easel 2560086
feat(lucebox): add release CLI and Docker prebuilds
easel 84ddd04
feat(lucebox): add benchmark and profile evidence suite
easel 3f600f9
feat(cpp-server): port /props + /v1/messages/count_tokens
easel 8d6ff04
feat(cpp-server): thinking-budget surface (--think-max-tokens, finish…
easel b4b46a4
feat(cpp-server): Level 1 thinking-budget reprompt + codex review fixes
easel 14f51c3
fix(bench): cap digit-run length in semantic_hint to avoid Python int…
easel 2a89e62
fix(cpp-server): pre-open <think> when enable_thinking=true (Qwen3.6)
easel ed8cbc5
feat(dflash): add bench_long_ctx.py long-context KV sweep
easel 3e8323d
fix(cpp-server): phase-2 gate diagnostics + entrypoint draft-path + m…
easel 6e66891
fix(lucebox): raise ds4_eval max_tokens 4096 -> 16000 (upstream default)
easel 5a1d79e
build(bake): expose DFLASH_CUDA_ARCHES as bake variable
easel 9017c92
feat(lucebox): expose --think-max-tokens via DFLASH_THINK_MAX, defaul…
easel 53c85c5
fix(entrypoint): priority-order draft resolution to avoid safetensors
easel a44a7bc
fix(cpp-server): forward Qwen3.6 <think>/</think> tokens to emitter
easel aacb9bb
docs(thinking-budget): document multi-dialect response aliasing
easel 5059fea
feat(cpp-server): Level 2 in-process force-close on AR path
easel 43ad46b
chore(dflash): drop Python server.py and tests that import from it
easel c7c4bcb
feat(cpp-server,bench): emit reasoning aliases per multi-dialect spec
easel 3c8a218
Merge origin/main into integration/props-uv-squared-clean
easel a1144dd
perf(cpp-server,L2): spec-decode tail-off to AR instead of full bypass
easel 430c413
bench(scripts): add sweep_ds4_2case.sh — portable ds4-eval 2-case swe…
easel 8404bce
docs(tuning-snapshots): L2 force-close partial bench — case 1 FAIL→PASS
easel 6bb6ba1
fix(cpp-server): finish_reason=length + #ifdef out phase2-gate debug
easel 932fe6d
data(ds4-eval): sindri post-merge sweep snapshot — config A-baseline-…
easel 05015f5
fix(scripts): sweep_ds4_2case.sh — fetch+rebase before push to avoid …
easel 408f054
data(ds4-eval): sindri post-merge sweep snapshot — config B-kv-mix
easel c39326a
fix(cpp-server,L2): spec-decode→AR tail-off correctness (audit #47)
easel 34157d2
feat(cpp-server,L2): multi-token close-tag support in BudgetHook
easel fea7aae
fix(cpp-server): restore PrefixCache::stats() + full_stats() (regress…
easel 4685f0e
ci(docker): ship dflash_server binary in cuda12 image
easel 87bd540
feat(cpp-server,L2): port budget force-close to gemma4 + laguna backends
easel 5c785f0
fix(cpp-server,L2): force-close budget uses generated-since-entry frame
easel 6e11681
fix(cpp-server): thinking_tokens / reasoning_tokens use emitter mode-…
easel 7e5435d
fix(cpp-server): PrefixCache /props snapshot uses atomic hit counters
easel b86342d
fix(cpp-server,L2): gemma4 force-close uses generated-since-entry frame
easel cfe28e7
fix(cpp-server): extract normalize_anthropic_system helper
easel 8c37f3c
docs(thinking-budget): rewrite as standalone design spec
easel ca09f64
feat(cpp-server): thinking-budget v2 — model-card defaults + 5-tier e…
easel 7a22c5e
fix(cpp-server): /props + /v1/models advertise all 5 effort tiers + m…
easel 487de20
fix(cpp-server): effort tiers cap at max_ctx, not default_max_tokens
easel 7398103
feat(model_cards): add Laguna-XS.2 sidecar (non-reasoning code MoE, s…
easel 4380e4d
ci(docker): bundle share/model_cards/ sidecars into runtime image
easel e183780
data(ds4-eval): cross-backend snapshot — openrouter, vidar, bragi swe…
easel e0af2ce
docs(model_cards): add Laguna download_urls to sidecar
easel 0ed4444
data(ds4-eval): bragi no-think sweep — 5/8 PASS, beats global-budget 2/8
easel 15ec3fc
docs(tuning-snapshots): update SUMMARY with no-think final results
easel ff2784b
docs(specs): add model-cards.md + props-endpoint.md
easel a9a16e8
feat(cpp-server): address codex r1/r2 P2 items + add sidecar JSON Schema
easel 65a1148
fix(bench-http): --no-think sends thinking={type:disabled} explicitly
easel ab4d6a8
docs(specs): OpenAPI 3.1 for /props + spec accuracy review
easel 7e12454
feat(cpp-server,/props): expose model_card wholesale + new budget_env…
easel 18dd63a
feat(bench): add --area forge for tool-calling eval via forge-guardrails
easel 8cf241c
merge: origin/main into integration/props-uv-squared-clean
easel 9b13474
data(forge): OpenRouter Qwen3.6-27b baseline — 28/30 = 93.3% (--no-th…
easel 449dcfe
docs(tuning-snapshots): add forge --area baseline to SUMMARY (28/30 =…
easel 79083c5
feat(bench-http): --parallel N runs cases concurrently
easel 3b80fa8
feat(cpp-server): emit usage.timings.{prefill_ms,decode_ms,decode_tok…
easel b838d33
refactor(bench): drop forge-guardrails pypi dep, inline forge.* into …
easel 1933cd8
feat(model_cards): add Gemma 4 sidecars (26B-A4B-it MoE + 31B-it dense)
easel 109cebd
feat(cpp-server): thinking-budget v2 + multi-dialect reasoning aliases
easel 51a208e
data(tuning-snapshots): bragi nothink full 92 + OR 5-model comparison
easel ab7c006
feat(bench-http): per-row timed_out flag + prefill tok/s + thinking-t…
easel 3621d49
docs(run-request): luce-dflash --think 92-case sweep at think_max 4k …
easel 428ce3e
data(openrouter): Laguna --area forge (28/30 → 5/30 = 16.7%) + ds4-ev…
easel 56a4355
feat(bench-http): extract reasoning_tokens from OpenAI-style usage de…
easel d7d0684
data(openrouter): Laguna ds4-eval fixed-slug — 53/92 = 57.6% (--no-th…
easel 3d27801
data(openrouter): forge --area cross-model — DS4f 100%, Gemma 4 86.7%…
easel 27f169d
docs(run-request): sindri RTX 3090 Ti qwen3.6 --no-think full-92
easel 4ec18f6
data(openrouter): qwen3.6-27b ds4-eval --no-think full 92 — 51/92 = 5…
easel 3a30ccf
docs(experiments): cache impact + sampling variance design
easel cf8eb4b
feat(bench): enrich --area forge rows with iterations[] + ds4-eval pa…
easel 833ee75
feat(bench-http): capture OR provider + model_version + cost_usd per row
easel c35a8a4
feat(bench-http): probe /props at start, record server_info + --host-…
easel a8dec65
docs(run-request): forge against vidar native ds4-server (DS4f)
easel 43b7a46
data(sindri): RTX 3090 Ti qwen3.6-27b ds4-eval --no-think full 92 — 5…
easel e242ef3
data(tuning-snapshots): qwen36 v2 + vidar think + OR frontier/fill-ma…
easel de6c1ef
perf(qwen35): [ar-decode] summary log + sindri perf sweep iter 1+2
easel e64d824
data(perfsweep): validation finds tuned config doesn't beat canonical
easel 3ed5062
feat(/props): expose chunk + target_device + draft_device under runtime
easel 1357136
feat(/props): expose chunk + target_device + draft_device + [ar-decod…
easel b5e72f8
Merge pr/server (1357136) — /props.runtime expansion lands on PR #269
easel e73e18d
docs(run-request): bump hard_limit_reply_budget for Qwen3.6 (schema +…
easel b2cb6fe
docs(run-request): engine-side budget signaling overhaul (Qwen3.6)
easel eb065d9
feat(bench-http): --area code (HumanEval) integrated into unified har…
easel d63d3b0
docs(run-request): bragi gemma4 + laguna config issues block bench ma…
easel 069c412
feat(server): qwen3.6 budget-signaling overhaul (Phase A+B+C) + phase…
easel e245b6b
feat(server): qwen3.6 budget-signaling overhaul (Phase A+B+C) + phase…
easel c9ecdf4
Merge pr/server (e245b6b) — budget-signaling overhaul lands on PR #269
easel f3116a6
feat(bench-http): --area longctx (long-context frontier) integrated
easel 3fbeea0
feat(bench-http): --area agent (agent-shape probe) integrated
easel c1dac2f
docs(run-request): --area swe (SWE-bench Verified) integration plan
easel 57fdad0
refactor(bench): centralize HE+autotune prompts in bench_humaneval; l…
easel 2d226e8
docs(run-request): gemma4 detail — //thought leak + missing thinking
easel f1d30f2
fix(cpp-server,gemma4): chat template missing thinking-guard + opener…
easel 8f3af56
docs(run-request): gemma4 — definitive crash repro at prompt_tokens=169
easel 734cfac
docs(run-request): reassign gemma4 mul_mat crash investigation to erik
easel 67c444a
fix(entrypoint): pick draft GGUF that matches target's architecture
easel f40aa8f
docs(run-request): gemma4 mul_mat crash resolved — root cause was wro…
easel a4411de
docs(experiments): standard thinking-control probe protocol + gemma4-…
easel 77284cc
docs(experiments): gemma4-26b — sampling + brevity addenda confirm "t…
easel 0cb2674
fix(server,/props): default_generation_settings reflect model card sa…
easel 16bb31e
fix(thinking-budget): transition cue + 4096 reply default — gemma4 fo…
easel 041f491
refactor(server): simplify thinking control — sidecar terminator + dr…
easel 1569889
feat(bench,gemma4): wire bench sampling overrides + gemma thinking_te…
easel c2d725f
feat(server): post-close degenerate-decode watchdog + sibling-field s…
easel 8538ff9
fix(server,bench): broaden degenerate-decode watchdog + surface flag …
easel 4e9abda
feat(gemma4): wire prefill/decode timing into GenerateResult (mirrors…
easel 637dbca
fix(model_cards): laguna-xs.2 — speculator URLs, reasoning toggle, te…
easel 7786b35
refactor(server): drop Level 1 Phase-2 reprompt fallback
easel 92f84cd
fix(laguna): correct chat template + eos_chat_id fallback to </assist…
easel 5f1c7c1
chore: regenerate uv.lock (anthropic dep added; align with pyproject)
easel 598cc88
data(tuning-snapshots): gemma-4-26b ds4-eval-92 think + nothink + 31b…
easel 417009d
feat(bench,laguna): add reasoning_effort knob + laguna comparison sweeps
easel c501e50
data(tuning-snapshots): laguna think 2x2 matrix complete (bragi + OR)
easel 3966d98
data(tuning-snapshots): gemma-4-26b thinking-budget visibility sweep
easel 3f90e7d
fix(bench): omit unset sampling fields so server's card defaults apply
easel 89b1dfe
refactor(bench): replace luce-hub bench scripts with luce-bench dep
easel 925d41f
chore: remove benchmark snapshots from lucebox-hub
easel 2ae6f8d
chore(.gitignore): exclude dflash/docs/tuning-snapshots/
easel 94adb89
merge: luce-org/main into integration/props-uv-squared-clean
easel 1df9099
merge: luce-org/main into integration/props-uv-squared-clean (post-re…
easel e30abf2
chore(deps): bump luce-bench v0.2.3 → v0.2.4
easel 490ff95
feat: absorb luce-bench into the monorepo as a uv workspace member
easel 8d25d27
feat(lucebox): re-wire profile.py to drive benchmarks via luce-bench
easel 469edf3
fix(ci): docker.yml path filter targets server/ (post-rename)
easel a4888f3
fix(server): remove duplicate model-card-resolution block + dup include
easel fc78809
chore(luce-bench): drop dormant nested .github + .gitignore
easel 07bc60b
fix(docker): COPY luce-bench/ workspace member into build context
easel 8524f58
feat(harness): add run_lucebench.sh — luce-bench as a harness client
easel 7bbf9af
feat: harness as uv workspace member + lucebox profile delegates to it
easel 9599f91
chore(deps): merge luce-bench[forge] + harness workspace member
easel 7501564
feat: port all harness clients (codex, opencode, hermes, pi, openclaw)
easel befd00e
fix(harness): inline SPDX license string (build sandbox can't read ..…
easel 4f900c9
fix(entrypoint): warn loudly when multiple targets in models/
easel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # Local venv and Python caches — uv rebuilds inside the image. | ||
| .venv/ | ||
| **/__pycache__/ | ||
| **/*.pyc | ||
|
|
||
| # Build artefacts. | ||
| **/build/ | ||
| **/build-*/ | ||
| dflash/build/ | ||
|
|
||
| # Model weights — bind-mount at runtime instead of baking into the image. | ||
| dflash/models/ | ||
| **/*.gguf | ||
| **/*.safetensors | ||
|
|
||
| # Git metadata. Submodule contents are kept; .git files inside the worktree | ||
| # are not needed at build time. | ||
| .git/ | ||
| **/.git | ||
| **/.gitignore.local | ||
|
|
||
| # Local agent / IDE state. | ||
| .claude/ | ||
| .idea/ | ||
| .vscode/ | ||
|
|
||
| # Misc large or volatile. | ||
| *.log | ||
| *.tmp | ||
| *.swp | ||
| **/*.bin | ||
| **/*.npy |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| name: Docker prebuilds | ||
|
|
||
| # Builds the cuda12 lucebox-hub Docker image defined in docker-bake.hcl | ||
| # and pushes it to GHCR. The bake file is the source of | ||
| # truth for arch matrices and CUDA pinning; this workflow only handles | ||
| # fetching submodules, freeing runner disk, signing in to the registry, and | ||
| # wiring the cache. | ||
|
|
||
| on: | ||
| # Build + push to GHCR when a GitHub Release is published. The release tag | ||
| # becomes one of the image tags via docker/metadata-action's `type=ref, | ||
| # event=tag` + `type=semver` rules below. | ||
| release: | ||
| types: [published] | ||
| # Build-only CI guard on PRs that touch the docker surface. We never push | ||
| # from a PR — even if we wanted to, GITHUB_TOKEN on PRs from forks lacks | ||
| # `packages:write`. The point is to catch Dockerfile / bake-file / arch- | ||
| # list regressions before they land on main. | ||
| pull_request: | ||
| paths: | ||
| - Dockerfile | ||
| - docker-bake.hcl | ||
| - .dockerignore | ||
| - .github/workflows/docker.yml | ||
| - server/CMakeLists.txt | ||
| - server/src/** | ||
| - server/test/** | ||
| - server/include/** | ||
| - server/scripts/** | ||
| - server/deps/** | ||
| - server/pyproject.toml | ||
| - pyproject.toml | ||
| - uv.lock | ||
| - lucebox.sh | ||
| - lucebox/** | ||
| # Manual trigger for one-off rebuilds or pre-release smoke tests. The | ||
| # `push` input controls whether the resulting images land in GHCR or only | ||
| # populate the buildx cache. | ||
| workflow_dispatch: | ||
| inputs: | ||
| push: | ||
| description: "Push images to GHCR after build" | ||
| type: boolean | ||
| default: false | ||
|
|
||
| # Single in-flight build per ref. New pushes cancel the previous run so we | ||
| # don't queue 30-min compiles. | ||
| concurrency: | ||
| group: docker-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository_owner }}/lucebox-hub | ||
|
|
||
| jobs: | ||
| build: | ||
| name: ${{ matrix.variant }} | ||
| # ubuntu-latest = 4 CPU / 16 GB RAM / 14 GB free disk on the GitHub- | ||
| # hosted plan. The disk-free step at the top of the job claws back | ||
| # ~30 GB, which is enough to land a 14 GB image with build cache. | ||
| # CPU is the harder constraint: the fat-binary arch list can take hours | ||
| # on hosted runners. If you outgrow this: | ||
| # • Larger GitHub-hosted runners (`ubuntu-latest-8-cores`, paid) | ||
| # halve wall time. | ||
| # • A self-hosted runner with the host's nvcc avoids the | ||
| # containerised CUDA toolkit pull entirely. | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| variant: [cuda12] | ||
| steps: | ||
| - name: Free runner disk space | ||
| # The default ubuntu-latest image keeps ~25 GB of preinstalled | ||
| # tooling (Android SDK, .NET, Haskell, ghc, etc.) we don't need. | ||
| # Pinned action; check upstream releases before bumping. | ||
| uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 | ||
| with: | ||
| tool-cache: true | ||
| android: true | ||
| dotnet: true | ||
| haskell: true | ||
| large-packages: false # slow; preinstalled apt packages we don't need | ||
| swap-storage: true | ||
|
|
||
| - uses: actions/checkout@v4 | ||
| with: | ||
| # Submodule contents are needed by the cmake build (llama.cpp ggml | ||
| # subtree, mit-han-lab Block-Sparse-Attention). The Dockerfile | ||
| # asserts they're present before running cmake. | ||
| submodules: recursive | ||
|
|
||
| - uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Log in to GHCR | ||
| # Skip on PR runs: we never push from a PR and the token from a fork | ||
| # PR can't `packages:write` anyway. | ||
| if: github.event_name != 'pull_request' | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Derive image metadata | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| # Suffix every tag with the variant so future CUDA stacks can | ||
| # coexist under the same image name. Examples: | ||
| # ghcr.io/<owner>/lucebox-hub:cuda12 | ||
| # ghcr.io/<owner>/lucebox-hub:v0.2.0-cuda12 | ||
| # ghcr.io/<owner>/lucebox-hub:main-cuda12 | ||
| # ghcr.io/<owner>/lucebox-hub:sha-abc1234-cuda12 | ||
| flavor: | | ||
| latest=false | ||
| suffix=-${{ matrix.variant }},onlatest=true | ||
| tags: | | ||
| type=raw,value=${{ matrix.variant }},suffix=,priority=1000,enable=${{ github.event_name == 'release' }} | ||
| type=ref,event=branch | ||
| type=ref,event=tag | ||
| type=ref,event=pr | ||
| type=sha,prefix=sha- | ||
| type=semver,pattern={{version}} | ||
| type=semver,pattern={{major}}.{{minor}} | ||
|
|
||
| - name: Build and push | ||
| uses: docker/bake-action@v5 | ||
| with: | ||
| files: | | ||
| docker-bake.hcl | ||
| ${{ steps.meta.outputs.bake-file }} | ||
| targets: ${{ matrix.variant }} | ||
| push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.push) }} | ||
| # gha cache stores layer blobs in the workflow's Actions cache, | ||
| # scoped by variant so future CUDA stacks don't evict each other. | ||
| # mode=max also caches multi-stage intermediate layers (the | ||
| # builder stage with the 30-min nvcc compile), which is the whole | ||
| # point of doing this. | ||
| set: | | ||
| ${{ matrix.variant }}.cache-from=type=gha,scope=${{ matrix.variant }} | ||
| ${{ matrix.variant }}.cache-to=type=gha,scope=${{ matrix.variant }},mode=max | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| name: Release luce-bench | ||
|
|
||
| # Builds and publishes the luce-bench package to PyPI when a tag | ||
| # matching `luce-bench-v*` is pushed (e.g. `luce-bench-v0.2.5`). The | ||
| # tag's version suffix must match `luce-bench/pyproject.toml`'s | ||
| # `[project] version` — the workflow asserts this and fails otherwise. | ||
| # | ||
| # Uses PyPI trusted publishing (OIDC): set up the publisher in the | ||
| # PyPI project settings as `easel/lucebox-hub` repo + this workflow | ||
| # file + the `pypi` environment. No long-lived API token needed. | ||
|
|
||
| on: | ||
| push: | ||
| tags: | ||
| - 'luce-bench-v*' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build-and-publish: | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: pypi | ||
| url: https://pypi.org/p/luce-bench | ||
| permissions: | ||
| id-token: write # trusted publishing | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v5 | ||
| with: | ||
| version: latest | ||
|
|
||
| - name: Verify tag version matches pyproject.toml | ||
| run: | | ||
| set -euo pipefail | ||
| tag="${GITHUB_REF##*/}" # luce-bench-v0.2.5 | ||
| tag_version="${tag#luce-bench-v}" # 0.2.5 | ||
| file_version=$(awk -F'"' '/^version[[:space:]]*=/{print $2; exit}' luce-bench/pyproject.toml) | ||
| if [ "$tag_version" != "$file_version" ]; then | ||
| echo "Tag version ($tag_version) does not match luce-bench/pyproject.toml version ($file_version)" | ||
| exit 1 | ||
| fi | ||
| echo "Releasing luce-bench v$tag_version" | ||
|
|
||
| - name: Build wheel + sdist | ||
| working-directory: luce-bench | ||
| run: | | ||
| uv build --out-dir dist | ||
|
|
||
| - name: Publish to PyPI (trusted publisher) | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| packages-dir: luce-bench/dist |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.