fix: skip inactive shared defaults for music and sound-effect models - #1458
Conversation
There was a problem hiding this comment.
Code Review
This pull request ensures that inactive shared defaults are filtered out by adding a DBModel.is_active check to both get_default_sound_effect_model and get_default_music_model in model_service.py, and introduces a corresponding unit test. The review feedback suggests making the new unit test more robust by explicitly asserting that the query filter was called before checking its arguments, preventing confusing attribute errors if the query structure changes in the future.
The shared-default branches of `get_default_sound_effect_model` and `get_default_music_model` did not filter on `is_active`, unlike the user-default branches directly above them. An inactive shared default therefore still resolved to a model instance that is absent from the audio tool's own registry, so callers fell back to a placeholder model name instead of the real one. Split out of the media-usage metering series (xorbitsai#997) because it is a behaviour change unrelated to metering — it changes which model gets resolved, not how usage is recorded — and reviewers asked for it to be called out on its own. Adds the test coverage that review asked for: the predicate was previously unasserted, so a revert would have gone unnoticed.
4efac34 to
c363ff2
Compare
|
Rebased onto latest The rebase applied cleanly with no conflicts, and the resulting patch is byte-identical to the pre-rebase one — still the same 2 files, +42/-0 ( No API migration was required here. Unlike the other PRs split out of #997, I verified by grep that this branch contains zero references to Checks run locally on the rebased tree: |
rogercloud
left a comment
There was a problem hiding this comment.
PR summary
This PR adds DBModel.is_active to the shared-default queries for sound-effect and music models so they follow the active-only eligibility already enforced by user-default lookups and model registries. This prevents a deactivated shared model from being instantiated outside the active registry, where audio-tool selection can lose the configured identity and produce phantom/placeholder usage attribution.
Blocking: no — recommended event: APPROVE
Update summary
Since the previous review, the branch was rebased onto latest main, and current head c363ff2 is the single fix commit. The actual semantic patch remains the two symmetric active predicates plus the new parametrized test (+42/-0); there is no API or metering migration.
Independent approach verdict: sound
The approach is sound: model eligibility is corrected in model_service before runtime tool instantiation, at the boundary where persisted defaults become provider instances. It adds no persistence or API boundary change (and no metering migration), while preserving visibility, category/ability filtering, fallback behavior, and session ownership; no design-level concern survived.
Prior findings checklist
These are separate canonical roots, not duplicates: the first is a test-diagnostic robustness issue (filter_query.call_args may be absent), while the second is a behavioral/polarity coverage issue (the test never proves that an inactive shared default is skipped). Their causes, impacts, and required remediations differ.
| Canonical root and source evidence | Status | Current verification and context |
|---|---|---|
| 3802029210 (PR inline, NOT FIXED); 4958529477 (duplicate review body, NOT FIXED); 5365058912 (OliverBryant author context) | NOT FIXED — confirmed minor; follow-up required | tests/web/test_model_service.py:731 still dereferences filter_query.call_args.args without first asserting that call_args is non-None. The body-only occurrence is the same root. The author's claim in 5365058912 that removing the predicate makes the test fail addresses predicate-presence coverage only; it does not make this dereference safe. |
| 3683463297 (linked #997 inline, NOT FIXED); 4819750857 (linked #997 review/R13, NOT FIXED); 5101719615 (linked author context); 5365058912 (PR author context) | NOT FIXED — confirmed minor; follow-up required | Linked #997 explicitly requested a behavior case showing an inactive shared default being skipped; review 4819750857 records that same R13 root. The current test at tests/web/test_model_service.py:728-732 only inspects is_active expression metadata on a MagicMock whose .all() is forced to []; it never creates an inactive row or observes the getter/factory result. The linked follow-up says filters and a test were added, but the test still permits an inverted predicate such as DBModel.is_active.is_(False). OliverBryant's 5365058912 claim that removing the predicate fails the test therefore does not answer the polarity or observable-behavior gap. |
No confirmed critical or major issues survived verification; both canonical roots above are confirmed minor follow-ups.
Line-level findings (ordered by severity)
Minor
-
[minor]
tests/web/test_model_service.py:731— Guard the mocked call before iterating its arguments.Impact:
filter_query.call_argsisNoneif the query chain changes, the filter is bypassed, or the getter returns before this mock is called. The test then raisesAttributeError: 'NoneType' object has no attribute 'args'instead of reporting a clear assertion failure, making a future regression harder to diagnose.I saw OliverBryant's 5365058912 reply that removing the
is_activepredicate makes this test fail. I re-checked the current test: that claim covers only the predicate-presence assertion; it does not establish thatfilter_query.call_argsexists, so it does not resolve this separate diagnostic root.Suggested fix: assert the call before reading its arguments:
assert filter_query.call_args is not None, "The query filter was not called." assert any( getattr(condition, "key", None) == "is_active" or getattr(getattr(condition, "left", None), "key", None) == "is_active" for condition in filter_query.call_args.args )
-
[minor]
tests/web/test_model_service.py:728— Assert inactive-default selection behavior, not only expression shape.Impact: this
MagicMocktest hard-codes.all()to return[]regardless of the filter conditions and never creates an inactive shared default, observes the getter result, or checks the model factory. A future polarity regression such asDBModel.is_active.is_(False)would still expose anis_activekey and pass, allowing an inactive model to be instantiated outside the active registry and causing the audio path to lose its configured identity/usage attribution.OliverBryant's 5365058912 reply correctly shows that removing the predicate is caught, but that is weaker than the required contract and does not test polarity or observable selection. This is the same underlying behavior gap raised in linked #997 discussion 3683463297, not a duplicate of the
call_argsdiagnostic finding.Suggested fix: use the existing
db_sessionfixture to create a visible sharedUserDefaultModelwhose relatedModel.is_active=False, parametrize the corresponding factory with the getter, and assert the inactive row is neither instantiated nor returned. For example:result = get_default(user_id=None, db=db_session) assert result is None model_factory.assert_not_called()
Add an eligible active shared/default fallback case where ordering is deterministic and assert that the active model is returned. These checks fail for a removed, inverted, or otherwise ineffective active filter.
Criteria coverage
- Production correctness, security, resource lifecycle, API/compatibility, persistence/migration, error paths, and documentation: no issue found. The predicates align shared-default eligibility with the active registries while visibility, session ownership/closure, provider teardown, and public interfaces remain unchanged.
- Test coverage: the two confirmed minor gaps above are the unchecked
call_argsdereference and the absence of an inactive-row behavior assertion. - Testing: no local tests were run per review policy; CI is green.
Blocking status & recommended decision
Blocking: no. No blocking issues were confirmed. Both remaining findings are minor test-quality follow-ups and do not block merge. Recommended event: APPROVE.
…tered Addresses both review findings on the shared-default active-filter test. Guard `filter_query.call_args` before dereferencing `.args`. If the query chain changes or the filter is never reached, the test now reports a clear assertion failure instead of `AttributeError: 'NoneType' object has no attribute 'args'`. Add a behavioural test that runs the real query against the in-memory SQLite fixture. The existing assertion only checks that some condition names `is_active`, so an inverted predicate such as `DBModel.is_active.is_(False)` satisfied it while still returning a deactivated model. The new test creates a visible shared default whose model is inactive, asserts the getter returns None and never calls the model factory, then flips the row to active and asserts the model is returned -- the active case is what makes an inverted predicate observable rather than vacuously passing. Verified by mutation: inverting the predicate to `is_active.is_(False)` fails the new test for both getters, and removing the predicate entirely fails all four parametrisations.
Split out of the #997 media-usage series as its own change, since it is a
behaviour change unrelated to metering — it changes which model gets
resolved, not how usage is recorded. Reviewers asked for it to be called
out separately rather than riding along.
Independent of the rest of the series — this branches straight off
mainand can merge in any order relative to #1422, #1424, #1425 and#1457.
The bug
The shared-default branches of
get_default_sound_effect_modelandget_default_music_modeldo not filter onis_active, unlike theuser-default branches directly above them. So an inactive shared default
still resolves to a model instance — one that is absent from the audio
tool's own registry, which means callers fall back to a placeholder model
name instead of the real one.
Review feedback addressed
The comment on #997 noted this predicate was new and unasserted by any
test, so a future revert would go unnoticed, and asked for a case
covering an inactive shared default being skipped. Added:
test_audio_generation_shared_default_requires_active_model, whichasserts the
is_activecolumn is among the filter conditions for bothgetters. It follows the existing
test_audio_generation_default_closes_owned_sessionpattern in the sameclass, which already inspects
filter_query.call_args.argsthe same way.Testing
CI. Local runs are not usable in my environment — importing
xagent.core.model.chathangs on this machine, onmainas well as onthis branch — so I am relying on CI rather than claiming a local pass I
did not get.