mirror of
https://github.com/block/buzz.git
synced 2026-11-13 04:41:12 +00:00
## Summary Builderlab's model catalog can now recommend a model and thinking effort for new agents (`recommended_selection` on `/v3/beekeeper/get-model-catalog`). The mobile create-agent screen now starts on that recommendation, so the default can change on the server without an app release. New agents start on the first of these that applies: 1. The model an agent link suggests, when the catalog offers it (unchanged). 2. The server's recommended model and effort. 3. The first Sonnet model at medium effort, as added in #9034. Used while a deployment sends no recommendation. 4. Blank. A setup resumed after a failure still keeps its saved choice. A recommendation that isn't in the returned catalog, or that is malformed, is ignored. Existing agents' saved models are never changed. The server only sends a recommendation when the deployment's catalog config sets `recommended_selection`. Until then, the app keeps falling back to Sonnet. ## Testing - Unit tests cover the recommendation beating the Sonnet fallback, a draft model beating the recommendation, and recommendations that should be ignored (unsupported effort, unknown model, missing effort, malformed, absent). - A new widget test checks that the form opens on the recommended model and effort instead of Sonnet. - `just mobile-check` and the full mobile suite (`flutter test --dart-define=BUZZ_PUSH_GATEWAY_URL=https://push.example`) pass locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
2.8 KiB
Dart
105 lines
2.8 KiB
Dart
import 'package:buzz/features/agents/agent_model_picker.dart';
|
|
import 'package:buzz/shared/agents/remote_agents.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
const opus = RemoteAgentModel(
|
|
id: 'kgoose-claude-opus-4-6',
|
|
name: 'Opus',
|
|
efforts: ['low', 'medium', 'high'],
|
|
);
|
|
const sonnet = RemoteAgentModel(
|
|
id: 'kgoose-claude-sonnet-4-6',
|
|
name: 'Sonnet',
|
|
efforts: ['low', 'medium', 'high'],
|
|
);
|
|
|
|
test('defaults to Sonnet at medium effort', () {
|
|
expect(startingAgentModel([opus, sonnet]), (
|
|
'kgoose-claude-sonnet-4-6',
|
|
'medium',
|
|
));
|
|
});
|
|
|
|
test('leaves effort for the user when medium is not offered', () {
|
|
const highOnly = RemoteAgentModel(
|
|
id: 'claude-sonnet-5',
|
|
name: 'Sonnet 5',
|
|
efforts: ['high'],
|
|
);
|
|
expect(startingAgentModel([opus, highOnly]), ('claude-sonnet-5', null));
|
|
});
|
|
|
|
test('the service recommendation wins over the Sonnet fallback', () {
|
|
final recommendedOpus = RemoteAgentModel.fromJson(
|
|
{
|
|
'id': opus.id,
|
|
'display_name': opus.name,
|
|
'supported_reasoning_efforts': opus.efforts,
|
|
},
|
|
recommended: {'model_id': opus.id, 'reasoning_effort': 'high'},
|
|
);
|
|
expect(startingAgentModel([recommendedOpus, sonnet]), (opus.id, 'high'));
|
|
expect(
|
|
startingAgentModel(
|
|
[recommendedOpus, sonnet],
|
|
draftModel: sonnet.id,
|
|
draftEffort: 'low',
|
|
),
|
|
(sonnet.id, 'low'),
|
|
);
|
|
});
|
|
|
|
test('ignores a recommendation the model does not offer', () {
|
|
for (final recommended in [
|
|
{'model_id': opus.id, 'reasoning_effort': 'max'},
|
|
{'model_id': 'gone', 'reasoning_effort': 'high'},
|
|
{'model_id': opus.id},
|
|
'opus',
|
|
null,
|
|
]) {
|
|
final model = RemoteAgentModel.fromJson({
|
|
'id': opus.id,
|
|
'display_name': opus.name,
|
|
'supported_reasoning_efforts': opus.efforts,
|
|
}, recommended: recommended);
|
|
expect(model.recommendedEffort, isNull, reason: '$recommended');
|
|
expect(startingAgentModel([model, sonnet]), (sonnet.id, 'medium'));
|
|
}
|
|
});
|
|
|
|
test('chooses nothing when no Sonnet is offered', () {
|
|
expect(startingAgentModel([opus]), (null, null));
|
|
});
|
|
|
|
test('a draft model the catalog offers wins over the default', () {
|
|
expect(
|
|
startingAgentModel(
|
|
[opus, sonnet],
|
|
draftModel: opus.id,
|
|
draftEffort: 'high',
|
|
),
|
|
(opus.id, 'high'),
|
|
);
|
|
expect(
|
|
startingAgentModel(
|
|
[opus, sonnet],
|
|
draftModel: opus.id,
|
|
draftEffort: 'max',
|
|
),
|
|
(opus.id, null),
|
|
);
|
|
});
|
|
|
|
test('a draft model the catalog lacks falls back to the default', () {
|
|
expect(
|
|
startingAgentModel(
|
|
[opus, sonnet],
|
|
draftModel: 'gone',
|
|
draftEffort: 'high',
|
|
),
|
|
(sonnet.id, 'medium'),
|
|
);
|
|
});
|
|
}
|