Skip to content

Commit 1a67a81

Browse files
authored
feat(frontend): filter models by team protocol and auto-select default (#211)
- Filter model list based on team's agent_type (agno -> openai, claude -> claude) - Auto-select "Default" model when all bots in team have predefined models - Reset model selection when team changes and current model is incompatible - Improve model selection UX when switching between different team types Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>
1 parent cba6cf3 commit 1a67a81

File tree

1 file changed

+48
-11
lines changed

1 file changed

+48
-11
lines changed

frontend/src/features/tasks/components/ModelSelector.tsx

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ export default function ModelSelector({
116116
const showDefaultOption = useMemo(() => {
117117
return allBotsHavePredefinedModel(selectedTeam);
118118
}, [selectedTeam]);
119+
120+
// Get compatible provider based on team agent_type
121+
// agent_type 'agno' -> provider 'openai', agent_type 'claude'/'claudecode' -> provider 'claude'
122+
const compatibleProvider = useMemo((): string | null => {
123+
if (!selectedTeam?.agent_type) return null;
124+
const agentType = selectedTeam.agent_type.toLowerCase();
125+
if (agentType === 'agno') return 'openai';
126+
if (agentType === 'claude' || agentType === 'claudecode') return 'claude';
127+
return null;
128+
}, [selectedTeam?.agent_type]);
129+
119130
// Fetch all models using unified API
120131
const fetchModels = useCallback(async () => {
121132
setIsLoading(true);
@@ -133,14 +144,43 @@ export default function ModelSelector({
133144
}
134145
}, [t]);
135146

147+
// Filter models by compatible provider when team is selected
148+
const filteredModels = useMemo(() => {
149+
if (!compatibleProvider) return models;
150+
return models.filter(model => model.provider === compatibleProvider);
151+
}, [models, compatibleProvider]);
152+
153+
// Reset selected model when team changes and current selection is not compatible
154+
useEffect(() => {
155+
if (selectedModel && selectedModel.name !== DEFAULT_MODEL_NAME && compatibleProvider) {
156+
// Check if current selected model is still in filtered list
157+
const isStillCompatible = filteredModels.some(
158+
m => m.name === selectedModel.name && m.type === selectedModel.type
159+
);
160+
if (!isStillCompatible) {
161+
// Reset selection - will be handled by the next useEffect
162+
setSelectedModel(null);
163+
}
164+
}
165+
}, [compatibleProvider, filteredModels, selectedModel, setSelectedModel]);
166+
136167
// Load models on mount
137168
useEffect(() => {
138169
fetchModels();
139170
}, [fetchModels]);
140171

141172
// Restore last selected model from localStorage or set default
142173
useEffect(() => {
143-
if (models.length > 0 && !selectedModel) {
174+
// When team changes or all bots have predefined models, auto-select default
175+
if (showDefaultOption) {
176+
// If all bots have predefined models, auto-select "Default"
177+
if (!selectedModel || selectedModel.name !== DEFAULT_MODEL_NAME) {
178+
setSelectedModel({ name: DEFAULT_MODEL_NAME, provider: '', modelId: '' });
179+
}
180+
return;
181+
}
182+
183+
if (filteredModels.length > 0 && !selectedModel) {
144184
const lastSelectedId = localStorage.getItem(LAST_SELECTED_MODEL_KEY);
145185
const lastSelectedType = localStorage.getItem(
146186
LAST_SELECTED_MODEL_TYPE_KEY
@@ -151,8 +191,8 @@ export default function ModelSelector({
151191
setSelectedModel({ name: DEFAULT_MODEL_NAME, provider: '', modelId: '' });
152192
return;
153193
}
154-
// Find model by name and type (if type was saved)
155-
const foundModel = models.find(m => {
194+
// Find model by name and type (if type was saved) in filtered list
195+
const foundModel = filteredModels.find(m => {
156196
if (lastSelectedType) {
157197
return m.name === lastSelectedId && m.type === lastSelectedType;
158198
}
@@ -163,12 +203,9 @@ export default function ModelSelector({
163203
return;
164204
}
165205
}
166-
// If showDefaultOption is true and no previous selection, default to "Default"
167-
if (showDefaultOption) {
168-
setSelectedModel({ name: DEFAULT_MODEL_NAME, provider: '', modelId: '' });
169-
}
206+
// No previous selection and no default option, leave unselected
170207
}
171-
}, [models, selectedModel, setSelectedModel, showDefaultOption]);
208+
}, [filteredModels, selectedModel, setSelectedModel, showDefaultOption]);
172209

173210
// Save selected model to localStorage
174211
useEffect(() => {
@@ -195,7 +232,7 @@ export default function ModelSelector({
195232
}
196233
// Parse value format: "modelName:modelType"
197234
const [modelName, modelType] = value.split(':');
198-
const model = models.find(m => m.name === modelName && m.type === modelType);
235+
const model = filteredModels.find(m => m.name === modelName && m.type === modelType);
199236
if (model) {
200237
setSelectedModel(model);
201238
}
@@ -284,7 +321,7 @@ export default function ModelSelector({
284321
<CommandList className="max-h-[300px] overflow-y-auto">
285322
{error ? (
286323
<div className="py-4 px-3 text-center text-sm text-error">{error}</div>
287-
) : models.length === 0 ? (
324+
) : filteredModels.length === 0 ? (
288325
<CommandEmpty className="py-4 text-center text-sm text-text-muted">
289326
{isLoading ? 'Loading...' : t('models.no_models')}
290327
</CommandEmpty>
@@ -330,7 +367,7 @@ export default function ModelSelector({
330367
</div>
331368
</CommandItem>
332369
)}
333-
{models.map(model => (
370+
{filteredModels.map(model => (
334371
<CommandItem
335372
key={getModelKey(model)}
336373
value={`${model.name} ${model.provider} ${model.modelId} ${model.type}`}

0 commit comments

Comments
 (0)