diff --git a/api/core/rag/index_processor/processor/parent_child_index_processor.py b/api/core/rag/index_processor/processor/parent_child_index_processor.py index 3140122081..894b85339a 100644 --- a/api/core/rag/index_processor/processor/parent_child_index_processor.py +++ b/api/core/rag/index_processor/processor/parent_child_index_processor.py @@ -47,6 +47,8 @@ class ParentChildIndexProcessor(BaseIndexProcessor): embedding_model_instance=kwargs.get("embedding_model_instance"), ) for document in documents: + if kwargs.get("preview") and len(all_documents) >= 10: + return all_documents # document clean document_text = CleanProcessor.clean(document.page_content, process_rule) document.page_content = document_text diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index cb0f3dfbc0..4ddb56981e 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -1,5 +1,6 @@ import logging import uuid +from collections.abc import Mapping from enum import StrEnum from typing import Optional from urllib.parse import urlparse @@ -256,6 +257,16 @@ class AppDslService: check_dependencies_pending_data = None if dependencies: check_dependencies_pending_data = [PluginDependency.model_validate(d) for d in dependencies] + elif imported_version <= "0.1.5": + if "workflow" in data: + graph = data.get("workflow", {}).get("graph", {}) + dependencies_list = self._extract_dependencies_from_workflow_graph(graph) + else: + dependencies_list = self._extract_dependencies_from_model_config(data.get("model_config", {})) + + check_dependencies_pending_data = DependenciesAnalysisService.generate_latest_dependencies( + dependencies_list + ) # Create or update app app = self._create_or_update_app( @@ -558,7 +569,7 @@ class AppDslService: raise ValueError("Missing app configuration, please check.") export_data["model_config"] = app_model_config.to_dict() - dependencies = cls._extract_dependencies_from_model_config(app_model_config) + dependencies = cls._extract_dependencies_from_model_config(app_model_config.to_dict()) export_data["dependencies"] = [ jsonable_encoder(d.model_dump()) for d in DependenciesAnalysisService.generate_dependencies( @@ -574,6 +585,16 @@ class AppDslService: :return: dependencies list format like ["langgenius/google"] """ graph = workflow.graph_dict + dependencies = cls._extract_dependencies_from_workflow_graph(graph) + return dependencies + + @classmethod + def _extract_dependencies_from_workflow_graph(cls, graph: Mapping) -> list[str]: + """ + Extract dependencies from workflow graph + :param graph: Workflow graph + :return: dependencies list format like ["langgenius/google"] + """ dependencies = [] for node in graph.get("nodes", []): try: @@ -647,24 +668,24 @@ class AppDslService: return dependencies @classmethod - def _extract_dependencies_from_model_config(cls, model_config: AppModelConfig) -> list[str]: + def _extract_dependencies_from_model_config(cls, model_config: Mapping) -> list[str]: """ Extract dependencies from model config - :param model_config: AppModelConfig instance - :return: dependencies list format like ["langgenius/google:1.0.0@abcdef1234567890"] + :param model_config: model config dict + :return: dependencies list format like ["langgenius/google"] """ dependencies = [] try: # completion model - model_dict = model_config.model_dict + model_dict = model_config.get("model", {}) if model_dict: dependencies.append( DependenciesAnalysisService.analyze_model_provider_dependency(model_dict.get("provider", "")) ) # reranking model - dataset_configs = model_config.dataset_configs_dict + dataset_configs = model_config.get("dataset_configs", {}) if dataset_configs: for dataset_config in dataset_configs.get("datasets", {}).get("datasets", []): if dataset_config.get("reranking_model"): @@ -677,7 +698,7 @@ class AppDslService: ) # tools - agent_configs = model_config.agent_mode_dict + agent_configs = model_config.get("agent_mode", {}) if agent_configs: for agent_config in agent_configs.get("tools", []): dependencies.append( diff --git a/api/services/plugin/dependencies_analysis.py b/api/services/plugin/dependencies_analysis.py index d425483863..df0c321a8b 100644 --- a/api/services/plugin/dependencies_analysis.py +++ b/api/services/plugin/dependencies_analysis.py @@ -1,3 +1,4 @@ +from core.helper import marketplace from core.plugin.entities.plugin import GenericProviderID, PluginDependency, PluginInstallationSource from core.plugin.manager.plugin import PluginInstallationManager @@ -98,3 +99,18 @@ class DependenciesAnalysisService: raise ValueError(f"Unknown plugin source: {plugin.source}") return result + + @classmethod + def generate_latest_dependencies(cls, dependencies: list[str]) -> list[PluginDependency]: + """ + Generate the latest version of dependencies + """ + dependencies = list(set(dependencies)) + deps = marketplace.batch_fetch_plugin_manifests(dependencies) + return [ + PluginDependency( + type=PluginDependency.Type.Marketplace, + value=PluginDependency.Marketplace(marketplace_plugin_unique_identifier=dep.latest_package_identifier), + ) + for dep in deps + ] diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 231df0e9db..48acaeb64a 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -275,6 +275,8 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: category: PluginType.model, exclude, type: 'plugin', + sortBy: 'install_count', + sortOrder: 'DESC', }) } else { @@ -284,6 +286,8 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: type: 'plugin', pageSize: 1000, exclude, + sortBy: 'install_count', + sortOrder: 'DESC', }) } }, [queryPlugins, queryPluginsWithDebounced, searchText, exclude]) diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index f77974abaf..949352a265 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -71,11 +71,11 @@ const ModelProviderPage = ({ searchText }: Props) => { const [filteredConfiguredProviders, filteredNotConfiguredProviders] = useMemo(() => { const filteredConfiguredProviders = configuredProviders.filter( provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase()) - || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())), + || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())), ) const filteredNotConfiguredProviders = notConfiguredProviders.filter( provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase()) - || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())), + || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())), ) return [filteredConfiguredProviders, filteredNotConfiguredProviders] @@ -143,7 +143,7 @@ const ModelProviderPage = ({ searchText }: Props) => { )} {!!filteredNotConfiguredProviders?.length && ( <> -
{t('common.modelProvider.configureRequired')}
+
{t('common.modelProvider.toBeConfigured')}
{filteredNotConfiguredProviders?.map(provider => ( { const res: Record = {} data?.plugins.forEach((plugin) => { res[plugin.plugin_id] = { + installedId: plugin.id, installedVersion: plugin.declaration.version, uniqueIdentifier: plugin.plugin_unique_identifier, } diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx index 48b1ecd325..803be4abd4 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx @@ -26,7 +26,7 @@ const InstallByDSLList: FC = ({ isFromMarketPlace, }) => { // DSL has id, to get plugin info to show more info - const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.plugin_unique_identifier!)) + const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.marketplace_plugin_unique_identifier!)) // has meta(org,name,version), to get id const { isLoading: isFetchingDataByMeta, data: infoByMeta, error: infoByMetaError } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value!)) diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx index 19baa86d73..1deb8d8282 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx @@ -8,8 +8,9 @@ import Button from '@/app/components/base/button' import { Trans, useTranslation } from 'react-i18next' import { RiLoader2Line } from '@remixicon/react' import checkTaskStatus from '../../base/check-task-status' -import { useInstallPackageFromLocal, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' +import { useInstallPackageFromLocal, usePluginTaskList } from '@/service/use-plugins' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' +import { uninstallPlugin } from '@/service/plugins' import Version from '../../base/version' const i18nPrefix = 'plugin.installModal' @@ -50,7 +51,6 @@ const Installed: FC = ({ const [isInstalling, setIsInstalling] = React.useState(false) const { mutateAsync: installPackageFromLocal } = useInstallPackageFromLocal() - const { mutateAsync: updatePackageFromMarketPlace } = useUpdatePackageFromMarketPlace() const { check, @@ -69,27 +69,15 @@ const Installed: FC = ({ onStartToInstall?.() try { - let taskId - let isInstalled - if (hasInstalled) { - const { - all_installed, - task_id, - } = await updatePackageFromMarketPlace({ - original_plugin_unique_identifier: installedInfoPayload.uniqueIdentifier, - new_plugin_unique_identifier: uniqueIdentifier, - }) - taskId = task_id - isInstalled = all_installed - } - else { - const { - all_installed, - task_id, - } = await installPackageFromLocal(uniqueIdentifier) - taskId = task_id - isInstalled = all_installed - } + if (hasInstalled) + await uninstallPlugin(installedInfoPayload.installedId) + + const { + all_installed, + task_id, + } = await installPackageFromLocal(uniqueIdentifier) + const taskId = task_id + const isInstalled = all_installed if (isInstalled) { onInstalled() diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index e9f7884257..9a94f7740c 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -373,6 +373,7 @@ export type VersionListResponse = { } export type VersionInfo = { + installedId: string, // use to uninstall installedVersion: string, uniqueIdentifier: string } diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index bbe80b539c..e961efc696 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -407,7 +407,7 @@ const translation = { loadBalancingLeastKeyWarning: 'To enable load balancing at least 2 keys must be enabled.', loadBalancingInfo: 'By default, load balancing uses the Round-robin strategy. If rate limiting is triggered, a 1-minute cooldown period will be applied.', upgradeForLoadBalancing: 'Upgrade your plan to enable Load Balancing.', - configureRequired: 'Configure required', + toBeConfigured: 'To be configured', configureTip: 'Set up api-key or add model to use', installProvider: 'Install model providers', discoverMore: 'Discover more in ', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 614d3f8528..32d711eb95 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -407,7 +407,7 @@ const translation = { loadBalancingInfo: '默认情况下,负载均衡使用 Round-robin 策略。如果触发速率限制,将应用 1 分钟的冷却时间', upgradeForLoadBalancing: '升级以解锁负载均衡功能', apiKey: 'API 密钥', - configureRequired: '尚未配置', + toBeConfigured: '待配置', configureTip: '请配置 API 密钥,添加模型。', installProvider: '安装模型供应商', discoverMore: '发现更多就在', diff --git a/web/package.json b/web/package.json index caefc8f1a0..87f0427050 100644 --- a/web/package.json +++ b/web/package.json @@ -66,7 +66,7 @@ "js-audio-recorder": "^1.0.7", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", - "katex": "^0.16.11", + "katex": "^0.16.21", "ky": "^1.7.2", "lamejs": "^1.2.1", "lexical": "^0.18.0", @@ -196,4 +196,4 @@ "eslint --fix" ] } -} \ No newline at end of file +} diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 68f8ca2ca9..cd2101df52 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -140,8 +140,8 @@ importers: specifier: ^4.0.0 version: 4.0.0 katex: - specifier: ^0.16.11 - version: 0.16.11 + specifier: ^0.16.21 + version: 0.16.21 ky: specifier: ^1.7.2 version: 1.7.2 @@ -5744,8 +5744,8 @@ packages: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} - katex@0.16.11: - resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} + katex@0.16.21: + resolution: {integrity: sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==} hasBin: true keyv@4.5.4: @@ -15130,7 +15130,7 @@ snapshots: jwt-decode@4.0.0: {} - katex@0.16.11: + katex@0.16.21: dependencies: commander: 8.3.0 @@ -15558,7 +15558,7 @@ snapshots: dagre-d3-es: 7.0.11 dayjs: 1.11.13 dompurify: 3.2.3 - katex: 0.16.11 + katex: 0.16.21 khroma: 2.1.0 lodash-es: 4.17.21 marked: 13.0.3 @@ -15652,7 +15652,7 @@ snapshots: dependencies: '@types/katex': 0.16.7 devlop: 1.1.0 - katex: 0.16.11 + katex: 0.16.21 micromark-factory-space: 2.0.0 micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 @@ -16991,7 +16991,7 @@ snapshots: '@types/katex': 0.16.7 hast-util-from-html-isomorphic: 2.0.0 hast-util-to-text: 4.0.2 - katex: 0.16.11 + katex: 0.16.21 unist-util-visit-parents: 6.0.1 vfile: 6.0.3 diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 2eb99055ad..841e4bfc11 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -29,6 +29,8 @@ import { useQueryClient, } from '@tanstack/react-query' import { useInvalidateAllBuiltInTools } from './use-tools' +import usePermission from '@/app/components/plugins/plugin-page/use-permission' +import { uninstallPlugin } from '@/service/plugins' const NAME_SPACE = 'plugins' @@ -236,10 +238,20 @@ export const useInstallOrUpdate = ({ } } if (isInstalled) { - await updatePackageFromMarketPlace({ - original_plugin_unique_identifier: installedPayload?.uniqueIdentifier, - new_plugin_unique_identifier: uniqueIdentifier, - }) + if (item.type === 'package') { + await uninstallPlugin(installedPayload.installedId) + await post('/workspaces/current/plugin/install/pkg', { + body: { + plugin_unique_identifiers: [uniqueIdentifier], + }, + }) + } + else { + await updatePackageFromMarketPlace({ + original_plugin_unique_identifier: installedPayload?.uniqueIdentifier, + new_plugin_unique_identifier: uniqueIdentifier, + }) + } } return ({ success: true }) } @@ -356,12 +368,16 @@ export const useFetchPluginsInMarketPlaceByInfo = (infos: Record[]) const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList'] export const usePluginTaskList = () => { + const { + canManagement, + } = usePermission() const { data, isFetched, refetch, ...rest } = useQuery({ + enabled: canManagement, queryKey: usePluginTaskListKey, queryFn: () => get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100'), refetchInterval: (lastQuery) => {