Skip to content

feat: fit() accepts an optional leading model name - #23

Merged
mstrathman merged 4 commits into
mainfrom
feat/fit-leading-name
Aug 3, 2026
Merged

feat: fit() accepts an optional leading model name#23
mstrathman merged 4 commits into
mainfrom
feat/fit-leading-name

Conversation

@mstrathman

@mstrathman mstrathman commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Makes the training side rhyme with the serving side. fit('churn', f1, ..., fN, label) registers the trained student as churn and returns its id, so it reads
in parallel with predict('churn', f1, ..., fN). A leading TEXT argument is
unambiguously the name (features are numeric), so every existing
fit(f1, ..., fN, label [, options]) call is unchanged. The name may still be
given as '{"register":"churn"}'; supplying it both ways raises
PREDICT_ERR_OPTIONS. Like the options object, the leading name must be constant
within an aggregate group.

Why

fit and predict did not read as a pair: the model name was buried in the
options JSON for fit but a leading positional for predict. This is the cheap,
non-breaking ergonomic half. The robustness half (binding features by name so a
reordered predict cannot silently score the wrong columns) is tracked in #21
for v0.3.

Also in here

  • Arity consistency: the trailing-options detection now discounts the leading
    name (argc - has_name >= 3), so a trailing JSON-object argument is treated the
    same way with or without a name.
  • Doc fix (pre-existing): the reference claimed fit's task is "inferred
    from the label", but it defaults to classify and only regresses on an explicit
    task:regress (a real-valued label with no task was silently classified). The
    docs now state the real default. Surfaced by review while touching these files.

Verification

  • make test: 215 passed, 35 skipped. New adversarial tests: leading-name
    register + serve, name-with-options, name-given-twice, name-with-no-features,
    JSON-object-label consistency, and a parity test that a leading-name model
    predicts identically to an options-register one.
  • Local CodeRabbit review against main: no findings.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • fit now accepts an optional leading model name and can register the trained model under that name.
    • Named training returns the registered model ID; unnamed training continues to return a model blob.
    • Existing register options remain supported.
  • Bug Fixes

    • Added validation for conflicting, invalid, or inconsistent model names and aggregate arguments.
  • Documentation

    • Updated function references, options, examples, and changelog entries for named model training and explicit regression selection.

mstrathman and others added 3 commits August 2, 2026 18:54
Mirror predict(model, ...) on the training side: fit('churn', f1, ..., fN,
label) registers the trained student as 'churn' and returns its id, so the
train and serve calls read in parallel with predict('churn', f1, ..., fN).

A leading argument is only a name when it is TEXT, and features are numeric,
so every existing fit(f1, ..., fN, label [, options]) call is unchanged. The
name may still be given as {"register":"churn"}; supplying it both ways is a
mistake, not a precedence to resolve, and raises PREDICT_ERR_OPTIONS. The
leading name, like the options object, must be constant within a group.

Adds adversarial tests (name-given-twice, name-with-no-features) and a parity
test that a leading-name model predicts identically to an options-register one.
Docs and CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review: the trailing-options detection used a raw argc >= 3, so a
JSON-object trailing argument was consumed as options with a leading name but
could land as a class label without one. Detect the leading name first and gate
options on argc - has_name >= 3, so both forms treat the trailing object the
same way. Adds a consistency test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review: the fit reference claimed task is "inferred from the label",
but fit defaults to classify and only regresses when task=regress is given
explicitly (a real-valued label without it is trained as classification). State
the real default. Also document that the leading name and the options object
must be constant within an aggregate group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a4b7c80e-ff10-43c0-b85a-eac07a80d416

📥 Commits

Reviewing files that changed from the base of the PR and between 2e30518 and 24b4c00.

📒 Files selected for processing (5)
  • README.md
  • predict-tabular.c
  • tests/test_fit.py
  • website/src/content/docs/reference/functions.md
  • website/src/content/docs/reference/options.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • website/src/content/docs/reference/options.md
  • website/src/content/docs/reference/functions.md
  • README.md
  • predict-tabular.c
  • tests/test_fit.py

📝 Walkthrough

Walkthrough

fit() now accepts an optional leading model name. Named calls register the trained model and return its ID. Calls using both a leading name and register fail with PREDICT_ERR_OPTIONS. Existing positional calls remain supported.

Changes

Named fit registration

