Store dataset generation settings and reuse them - #10
Merged
Conversation
Generation steering was used for one call and discarded, so nothing could show how a
dataset's rows came to be, and repeating an ask meant reconstructing it by hand.
Settings live in their own `DatasetGeneration` table rather than as columns on `Dataset`.
`init_db` is a bare `create_all`, which creates missing tables but never missing columns,
so fields here reach an existing database while new `Dataset` fields would leave one
raising `no such column` on every dataset query. It also keeps provenance out of the
dataset's own metadata: an uploaded dataset simply has no row. A test drops the table and
re-runs `init_db` to hold that property.
Both generate routes record what they were asked for, and `set_generation` replaces rather
than accumulates — the settings describe the most recent ask, which is what a form
repopulates from. `source_version_id` is provenance only; nothing reads through it to
derive shape, since the version may have changed or been deleted.
Three consumers:
- `GET /{id}/generation` returns the settings, or null for a dataset that was never
generated. A collapsed panel on the dataset detail page renders them, with the mix shown
as percentages and the source version as an id rather than a link that could 404.
- `POST /{id}/generate-rows` appends more rows, falling back to the stored settings so
repeating an ask needs only a count. Overrides win and become the new stored ask.
Shape is never overridable: the dataset's own columns and label space apply, which is
what keeps new rows compatible with the existing ones. Deleting a dataset drops its
generation record, which would otherwise be unreachable.
- A Duplicate action on the datasets list opens the generate form seeded from an existing
dataset's shape and settings.
`labelMix.fromProportions` inverts `toProportions` so a stored mix repopulates the percent
editor; both prefill paths use it. `DatasetGenerateForm` takes an optional `initial` read
once at mount, with callers remounting via `key` to change it — that keeps the fields plain
state instead of props needing to be synced back on every edit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Every conflict resolved to main's side, because this branch has nothing left to contribute. Its commit 912d6f1 and main's ed5915f are the same work committed twice — identical patch-ids, and 912d6f1's tree is byte-identical to ed5915f's. The web-ui-redesign branch was cut on top of ed5915f, so PR #11 already shipped the generation-settings work; the merge base falls back to 4997006, which is the only reason git saw seven conflicts at all. The resulting tree equals main's exactly, so PR #10's diff is now empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Follow-up to #9.
Why
Generation steering was used for one call and then discarded.
Datasetpersists onlyname,description,columns, andlabel_schema— so nothing could show how a dataset's rows came to be, and repeating an ask meant reconstructing the instructions, column notes, and label mix by hand.Where the settings live
A separate
DatasetGenerationtable, not columns onDataset. That's load-bearing:init_dbis a barecreate_all, which creates missing tables but never missing columns. New fields onDatasetwould leave every existing SQLite database raisingno such columnon any dataset query; a new table is created on next start and existing data is untouched.test_init_db_adds_the_generation_table_to_an_existing_databasedrops the table and re-runsinit_dbto hold that property.It also keeps provenance out of the dataset's own metadata — an uploaded dataset simply has no row, which is why the read returns
nullrather than an empty object.Both generate routes record what they were asked for.
set_generationreplaces rather than accumulates: the settings describe the most recent ask, which is what a form repopulates from.source_version_idis provenance only — nothing reads through it to derive shape, since the version may since have changed or been deleted.Three consumers
Read the settings.
GET /api/datasets/{id}/generationreturns them, ornullfor a dataset that was never generated. A collapsed panel on the dataset detail page renders them — the mix converted back to percentages, and the source version as an id rather than a link that could 404.Top up an existing dataset.
POST /api/datasets/{id}/generate-rowsappends rows and falls back to the stored settings, so repeating an ask needs only{"count": 10}. Anything you do pass overrides the stored value and becomes the new stored ask, so the next top-up repeats what actually ran.Shape is deliberately not overridable — the dataset's own columns and label space always apply, which is what keeps new rows compatible with the existing rows and with any evaluator already running against them. A dataset with an empty label schema gets no suggested labels and rejects
label_guidance, matching the existing rule on/generate-from-version. Deleting a dataset drops its generation record, which would otherwise be unreachable.The modal prefills from the stored settings, and prunes any note whose column has since been removed by an edit — sending it would fail the server's unknown-column check.
Duplicate a dataset. An action on the datasets list opens the generate form seeded from an existing dataset's shape and settings, so you can change one thing and produce a fresh dataset.
Shared plumbing
labelMix.fromProportionsinvertstoProportionsso a stored mix repopulates the percent editor; both prefill paths use it.DatasetGenerateFormtakes an optionalinitial, read once at mount, with callers remounting viakeyto change it. That keeps the fields plain state rather than props that would have to be synced back on every keystroke.Verification
ruff checkandruff format --checkcleantsc --noEmitand production build cleanNew coverage: the store round-trip and replace-not-accumulate behaviour, cascade on delete, the
create_allmigration property, settings stored by both generate routes,nullfor an uploaded dataset, fallback and override precedence on top-up, index continuation, shape being fixed by the dataset, and the prefill in both forms.Note that
web/distis gitignored, so reviewing the UI from a checkout needscd web && npm run buildfirst.🤖 Generated with Claude Code