mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
959c210e86
* chore: stable updater * ✨ feat: add local update testing scripts and configuration - Introduced scripts for local update testing, including setup, server management, and manifest generation. - Added `dev-app-update.local.yml` for local server configuration. - Implemented `generate-manifest.sh` to create update manifests. - Created `run-test.sh` for streamlined testing process. - Updated `README.md` with instructions for local testing setup and usage. - Enhanced `UpdaterManager` to allow forced use of dev update configuration in packaged apps. Signed-off-by: Innei <tukon479@gmail.com> * 🐛 fix(desktop): update UpdaterManager test mocks for new exports Add missing mock exports for @/modules/updater/configs: - isStableChannel - githubConfig - UPDATE_SERVER_URL Add mock for @/env with getDesktopEnv Add setFeedURL method to autoUpdater mock * ✨ feat: add Conductor setup scripts and configuration * ✨ feat: enhance update modal functionality and refactor modal hooks - Added `useUpdateModal` for managing update modal state and behavior. - Refactored `UpdateModal` to utilize new modal management approach. - Improved `useWatchBroadcast` integration for handling update events. - Removed deprecated `createModalHooks` and related components from `FunctionModal`. - Updated `AddFilesToKnowledgeBase` and `CreateNew` modals to use new modal context for closing behavior. This refactor streamlines modal management and enhances the user experience during update processes. Signed-off-by: Innei <tukon479@gmail.com> * update flow (#11513) * ci: simplify desktop release workflow and add renderer tarball * 👷 ci: fix s3 upload credentials for desktop release * 🐛 fix(ci): use compact jq output for GitHub Actions matrix Add -c flag to jq commands to produce single-line JSON output, fixing "Invalid format" error when setting GITHUB_OUTPUT. * 🐛 fix(ci): add administration permission to detect self-hosted runner The /actions/runners API requires administration:read permission to list repository runners. * 🔧 refactor(ci): use workflow input for self-hosted runner selection Replace API-based runner detection with workflow input parameter since GITHUB_TOKEN lacks permission to call /actions/runners API. - Add `use_self_hosted_mac` input (default: true) - Release events always use self-hosted runner - Manual dispatch can toggle via input * feat(updater): add stable channel support with fallback mechanism - Configure electron-builder to generate stable-mac.yml for stable channel - Update CI workflow to handle both stable and latest manifest files - Implement fallback to GitHub provider when primary S3 provider fails - Reset to primary provider after successful update check * 🐛 fix(updater): remove invalid channel config from electron-builder - Remove unsupported 'channel' property from electron-builder config - Create stable*.yml files from latest*.yml in workflow instead - This ensures electron-updater finds correct manifest for stable channel * 🐛 fix(updater): use correct channel based on provider type - S3 provider: channel='stable' → looks for stable-mac.yml - GitHub provider: channel='latest' → looks for latest-mac.yml This fixes the 404 error when falling back to GitHub releases, which only have latest-mac.yml files. * refactor(env): remove unused OFFICIAL_CLOUD_SERVER and update env defaults Update environment variable handling by removing unused OFFICIAL_CLOUD_SERVER and setting defaults for UPDATE_CHANNEL and UPDATE_SERVER_URL from process.env during build stage. * 🐛 fix(ci): add version prefix to stable manifest URLs for S3 S3 directory structure: stable/{version}/xxx.dmg So stable-mac.yml URLs need version prefix: url: LobeHub-2.1.0-arm64.dmg → url: 2.1.1/LobeHub-2.1.0-arm64.dmg * ✨ feat(ci): add renderer tar manifest for integrity verification Creates stable-renderer.yml with SHA512 checksum for lobehub-renderer.tar.gz This allows the desktop app to verify renderer tarball integrity before extraction. * 🐛 fix(ci): fix YAML syntax error in renderer manifest generation * ✨ feat(ci): archive manifest files in version directory * refactor(ci): update desktop release workflows to streamline build process - Removed unnecessary dependencies in the build job for the desktop beta workflow. - Introduced a new gate job to conditionally proceed with publishing based on the success of previous jobs. - Updated macOS file merging to depend on the new gate job instead of the build job. - Simplified macOS runner selection logic in the stable workflow by using GitHub-hosted runners exclusively. Signed-off-by: Innei <tukon479@gmail.com> * refactor(electron): reorganize titlebar components and update imports - Moved titlebar components to a new directory structure for better organization. - Updated import paths for `SimpleTitleBar`, `TitleBar`, and related constants. - Introduced new components for connection management and navigation within the titlebar. - Added constants for title bar height to maintain consistency across components. This refactor enhances the maintainability of the titlebar code and improves the overall structure of the Electron application. Signed-off-by: Innei <tukon479@gmail.com> * feat(ci): add release notes handling to desktop stable workflow - Enhanced the desktop stable release workflow to include release notes. - Updated output variables to capture release notes from the GitHub event. - Adjusted environment variables in subsequent jobs to utilize the new release notes data. This addition improves the clarity and documentation of releases by ensuring that release notes are included in the workflow process. Signed-off-by: Innei <tukon479@gmail.com> * 🐛 fix: call onClose after knowledge base modal closes * 🧪 test: fix UpdaterManager update channel mocks --------- Signed-off-by: Innei <tukon479@gmail.com>
482 lines
16 KiB
TypeScript
482 lines
16 KiB
TypeScript
import { getModelPropertyWithFallback, resolveImageSinglePrice } from '@lobechat/model-runtime';
|
|
import { uniqBy } from 'es-toolkit/compat';
|
|
import {
|
|
type AIImageModelCard,
|
|
type EnabledAiModel,
|
|
type LobeDefaultAiModelListItem,
|
|
type ModelAbilities,
|
|
type ModelParamsSchema,
|
|
type Pricing,
|
|
} from 'model-bank';
|
|
import type { SWRResponse } from 'swr';
|
|
import { type StateCreator } from 'zustand/vanilla';
|
|
|
|
import { mutate, useClientDataSWR } from '@/libs/swr';
|
|
import { aiProviderService } from '@/services/aiProvider';
|
|
import { type AiInfraStore } from '@/store/aiInfra/store';
|
|
import { useUserStore } from '@/store/user';
|
|
import { authSelectors } from '@/store/user/selectors';
|
|
import {
|
|
type AiProviderDetailItem,
|
|
type AiProviderListItem,
|
|
type AiProviderRuntimeState,
|
|
type AiProviderSortMap,
|
|
AiProviderSourceEnum,
|
|
type CreateAiProviderParams,
|
|
type EnabledProvider,
|
|
type EnabledProviderWithModels,
|
|
type UpdateAiProviderConfigParams,
|
|
type UpdateAiProviderParams,
|
|
} from '@/types/aiProvider';
|
|
|
|
export type ProviderModelListItem = {
|
|
abilities: ModelAbilities;
|
|
approximatePricePerImage?: number;
|
|
contextWindowTokens?: number;
|
|
description?: string;
|
|
displayName: string;
|
|
id: string;
|
|
parameters?: ModelParamsSchema;
|
|
pricePerImage?: number;
|
|
pricing?: Pricing;
|
|
releasedAt?: string;
|
|
};
|
|
|
|
type ModelNormalizer = (model: EnabledAiModel) => Promise<ProviderModelListItem>;
|
|
|
|
const dedupeById = (models: ProviderModelListItem[]) => uniqBy(models, 'id');
|
|
|
|
const createProviderModelCollector = (
|
|
type: EnabledAiModel['type'],
|
|
normalizer: ModelNormalizer,
|
|
) => {
|
|
return async (enabledAiModels: EnabledAiModel[], providerId: string) => {
|
|
const filteredModels = enabledAiModels.filter(
|
|
(model) => model.providerId === providerId && model.type === type,
|
|
);
|
|
|
|
if (!filteredModels.length) return [];
|
|
|
|
const normalized = await Promise.all(filteredModels.map((model) => normalizer(model)));
|
|
return dedupeById(normalized);
|
|
};
|
|
};
|
|
|
|
export const normalizeChatModel = (model: EnabledAiModel): ProviderModelListItem => ({
|
|
abilities: (model.abilities || {}) as ModelAbilities,
|
|
contextWindowTokens: model.contextWindowTokens,
|
|
displayName: model.displayName ?? '',
|
|
id: model.id,
|
|
releasedAt: model.releasedAt,
|
|
});
|
|
|
|
export const normalizeImageModel = async (
|
|
model: EnabledAiModel,
|
|
): Promise<ProviderModelListItem> => {
|
|
const fallbackParametersPromise = model.parameters
|
|
? Promise.resolve<ModelParamsSchema | undefined>(model.parameters)
|
|
: getModelPropertyWithFallback<ModelParamsSchema | undefined>(
|
|
model.id,
|
|
'parameters',
|
|
model.providerId,
|
|
);
|
|
|
|
const modelWithPricing = model as AIImageModelCard;
|
|
const fallbackPricingPromise = modelWithPricing.pricing
|
|
? Promise.resolve<Pricing | undefined>(modelWithPricing.pricing)
|
|
: getModelPropertyWithFallback<Pricing | undefined>(model.id, 'pricing', model.providerId);
|
|
|
|
const fallbackDescriptionPromise = getModelPropertyWithFallback<string | undefined>(
|
|
model.id,
|
|
'description',
|
|
model.providerId,
|
|
);
|
|
|
|
const [fallbackParameters, fallbackPricing, fallbackDescription] = await Promise.all([
|
|
fallbackParametersPromise,
|
|
fallbackPricingPromise,
|
|
fallbackDescriptionPromise,
|
|
]);
|
|
|
|
const parameters = model.parameters ?? fallbackParameters;
|
|
const pricing = fallbackPricing;
|
|
const description = fallbackDescription;
|
|
const { price, approximatePrice } = resolveImageSinglePrice(pricing);
|
|
|
|
return {
|
|
abilities: (model.abilities || {}) as ModelAbilities,
|
|
contextWindowTokens: model.contextWindowTokens,
|
|
displayName: model.displayName ?? '',
|
|
id: model.id,
|
|
releasedAt: model.releasedAt,
|
|
...(parameters && { parameters }),
|
|
...(description && { description }),
|
|
...(pricing && { pricing }),
|
|
...(typeof approximatePrice === 'number' && { approximatePricePerImage: approximatePrice }),
|
|
...(typeof price === 'number' && { pricePerImage: price }),
|
|
};
|
|
};
|
|
|
|
export const getChatModelList = createProviderModelCollector('chat', async (model) =>
|
|
normalizeChatModel(model),
|
|
);
|
|
|
|
export const getImageModelList = createProviderModelCollector('image', normalizeImageModel);
|
|
|
|
const buildProviderModelLists = async (
|
|
providers: EnabledProvider[],
|
|
enabledAiModels: EnabledAiModel[],
|
|
collector: (
|
|
enabledAiModels: EnabledAiModel[],
|
|
providerId: string,
|
|
) => Promise<ProviderModelListItem[]>,
|
|
) => {
|
|
return Promise.all(
|
|
providers.map(async (provider) => ({
|
|
...provider,
|
|
children: await collector(enabledAiModels, provider.id),
|
|
name: provider.name || provider.id,
|
|
})),
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Build image provider model lists with proper async handling
|
|
*/
|
|
const buildImageProviderModelLists = async (
|
|
providers: EnabledProvider[],
|
|
enabledAiModels: EnabledAiModel[],
|
|
) => buildProviderModelLists(providers, enabledAiModels, getImageModelList);
|
|
|
|
/**
|
|
* Build chat provider model lists with proper async handling
|
|
*/
|
|
const buildChatProviderModelLists = async (
|
|
providers: EnabledProvider[],
|
|
enabledAiModels: EnabledAiModel[],
|
|
) => buildProviderModelLists(providers, enabledAiModels, getChatModelList);
|
|
|
|
enum AiProviderSwrKey {
|
|
fetchAiProviderItem = 'FETCH_AI_PROVIDER_ITEM',
|
|
fetchAiProviderList = 'FETCH_AI_PROVIDER',
|
|
fetchAiProviderRuntimeState = 'FETCH_AI_PROVIDER_RUNTIME_STATE',
|
|
}
|
|
|
|
type AiProviderRuntimeStateWithBuiltinModels = AiProviderRuntimeState & {
|
|
builtinAiModelList: LobeDefaultAiModelListItem[];
|
|
enabledChatModelList?: EnabledProviderWithModels[];
|
|
enabledImageModelList?: EnabledProviderWithModels[];
|
|
};
|
|
|
|
export interface AiProviderAction {
|
|
createNewAiProvider: (params: CreateAiProviderParams) => Promise<void>;
|
|
deleteAiProvider: (id: string) => Promise<void>;
|
|
internal_toggleAiProviderConfigUpdating: (id: string, loading: boolean) => void;
|
|
internal_toggleAiProviderLoading: (id: string, loading: boolean) => void;
|
|
refreshAiProviderDetail: () => Promise<void>;
|
|
refreshAiProviderList: () => Promise<void>;
|
|
refreshAiProviderRuntimeState: () => Promise<void>;
|
|
removeAiProvider: (id: string) => Promise<void>;
|
|
toggleProviderEnabled: (id: string, enabled: boolean) => Promise<void>;
|
|
updateAiProvider: (id: string, value: UpdateAiProviderParams) => Promise<void>;
|
|
updateAiProviderConfig: (id: string, value: UpdateAiProviderConfigParams) => Promise<void>;
|
|
updateAiProviderSort: (items: AiProviderSortMap[]) => Promise<void>;
|
|
|
|
useFetchAiProviderItem: (id: string) => SWRResponse<AiProviderDetailItem | undefined>;
|
|
useFetchAiProviderList: (params?: {
|
|
enabled?: boolean;
|
|
suspense?: boolean;
|
|
}) => SWRResponse<AiProviderListItem[]>;
|
|
/**
|
|
* fetch provider keyVaults and user enabled model list
|
|
* @param isLoginOnInit
|
|
*/
|
|
useFetchAiProviderRuntimeState: (
|
|
isLoginOnInit: boolean | undefined,
|
|
isSyncActive?: boolean,
|
|
) => SWRResponse<AiProviderRuntimeStateWithBuiltinModels | undefined>;
|
|
}
|
|
|
|
export const createAiProviderSlice: StateCreator<
|
|
AiInfraStore,
|
|
[['zustand/devtools', never]],
|
|
[],
|
|
AiProviderAction
|
|
> = (set, get) => ({
|
|
createNewAiProvider: async (params) => {
|
|
await aiProviderService.createAiProvider({ ...params, source: AiProviderSourceEnum.Custom });
|
|
await get().refreshAiProviderList();
|
|
},
|
|
deleteAiProvider: async (id: string) => {
|
|
await aiProviderService.deleteAiProvider(id);
|
|
|
|
await get().refreshAiProviderList();
|
|
},
|
|
internal_toggleAiProviderConfigUpdating: (id, loading) => {
|
|
set(
|
|
(state) => {
|
|
if (loading)
|
|
return { aiProviderConfigUpdatingIds: [...state.aiProviderConfigUpdatingIds, id] };
|
|
|
|
return {
|
|
aiProviderConfigUpdatingIds: state.aiProviderConfigUpdatingIds.filter((i) => i !== id),
|
|
};
|
|
},
|
|
false,
|
|
'toggleAiProviderLoading',
|
|
);
|
|
},
|
|
internal_toggleAiProviderLoading: (id, loading) => {
|
|
set(
|
|
(state) => {
|
|
if (loading) return { aiProviderLoadingIds: [...state.aiProviderLoadingIds, id] };
|
|
|
|
return { aiProviderLoadingIds: state.aiProviderLoadingIds.filter((i) => i !== id) };
|
|
},
|
|
false,
|
|
'toggleAiProviderLoading',
|
|
);
|
|
},
|
|
refreshAiProviderDetail: async () => {
|
|
await mutate([AiProviderSwrKey.fetchAiProviderItem, get().activeAiProvider]);
|
|
await get().refreshAiProviderRuntimeState();
|
|
},
|
|
refreshAiProviderList: async () => {
|
|
await mutate(AiProviderSwrKey.fetchAiProviderList);
|
|
await get().refreshAiProviderRuntimeState();
|
|
},
|
|
refreshAiProviderRuntimeState: async () => {
|
|
await Promise.all([
|
|
mutate([AiProviderSwrKey.fetchAiProviderRuntimeState, true]),
|
|
mutate([AiProviderSwrKey.fetchAiProviderRuntimeState, false]),
|
|
]);
|
|
},
|
|
removeAiProvider: async (id) => {
|
|
await aiProviderService.deleteAiProvider(id);
|
|
await get().refreshAiProviderList();
|
|
},
|
|
|
|
toggleProviderEnabled: async (id: string, enabled: boolean) => {
|
|
get().internal_toggleAiProviderLoading(id, true);
|
|
await aiProviderService.toggleProviderEnabled(id, enabled);
|
|
|
|
// Immediately update local aiProviderList to reflect the change
|
|
// This ensures the switch displays correctly without waiting for SWR refresh
|
|
set(
|
|
(state) => ({
|
|
aiProviderList: state.aiProviderList.map((item) =>
|
|
item.id === id ? { ...item, enabled } : item,
|
|
),
|
|
}),
|
|
false,
|
|
'toggleProviderEnabled/syncEnabled',
|
|
);
|
|
|
|
await get().refreshAiProviderList();
|
|
|
|
get().internal_toggleAiProviderLoading(id, false);
|
|
},
|
|
|
|
updateAiProvider: async (id, value) => {
|
|
get().internal_toggleAiProviderLoading(id, true);
|
|
await aiProviderService.updateAiProvider(id, value);
|
|
await get().refreshAiProviderList();
|
|
await get().refreshAiProviderDetail();
|
|
|
|
get().internal_toggleAiProviderLoading(id, false);
|
|
},
|
|
|
|
updateAiProviderConfig: async (id, value) => {
|
|
get().internal_toggleAiProviderConfigUpdating(id, true);
|
|
await aiProviderService.updateAiProviderConfig(id, value);
|
|
|
|
// Immediately update local state for instant UI feedback
|
|
set(
|
|
(state) => {
|
|
const currentRuntimeConfig = state.aiProviderRuntimeConfig[id];
|
|
const currentDetailConfig = state.aiProviderDetailMap[id];
|
|
|
|
const updates: Partial<typeof currentRuntimeConfig> = {};
|
|
const detailUpdates: Partial<typeof currentDetailConfig> = {};
|
|
|
|
// Update fetchOnClient if changed
|
|
if (typeof value.fetchOnClient !== 'undefined') {
|
|
// Convert null to undefined to match the interface definition
|
|
const fetchOnClientValue = value.fetchOnClient === null ? undefined : value.fetchOnClient;
|
|
updates.fetchOnClient = fetchOnClientValue;
|
|
detailUpdates.fetchOnClient = fetchOnClientValue;
|
|
}
|
|
|
|
// Update config.enableResponseApi if changed
|
|
if (value.config?.enableResponseApi !== undefined && currentRuntimeConfig?.config) {
|
|
updates.config = {
|
|
...currentRuntimeConfig.config,
|
|
enableResponseApi: value.config.enableResponseApi,
|
|
};
|
|
}
|
|
|
|
return {
|
|
// Update detail map for form display
|
|
aiProviderDetailMap:
|
|
currentDetailConfig && Object.keys(detailUpdates).length > 0
|
|
? {
|
|
...state.aiProviderDetailMap,
|
|
[id]: {
|
|
...currentDetailConfig,
|
|
...detailUpdates,
|
|
},
|
|
}
|
|
: state.aiProviderDetailMap,
|
|
// Update runtime config for selectors
|
|
aiProviderRuntimeConfig:
|
|
currentRuntimeConfig && Object.keys(updates).length > 0
|
|
? {
|
|
...state.aiProviderRuntimeConfig,
|
|
[id]: {
|
|
...currentRuntimeConfig,
|
|
...updates,
|
|
},
|
|
}
|
|
: state.aiProviderRuntimeConfig,
|
|
};
|
|
},
|
|
false,
|
|
'updateAiProviderConfig/syncChanges',
|
|
);
|
|
|
|
await get().refreshAiProviderDetail();
|
|
|
|
get().internal_toggleAiProviderConfigUpdating(id, false);
|
|
},
|
|
|
|
updateAiProviderSort: async (items) => {
|
|
await aiProviderService.updateAiProviderOrder(items);
|
|
await get().refreshAiProviderList();
|
|
},
|
|
useFetchAiProviderItem: (id) =>
|
|
useClientDataSWR<AiProviderDetailItem | undefined>(
|
|
[AiProviderSwrKey.fetchAiProviderItem, id],
|
|
() => aiProviderService.getAiProviderById(id),
|
|
{
|
|
onSuccess: (data) => {
|
|
if (!data) return;
|
|
|
|
set(
|
|
(state) => ({
|
|
activeAiProvider: id,
|
|
aiProviderDetailMap: { ...state.aiProviderDetailMap, [id]: data },
|
|
}),
|
|
false,
|
|
'useFetchAiProviderItem',
|
|
);
|
|
},
|
|
},
|
|
),
|
|
useFetchAiProviderList: (opts) =>
|
|
useClientDataSWR<AiProviderListItem[]>(
|
|
opts?.enabled === false ? null : AiProviderSwrKey.fetchAiProviderList,
|
|
() => aiProviderService.getAiProviderList(),
|
|
{
|
|
fallbackData: [],
|
|
onSuccess: (data) => {
|
|
if (!get().initAiProviderList) {
|
|
set(
|
|
{ aiProviderList: data, initAiProviderList: true },
|
|
false,
|
|
'useFetchAiProviderList/init',
|
|
);
|
|
return;
|
|
}
|
|
|
|
set({ aiProviderList: data }, false, 'useFetchAiProviderList/refresh');
|
|
},
|
|
},
|
|
),
|
|
|
|
useFetchAiProviderRuntimeState: (isLogin) => {
|
|
const isAuthLoaded = useUserStore(authSelectors.isLoaded);
|
|
// Only fetch when auth is loaded and login status is explicitly defined (true or false)
|
|
// Prevents unnecessary requests when login state is null/undefined
|
|
const shouldFetch = isAuthLoaded && isLogin !== null && isLogin !== undefined;
|
|
|
|
return useClientDataSWR<AiProviderRuntimeStateWithBuiltinModels | undefined>(
|
|
shouldFetch ? [AiProviderSwrKey.fetchAiProviderRuntimeState, isLogin] : null,
|
|
async ([, isLogin]) => {
|
|
const [{ LOBE_DEFAULT_MODEL_LIST: builtinAiModelList }, { DEFAULT_MODEL_PROVIDER_LIST }] =
|
|
await Promise.all([import('model-bank'), import('model-bank/modelProviders')]);
|
|
|
|
if (isLogin) {
|
|
const data = await aiProviderService.getAiProviderRuntimeState();
|
|
// Build model lists with proper async handling
|
|
const [enabledChatModelList, enabledImageModelList] = await Promise.all([
|
|
buildChatProviderModelLists(data.enabledChatAiProviders, data.enabledAiModels),
|
|
buildImageProviderModelLists(data.enabledImageAiProviders, data.enabledAiModels),
|
|
]);
|
|
|
|
return {
|
|
...data,
|
|
builtinAiModelList,
|
|
enabledChatModelList,
|
|
enabledImageModelList,
|
|
};
|
|
}
|
|
|
|
const enabledAiProviders: EnabledProvider[] = DEFAULT_MODEL_PROVIDER_LIST.filter(
|
|
(provider) => provider.enabled,
|
|
).map((item) => ({ id: item.id, name: item.name, source: AiProviderSourceEnum.Builtin }));
|
|
|
|
const enabledChatAiProviders = enabledAiProviders.filter((provider) => {
|
|
return builtinAiModelList.some(
|
|
(model) => model.providerId === provider.id && model.type === 'chat',
|
|
);
|
|
});
|
|
|
|
const enabledImageAiProviders = enabledAiProviders
|
|
.filter((provider) => {
|
|
return builtinAiModelList.some(
|
|
(model) => model.providerId === provider.id && model.type === 'image',
|
|
);
|
|
})
|
|
.map((item) => ({ id: item.id, name: item.name, source: AiProviderSourceEnum.Builtin }));
|
|
|
|
// Build model lists for non-login state as well
|
|
const enabledAiModels = builtinAiModelList.filter((m) => m.enabled);
|
|
const [enabledChatModelList, enabledImageModelList] = await Promise.all([
|
|
buildChatProviderModelLists(enabledChatAiProviders, enabledAiModels),
|
|
buildImageProviderModelLists(enabledImageAiProviders, enabledAiModels),
|
|
]);
|
|
|
|
return {
|
|
builtinAiModelList,
|
|
enabledAiModels,
|
|
enabledAiProviders,
|
|
enabledChatAiProviders,
|
|
enabledChatModelList,
|
|
enabledImageAiProviders,
|
|
enabledImageModelList,
|
|
runtimeConfig: {},
|
|
};
|
|
},
|
|
{
|
|
onSuccess: (data) => {
|
|
if (!data) return;
|
|
|
|
set(
|
|
{
|
|
aiProviderRuntimeConfig: data.runtimeConfig,
|
|
builtinAiModelList: data.builtinAiModelList,
|
|
enabledAiModels: data.enabledAiModels,
|
|
enabledAiProviders: data.enabledAiProviders,
|
|
enabledChatModelList: data.enabledChatModelList || [],
|
|
enabledImageModelList: data.enabledImageModelList || [],
|
|
isInitAiProviderRuntimeState: true,
|
|
},
|
|
false,
|
|
'useFetchAiProviderRuntimeState',
|
|
);
|
|
},
|
|
},
|
|
);
|
|
},
|
|
});
|