Repository navigation
Make the paint flow visible: Apply at the top, what the ranking sets, and a volume before a model - #155
Merged
Conversation
Every row of Which model to start from gives its threshold (the cut-off as a probability, 0.5 at the model's own boundary) and its input window (the normalizer's own range, or the raw stretched when that won), where they were prose only when they differed. The row is built once, in portable/rank.py score_row, which both routes answer as it is. Use this model now always sets Seed from Prediction's threshold, marked "set by ranking" beside the field until it is changed, and a line under Label it's buttons says what was set. Fit to Paint, beside Seed from Prediction, scores the model Prediction from names with the same score route and sets its threshold and, when the stretch won, its window, with no new volume: POST /api/finetune/rank/window (both modes) makes the window the session's input normalization alone, and Use shares it. The rank card starts closed, lists the models only while open, and stays as it was left. Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW
…el's declared normalization on its start Submit All sat at the bottom of each list, so a model started from the Models tab saw raw voxel values until the user scrolled down and found it. Each tab now opens with one bar (templates/_apply_bar.html): the tab's title and Apply, which stays at the top of the dashboard column as the list scrolls. It lights up and says how many changes are not applied while the lists differ from the chain the backend runs (shared/op-chain.js chainChanges, against the chain last applied or shown; in either tab, as Apply sends both lists), and goes quiet once they are applied. Nothing else sends the lists; Enter in a parameter field still applies. A model started with no input normalizers in the chain sets the ones it declares (an ONNX export's manifest), as part of its start and before its layer is drawn, through the chain's one setter on each side: routes/pipeline.py set_declared_input_norm (services/launch.py update_run_models calls it), and web/src/backend/serving.ts setDeclaredInputNorm (Submit and Add in routes/models.ts). A chain with input normalizers is left alone, and a model that declares none changes nothing. GET /api/models/loaded answers the chain too, and the model whose declaration its input part is; the page reads it after every model change, so the Input list shows what a start set, ticked, with a note naming the model (and a chain Use this model stretched, which it did not show). Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW
…int's window in the Input tab The Models tab's Add started a model without the step Submit's start has: with no input normalizers in the chain, the ones the model declares are set before its layer is drawn (routes.pipeline.set_declared_input_norm); the browser's Add already did this. And Fit to Paint, which stretches the raw's window with no model change, now tells the page (CHAIN_CHANGED_EVENT) so the Input tab's list shows the normalizer as it runs and a later Apply keeps it. Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW
…en voxel size With no model loaded, the Finetune tab's New Volume shows a voxel-size field offering the raw's levels (the models answer's raw_voxel_sizes) and taking any size typed; blank is the level the viewer shows. The volume lies on the raw's grid at that size, in 64-voxel chunks of uint8 labels, with model_name null. The plan is portable/volume.py's (plan_paint_volume, shown_level), which the server and the browser both run. Which model to start from ranks the models on its paint and Use this model carries the paint onto the chosen model's grid; a training job on the volume itself is refused in both modes with training_refusal's one sentence (paint_first in the answer). CreateVolume's model_name is optional and it gains voxel_size; the generated contract is regenerated. Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW
Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW # Conflicts: # CHANGELOG.md
A volume made before a model was chosen names no model, and Seed from Prediction refused it in both modes even with a model picked under Prediction from. The pick, or the only server running, now seeds it; with several running and none picked the seed is refused, not the volume. The rank card's status line no longer says "painted for null" on such a volume. Claude-Session: https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Three things the live try after #152 showed were hidden or missing in the paint-and-finetune flow, in both modes: the chain had to be applied from a button at the bottom of a list, what the ranking installs was invisible, and a paint volume could not exist before a model. Each is written once (shared JS and templates, portable Python) and served by both the Flask dashboard and the browser app.
Nothing applies the Input or Postprocess chain by itself. The one exception is part of an explicit model start, below.
Against main: 53 files, +2,092 −330.
Apply, visible (
ux-apply)_apply_bar.html). It lights up with "N changes not applied" while the lists differ from the chain the backend runs, counting the other tab's edits too ("1 change not applied, in Postprocess"), and goes quiet once applied. Apply, in either tab, sends both lists; Enter in a parameter field still applies. The count is computed inshared/op-chain.jsagainst the live chain.routes/pipeline.set_declared_input_norm, called from Submit (services/launch.update_run_models) and Add (routes/model_resolve.add_model); browser:serving.ts setDeclaredInputNormfrom Submit and Add inroutes/models.ts. Both go through the one chain setter on their side, before the layer is drawn, so the layer is drawn through the normalizers. A chain that already has input normalizers is left alone; a model that declares nothing changes nothing. Afterwards nothing changes without Apply.GET /api/models/loadednow also answers the chain (pipeline) and the model whose declaration its input part is (input_norm_from). The page reads it on load, after every model change, and when the ranking changes the chain, so the Input list shows a normalization the backend set, ticked and as applied, with a note naming the model. This also fixes an older gap: the window Use this model stretched never showed in the Input tab, so an Apply there put the old one back.What the ranking sets (
ux-rank)portable/rank.score_row, which also makes the keep-the-stretch decision the two routes used to make separately.POST /api/finetune/rank/scoreand installs its window and threshold without a new volume, through a new light routePOST /api/finetune/rank/windowthat appliesrank.windowedand the existing chain setter in each mode.rank/useinstalls its window through the same helper. One scoring function, no new maths.docs/finetuning.mdWhich model to start from explains the cut-off and the stretch in plain words, mirroringdocs/source/browser.rst, and both describe Fit to Paint.A volume before a model (
ux-volume)model_namenull) and no training manifest. Planned once inportable/volume.plan_paint_volumeandshown_level;CreateVolumetakes an optionalvoxel_sizeand its model becomes optional. A voxel size together with a model is refused.portable/volume.training_refusal,paint_firstin the answer), instead of the trainer's misleading "no manifest" message.GET /api/finetune/modelsanswer asraw_voxel_sizes(cached per dataset on the server, since the page polls it). Docs indocs/finetuning.mdanddocs/source/browser.rst.The chunk shape is always 64³ rather than the level's own chunk shape: the browser's dataset info carries no chunk shapes, and the two modes must plan the same volume.
Behaviour changes to review
CreateVolumewithout a model is no longer a 400 "No models loaded"; it plans a paint-first volume. A model's volume is planned as before.Left for later
index_page.py,model-advice.jsandpipeline-builder/state.jsstill say "Submit All".Checks
On the merged branch, once: Python 1,216 passed (plus the known import-hygiene row), vitest 58 files / 524 tests, typecheck,
gen:check,export_data --checkand ruff (the three pre-existing findings) all clean. The Apply bar was driven in a real page by its agent (count, lighting, note, Apply, Enter, stickiness); the rest is route-level tests in both modes. Not tried live on a running dashboard yet.Branches merged with merge commits:
ux-rank,ux-apply,ux-volume. Background: the UX follow-up planned after #152 (consolidation_review/UX_FOLLOWUP_BRIEFS.md).https://claude.ai/code/session_01EzWMS4pG3tm1ZtvKULfSfW