The-Agency/lib/ui/widgets/chat/models_panel.dart

175 lines
5.3 KiB
Dart

import "package:shadcn_flutter/shadcn_flutter.dart" as shad;
import "package:provider/provider.dart";
import "../../constants.dart";
import "../../providers/session_provider.dart";
import "../../providers/settings_provider.dart";
import "../common/panel_layout.dart";
import "../../../src/local_state.dart";
class ModelsPanel extends shad.StatelessWidget {
const ModelsPanel({super.key});
@override
shad.Widget build(shad.BuildContext context) {
final settings = context.watch<SettingsProvider>();
final currentModel = settings.normalizeModelId(settings.settings.model);
final advisorModel = settings.settings.advisorModel;
final effortLevel = settings.settings.effortLevel;
final advisorEffortLevel = settings.settings.advisorEffortLevel;
return shad.SizedBox(
height: 300,
child: shad.SingleChildScrollView(
child: PanelList(
fields: [
PanelField(
section: "Conversation",
label: const shad.Text("Model"),
child: _modelSelect(
context: context,
value: currentModel,
onChanged: (model) async {
await context.read<SettingsProvider>().updateModel(model);
await context.read<SessionProvider>().updateSessionModel(model);
},
),
),
PanelField(
section: "Conversation",
label: const shad.Text("Reasoning"),
child: _effortSelect(
context: context,
value: effortLevel,
onChanged: (level) async {
await context.read<SettingsProvider>().updateEffortLevel(level ?? "medium");
},
),
),
PanelField(
section: "Advisor",
label: const shad.Text("Model"),
child: _modelSelect(
context: context,
value: advisorModel ?? "",
placeholder: "None",
onChanged: (model) async {
await context.read<SettingsProvider>().updateAdvisorModel(model);
},
),
),
PanelField(
section: "Advisor",
label: const shad.Text("Reasoning"),
child: _effortSelect(
context: context,
value: advisorEffortLevel,
onChanged: (level) async {
await context.read<SettingsProvider>().updateAdvisorEffortLevel(level);
},
),
),
],
),
),
);
}
}
Map<String, List<SelectableAiModel>> _groupedModels() {
final map = <String, List<SelectableAiModel>>{};
for (final m in selectableAiModels) {
map.putIfAbsent(m.group, () => []).add(m);
}
return map;
}
Iterable<MapEntry<String, List<SelectableAiModel>>> _filteredGroups(String query) sync* {
for (final entry in _groupedModels().entries) {
final matched = entry.value
.where((m) => m.label.toLowerCase().contains(query) || m.id.toLowerCase().contains(query))
.toList();
if (matched.isNotEmpty) {
yield MapEntry(entry.key, matched);
} else if (entry.key.toLowerCase().contains(query)) {
yield entry;
}
}
}
shad.Widget _modelSelect({
required shad.BuildContext context,
required String value,
String? placeholder,
required Future<void> Function(String model) onChanged,
}) {
final current = value.isEmpty
? null
: selectableAiModels.firstWhere(
(m) => m.id == value,
orElse: () => SelectableAiModel(group: "", id: value, label: value),
);
return shad.Select<SelectableAiModel>(
itemBuilder: (ctx, item) => _modelOption(item),
onChanged: (v) {
if (v == null) return;
onChanged(v.id);
},
value: current,
placeholder: current != null
? _modelOption(current)
: shad.Text(placeholder ?? "Select...").muted,
popup: shad.SelectPopup.builder(
searchPlaceholder: const shad.Text("Search models"),
builder: (context, searchQuery) {
final groups = searchQuery == null
? _groupedModels().entries
: _filteredGroups(searchQuery.toLowerCase());
return shad.SelectItemList(
children: [
for (final entry in groups)
shad.SelectGroup(
headers: [shad.SelectLabel(child: shad.Text(entry.key).xSmall.muted)],
children: [
for (final m in entry.value)
shad.SelectItemButton(value: m, child: _modelOption(m)),
],
),
],
);
},
),
);
}
shad.Widget _modelOption(SelectableAiModel m) => shad.Text(m.label).xSmall;
shad.Widget _effortSelect({
required shad.BuildContext context,
required String? value,
required Future<void> Function(String? level) onChanged,
}) {
return shad.Select<String>(
itemBuilder: (ctx, item) => shad.Text(item).xSmall,
onChanged: (v) => onChanged(v),
value: value,
placeholder: shad.Text(value ?? "none").xSmall.muted,
popup: shad.SelectPopup(
items: shad.SelectItemList(
children: [
shad.SelectItemButton(value: null, child: shad.Text("none").xSmall),
for (final level in supportedEffortLevels)
shad.SelectItemButton(value: level, child: shad.Text(level).xSmall),
],
),
),
);
}