Layer / File(s) Summary
Parse and register named models
predict-tabular.c:858-1148
fit_step parses an optional leading text name, adjusts arity and feature/label extraction, validates aggregate consistency, and registers the model under the selected ID.
Validate named fit behavior
tests/test_fit.py:38-131
Tests cover named registration, prediction, trailing options, duplicate registration, missing arguments, aggregate name consistency, embedded NUL rejection, and trailing JSON handling.
Document the fit interface
CHANGELOG.md:9-19, README.md:162, website/src/content/docs/guides/operations.md:19, website/src/content/docs/reference/functions.md:68-91, website/src/content/docs/reference/options.md:51-61
Documentation describes the optional name, registration alternatives, return values, aggregate consistency, and explicit task behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SQLClient
  participant fit_step
  participant ModelRegistry
  participant predict
  SQLClient->>fit_step: call fit(name, features, label, options)
  fit_step->>fit_step: validate name, options, arity, and aggregate consistency
  fit_step->>ModelRegistry: register trained model under name
  ModelRegistry-->>fit_step: return model ID
  SQLClient->>predict: predict(model ID, scalar features)
  predict-->>SQLClient: return prediction
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required feat: prefix and clearly describes optional leading model-name support for fit().
Description check ✅ Passed The description includes the change rationale, detailed behavior, affected edge cases, and test verification; the required sections are present.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Changelog Entry For User-Visible Changes ✅ Passed CHANGELOG.md:7-18 contains an [Unreleased] Added entry for the new leading-name fit behavior, registration forms, error, and compatibility; source confirms the SQL behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fit-leading-name

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@predict-tabular.c`:
- Around line 858-866: Update the fit() documentation near the trailing options
description to state that a trailing JSON-object string is interpreted as
options only when argc - has_name is at least 3; otherwise, as in fit(tenure,
'{}'), it remains the classification label. Preserve the existing behavior and
clarify that object-valued labels are valid for the lower-arity form.
- Around line 965-972: Update the name handling in the visible model-name setup
block to obtain the TEXT byte length with sqlite3_value_bytes() and reject any
embedded NUL using memchr() after sqlite3_value_text(); set a
PREDICT_ERR_OPTIONS error formatted as "PREDICT_ERR_OPTIONS: ..." and return
before C-string consumers such as the registration/comparison paths. Add a
regression test that binds TEXT containing an embedded NUL and verifies the
operation returns that error rather than truncating the model name.

In `@README.md`:
- Line 162: Update the fit(...) API description in the README to explicitly
state that the leading optional argument is a TEXT model name, while register is
a key in the trailing JSON options object; replace the ambiguous “leading id (or
register)” wording and keep the native tabular student/blob behavior clear for
first-time readers.

In `@tests/test_fit.py`:
- Around line 38-48: Add a negative aggregate test near
test_fit_leading_name_rhymes_with_predict using a CASE expression over tenure as
the leading TEXT argument so different rows produce different model names.
Execute the query through the aggregate prediction path and assert the result is
PREDICT_ERR_OPTIONS, covering the group-consistency contract rather than a
constant-name happy path.

In `@website/src/content/docs/reference/functions.md`:
- Around line 70-72: Update the descriptions in
website/src/content/docs/reference/functions.md lines 70-72 and
website/src/content/docs/reference/options.md lines 51-53 so both consistently
identify label as the last training-data argument before optional options, while
keeping each document standalone for first-time readers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc714660-b43a-495c-b31b-ecaf7f3be81c

📥 Commits

Reviewing files that changed from the base of the PR and between dd6f6be and 2e30518.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • README.md
  • predict-tabular.c
  • tests/test_fit.py
  • website/src/content/docs/guides/operations.md
  • website/src/content/docs/reference/functions.md
  • website/src/content/docs/reference/options.md

Comment thread predict-tabular.c Outdated
Comment thread predict-tabular.c
Comment thread README.md Outdated
Comment thread tests/test_fit.py
Comment thread website/src/content/docs/reference/functions.md
- Reject a model name with an embedded NUL byte (PREDICT_ERR_OPTIONS): it would
  otherwise be truncated by the mprintf/strcmp path and register a different id
  than the caller passed. Adds a char(97,0,98) regression test.
- Add an adversarial test that a leading name varying within an aggregate group
  (a CASE over a column) fails loud, exercising the group-constancy contract.
- Correct the header comment: a trailing '{...}' is options only once a feature
  and a label precede it (argc - has_name >= 3); at the lowest arity it is the
  label.
- Docs: state both registration forms (leading id argument or the register
  option) explicitly, and describe the label as the last argument before the
  optional options.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mstrathman
mstrathman merged commit edb743f into main Aug 3, 2026
17 checks passed
@mstrathman
mstrathman deleted the feat/fit-leading-name branch August 3, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant