diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index 5ee58835..e5f4cdd4 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -5,7 +5,7 @@ import { ThemeProvider as MuiThemeProvider, CssBaseline } from '@mui/material'; import { store } from '../shared/state/store'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { GET_SETTINGS } from '@/shared/backend-bridge/apps/settings'; -// import { fetchModels } from '@/shared/state/modelsSlice'; +import { SUBSCRIPTIONS_STATUS } from '@/shared/backend-bridge/apps/subscriptions'; import { setAppVersion, setUpdateAvailable, @@ -40,7 +40,7 @@ const SettingsLoader: React.FC<{ children: React.ReactNode }> = ({ children }) = const loaded = useAppSelector((s) => s.settings.loaded); useEffect(() => { dispatch(GET_SETTINGS()); - // dispatch(fetchModels()); + dispatch(SUBSCRIPTIONS_STATUS()); }, [dispatch]); useEffect(() => { if (loaded) setThemeMode(theme as 'light' | 'dark'); diff --git a/frontend/src/app/pages/AgentChat/ModelModeSelector.tsx b/frontend/src/app/pages/AgentChat/ModelModeSelector.tsx index 6598ccc4..e18427d5 100644 --- a/frontend/src/app/pages/AgentChat/ModelModeSelector.tsx +++ b/frontend/src/app/pages/AgentChat/ModelModeSelector.tsx @@ -52,10 +52,10 @@ interface Props { } const ModelModeSelector: React.FC = ({ - mode, onModeChange, model, onModelChange, provider, onProviderChange, + mode, onModeChange, model, onModelChange, onProviderChange, contextEstimate, ownerId, sessionId, autoRunMode, hasContent, isRunning, disabled, onSend, onStop, - browseAndAttachFiles, queueLength = 0, + browseAndAttachFiles, }) => { const c = useClaudeTokens(); const dispatch = useAppDispatch(); diff --git a/frontend/src/app/pages/Settings/hooks/useSettings.ts b/frontend/src/app/pages/Settings/hooks/useSettings.ts index 6e56abb9..3aafd6dd 100644 --- a/frontend/src/app/pages/Settings/hooks/useSettings.ts +++ b/frontend/src/app/pages/Settings/hooks/useSettings.ts @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { UPDATE_SETTINGS, AppSettings } from '@/shared/backend-bridge/apps/settings'; import { closeSettingsModal } from '@/shared/state/settingsSlice'; -// import { fetchModels } from '@/shared/state/modelsSlice'; +import { SUBSCRIPTIONS_STATUS } from '@/shared/backend-bridge/apps/subscriptions'; import { setChecking, setUpdateError } from '@/shared/state/updateSlice'; import { LIST_MODES } from '@/shared/state/modesSlice'; import { useClaudeTokens, useThemeMode } from '@/shared/styles/ThemeContext'; @@ -35,7 +35,7 @@ export function useSettings() { const handleSave = async () => { await dispatch(UPDATE_SETTINGS(form)); if (form.theme !== settings.theme) setThemeMode(form.theme); - // dispatch(fetchModels()); + dispatch(SUBSCRIPTIONS_STATUS()); setSaved(true); }; const handleRequestClose = useCallback(() => { @@ -50,7 +50,7 @@ export function useSettings() { const handleSaveAndClose = useCallback(async () => { await dispatch(UPDATE_SETTINGS(form)); if (form.theme !== settings.theme) setThemeMode(form.theme); - // dispatch(fetchModels()); + dispatch(SUBSCRIPTIONS_STATUS()); setSaved(true); setConfirmDiscard(false); dispatch(closeSettingsModal()); diff --git a/frontend/src/shared/state/modelsSlice.ts b/frontend/src/shared/state/modelsSlice.ts new file mode 100644 index 00000000..91d74f3e --- /dev/null +++ b/frontend/src/shared/state/modelsSlice.ts @@ -0,0 +1,50 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { SUBSCRIPTIONS_STATUS } from '@/shared/backend-bridge/apps/subscriptions'; + +interface ModelEntry { + value: string; + label: string; + context_window: number; + provider: string; +} + +interface ModelsState { + byProvider: Record; + loading: boolean; + loaded: boolean; +} + +const initialState: ModelsState = { byProvider: {}, loading: false, loaded: false }; + +function groupByProvider(raw: any[]): Record { + const grouped: Record = {}; + for (const m of raw) { + const entry: ModelEntry = { + value: m.value ?? m.id ?? '', + label: m.label ?? m.value ?? '', + context_window: m.context_window ?? 200_000, + provider: m.provider ?? 'Unknown', + }; + const key = entry.provider.charAt(0).toUpperCase() + entry.provider.slice(1); + (grouped[key] ??= []).push(entry); + } + return grouped; +} + +const modelsSlice = createSlice({ + name: 'models', + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(SUBSCRIPTIONS_STATUS.pending, (state) => { state.loading = true; }) + .addCase(SUBSCRIPTIONS_STATUS.fulfilled, (state, action) => { + state.loading = false; + state.loaded = true; + state.byProvider = groupByProvider((action.payload as any).models ?? []); + }) + .addCase(SUBSCRIPTIONS_STATUS.rejected, (state) => { state.loading = false; state.loaded = true; }); + }, +}); + +export default modelsSlice.reducer; diff --git a/frontend/src/shared/state/store.ts b/frontend/src/shared/state/store.ts index 0386e7ae..3790cc0c 100644 --- a/frontend/src/shared/state/store.ts +++ b/frontend/src/shared/state/store.ts @@ -11,7 +11,7 @@ import dashboardLayoutReducer from './dashboardLayoutSlice'; import dashboardsReducer from './dashboardsSlice'; import appsReducer from './appsSlice'; import updateReducer from './updateSlice'; -// import modelsReducer from './modelsSlice'; +import modelsReducer from './modelsSlice'; export const store = configureStore({ reducer: { @@ -27,7 +27,7 @@ export const store = configureStore({ dashboards: dashboardsReducer, apps: appsReducer, update: updateReducer, - // models: modelsReducer, + models: modelsReducer, }, });