only enabled ai models for all users
This commit is contained in:
@@ -33,8 +33,6 @@ export const AIModels = () => {
|
|||||||
const piModels = useEnabledPiModels();
|
const piModels = useEnabledPiModels();
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
console.log('[AIModels] enabledModels:', settings.ai?.enabledModels?.length, 'filtered:', piModels.length);
|
|
||||||
|
|
||||||
const [chatModel, setChatModel] = useState<string | null>(settings.chat.defaultModel);
|
const [chatModel, setChatModel] = useState<string | null>(settings.chat.defaultModel);
|
||||||
const [projectModel, setProjectModel] = useState<string | null>(settings.chat.defaultProjectModel);
|
const [projectModel, setProjectModel] = useState<string | null>(settings.chat.defaultProjectModel);
|
||||||
const [taskModel, setTaskModel] = useState<string | null>(settings.tasks.defaultModel);
|
const [taskModel, setTaskModel] = useState<string | null>(settings.tasks.defaultModel);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { createSettingsPanelComponents, type SettingsSectionGroup } from './Sett
|
|||||||
import { useSettings } from 'state/useSettings';
|
import { useSettings } from 'state/useSettings';
|
||||||
import { useUserState } from 'state/useUserState';
|
import { useUserState } from 'state/useUserState';
|
||||||
import { usePiModels, useVisiblePiModels, modelKey, getProviderDisplayName, type ModelOption } from 'state/useModels';
|
import { usePiModels, useVisiblePiModels, modelKey, getProviderDisplayName, type ModelOption } from 'state/useModels';
|
||||||
|
import { useAccessPolicy } from 'state/useAccessPolicy';
|
||||||
import type { UserSettings } from 'state/useSettings';
|
import type { UserSettings } from 'state/useSettings';
|
||||||
import { AIHarnessesSection } from './ServerSettings/AIHarnessesSection';
|
import { AIHarnessesSection } from './ServerSettings/AIHarnessesSection';
|
||||||
import { RUN_COMMAND_CHANNEL, type RunCommandState } from './ServerSettings/run-command-channel';
|
import { RUN_COMMAND_CHANNEL, type RunCommandState } from './ServerSettings/run-command-channel';
|
||||||
@@ -337,11 +338,11 @@ const DropZone = ({ label, children, onDrop }: DropZoneProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ModelVisibilitySection() {
|
function ModelVisibilitySection() {
|
||||||
const { settings, saveSettings } = useSettings();
|
const { policy, savePolicy } = useAccessPolicy();
|
||||||
const piModels = usePiModels();
|
const piModels = usePiModels();
|
||||||
const [activeProvider, setActiveProvider] = useUserState<string>('model-visibility-provider', '');
|
const [activeProvider, setActiveProvider] = useUserState<string>('model-visibility-provider', '');
|
||||||
|
|
||||||
const enabledModels = settings.ai?.enabledModels ?? [];
|
const enabledModels = policy.allowedModels;
|
||||||
|
|
||||||
const providerGroups = useMemo(() => {
|
const providerGroups = useMemo(() => {
|
||||||
const groups: Record<string, { id: string; name: string }[]> = {};
|
const groups: Record<string, { id: string; name: string }[]> = {};
|
||||||
@@ -383,9 +384,9 @@ function ModelVisibilitySection() {
|
|||||||
const enableModel = useCallback(
|
const enableModel = useCallback(
|
||||||
async (key: string) => {
|
async (key: string) => {
|
||||||
if (enabledModels.includes(key)) return;
|
if (enabledModels.includes(key)) return;
|
||||||
await saveSettings({ ...settings, ai: { ...settings.ai, enabledModels: [...enabledModels, key] } });
|
await savePolicy({ allowedModels: [...enabledModels, key] });
|
||||||
},
|
},
|
||||||
[settings, enabledModels, saveSettings],
|
[enabledModels, savePolicy],
|
||||||
);
|
);
|
||||||
|
|
||||||
const disableModel = useCallback(
|
const disableModel = useCallback(
|
||||||
@@ -401,9 +402,9 @@ function ModelVisibilitySection() {
|
|||||||
base = [...base, ...newProviderKeys];
|
base = [...base, ...newProviderKeys];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await saveSettings({ ...settings, ai: { ...settings.ai, enabledModels: base.filter((id) => id !== key) } });
|
await savePolicy({ allowedModels: base.filter((id) => id !== key) });
|
||||||
},
|
},
|
||||||
[settings, enabledModels, piModels, saveSettings],
|
[enabledModels, piModels, savePolicy],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDragStart = useCallback((ev: DragEvent, key: string) => {
|
const onDragStart = useCallback((ev: DragEvent, key: string) => {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ export { useSettings, DEFAULT_SETTINGS } from './useSettings';
|
|||||||
export type { UseSettingsType, UserSettings, UserState } from './useSettings';
|
export type { UseSettingsType, UserSettings, UserState } from './useSettings';
|
||||||
export { useUserState } from './useUserState';
|
export { useUserState } from './useUserState';
|
||||||
export { useWorkspacesState } from './useWorkspacesState';
|
export { useWorkspacesState } from './useWorkspacesState';
|
||||||
export { usePiModels, useVisiblePiModels, modelKey } from './useModels';
|
export { usePiModels, useVisiblePiModels, useEnabledPiModels, modelKey } from './useModels';
|
||||||
export type { ModelOption } from './useModels';
|
export type { ModelOption } from './useModels';
|
||||||
|
export { useAccessPolicy } from './useAccessPolicy';
|
||||||
export { useRecentModels } from './useRecentModels';
|
export { useRecentModels } from './useRecentModels';
|
||||||
export { usePlans } from './usePlans';
|
export { usePlans } from './usePlans';
|
||||||
export { useLandingPage } from './useLandingPage';
|
export { useLandingPage } from './useLandingPage';
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useClient } from 'hooks/useClient';
|
||||||
|
import { useAuth } from 'hooks/useAuth';
|
||||||
|
|
||||||
|
type AccessPolicy = {
|
||||||
|
allowedModels: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const QUERY_KEY = ['ACCESS_POLICY'];
|
||||||
|
|
||||||
|
export function useAccessPolicy() {
|
||||||
|
const client = useClient();
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const { data: policy = { allowedModels: [] } } = useQuery<AccessPolicy>({
|
||||||
|
queryKey: QUERY_KEY,
|
||||||
|
enabled: isAuthenticated,
|
||||||
|
queryFn: () => client.get<AccessPolicy>('/server-settings/pi-mono/access-policy'),
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const savePolicy = useCallback(
|
||||||
|
async (newPolicy: AccessPolicy) => {
|
||||||
|
queryClient.setQueryData(QUERY_KEY, newPolicy);
|
||||||
|
await client.put('/server-settings/pi-mono/access-policy', newPolicy);
|
||||||
|
},
|
||||||
|
[client, queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { policy, savePolicy };
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useClient } from 'hooks/useClient';
|
import { useClient } from 'hooks/useClient';
|
||||||
import { useAuth } from 'hooks/useAuth';
|
import { useAuth } from 'hooks/useAuth';
|
||||||
import { useSettings } from './useSettings';
|
import { useAccessPolicy } from './useAccessPolicy';
|
||||||
import type { ModelOption } from 'officerdev';
|
import type { ModelOption } from 'officerdev';
|
||||||
|
|
||||||
export type { ModelOption };
|
export type { ModelOption };
|
||||||
@@ -48,53 +48,32 @@ export function usePiModels() {
|
|||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Filter models by system-wide access policy. New providers pass through. */
|
||||||
export function useVisiblePiModels() {
|
export function useVisiblePiModels() {
|
||||||
const models = usePiModels();
|
const models = usePiModels();
|
||||||
const { settings } = useSettings();
|
const { policy } = useAccessPolicy();
|
||||||
const enabled = settings.ai?.enabledModels ?? [];
|
const allowed = policy.allowedModels;
|
||||||
const disabledProviders = new Set(settings.ai?.disabledProviders ?? []);
|
|
||||||
|
|
||||||
// First filter out disabled providers
|
if (allowed.length === 0) return models;
|
||||||
const providerFiltered = disabledProviders.size > 0
|
|
||||||
? models.filter((m) => !disabledProviders.has(m.provider))
|
|
||||||
: models;
|
|
||||||
|
|
||||||
// If no enabled models list, show all (after provider filtering)
|
const allowedSet = new Set(allowed);
|
||||||
if (enabled.length === 0) {
|
const allowedProviderSet = new Set(allowed.map((key) => key.split(':')[0]));
|
||||||
return providerFiltered;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all providers from enabled models
|
return models.filter((m) => {
|
||||||
const enabledProviderSet = new Set(
|
const isExplicitlyAllowed = allowedSet.has(modelKey(m));
|
||||||
enabled.map(key => key.split(':')[0]),
|
const isFromNewProvider = !allowedProviderSet.has(m.provider);
|
||||||
);
|
return isExplicitlyAllowed || isFromNewProvider;
|
||||||
|
|
||||||
// Filter to include:
|
|
||||||
// 1. Models that are explicitly enabled
|
|
||||||
// 2. Models from providers that aren't in the enabled list at all (new providers)
|
|
||||||
return providerFiltered.filter((m) => {
|
|
||||||
const isExplicitlyEnabled = enabled.includes(modelKey(m));
|
|
||||||
const isFromNewProvider = !enabledProviderSet.has(m.provider);
|
|
||||||
return isExplicitlyEnabled || isFromNewProvider;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Strict filtering — only explicitly enabled models, no "new provider" passthrough. */
|
/** Strict filtering — only explicitly allowed models, no "new provider" passthrough. */
|
||||||
export function useEnabledPiModels() {
|
export function useEnabledPiModels() {
|
||||||
const models = usePiModels();
|
const models = usePiModels();
|
||||||
const { settings } = useSettings();
|
const { policy } = useAccessPolicy();
|
||||||
const enabled = settings.ai?.enabledModels ?? [];
|
const allowed = policy.allowedModels;
|
||||||
const disabledProviders = new Set(settings.ai?.disabledProviders ?? []);
|
|
||||||
|
|
||||||
const providerFiltered = disabledProviders.size > 0
|
if (allowed.length === 0) return models;
|
||||||
? models.filter((m) => !disabledProviders.has(m.provider))
|
|
||||||
: models;
|
|
||||||
|
|
||||||
if (enabled.length === 0) return providerFiltered;
|
const allowedSet = new Set(allowed);
|
||||||
|
return models.filter((m) => allowedSet.has(modelKey(m)));
|
||||||
// Match by modelKey (new format), model id, or model name (legacy formats)
|
|
||||||
const enabledSet = new Set(enabled);
|
|
||||||
return providerFiltered.filter((m) =>
|
|
||||||
enabledSet.has(modelKey(m)) || enabledSet.has(m.id) || enabledSet.has(m.name),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user