service.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. import logging
  2. from collections.abc import Mapping
  3. from typing import Any
  4. import yaml
  5. from packaging import version
  6. from core.helper import ssrf_proxy
  7. from core.model_runtime.utils.encoders import jsonable_encoder
  8. from core.plugin.entities.plugin import PluginDependency
  9. from core.workflow.nodes.enums import NodeType
  10. from core.workflow.nodes.knowledge_retrieval.entities import KnowledgeRetrievalNodeData
  11. from core.workflow.nodes.llm.entities import LLMNodeData
  12. from core.workflow.nodes.parameter_extractor.entities import ParameterExtractorNodeData
  13. from core.workflow.nodes.question_classifier.entities import QuestionClassifierNodeData
  14. from core.workflow.nodes.tool.entities import ToolNodeData
  15. from events.app_event import app_model_config_was_updated, app_was_created
  16. from extensions.ext_database import db
  17. from factories import variable_factory
  18. from models.account import Account
  19. from models.model import App, AppMode, AppModelConfig
  20. from models.workflow import Workflow
  21. from services.plugin.dependencies_analysis import DependenciesAnalysisService
  22. from services.workflow_service import WorkflowService
  23. from .exc import (
  24. ContentDecodingError,
  25. EmptyContentError,
  26. FileSizeLimitExceededError,
  27. InvalidAppModeError,
  28. InvalidYAMLFormatError,
  29. MissingAppDataError,
  30. MissingModelConfigError,
  31. MissingWorkflowDataError,
  32. )
  33. logger = logging.getLogger(__name__)
  34. current_dsl_version = "0.1.3"
  35. dsl_max_size = 10 * 1024 * 1024 # 10MB
  36. class AppDslService:
  37. @classmethod
  38. def import_and_create_new_app_from_url(cls, tenant_id: str, url: str, args: dict, account: Account) -> App:
  39. """
  40. Import app dsl from url and create new app
  41. :param tenant_id: tenant id
  42. :param url: import url
  43. :param args: request args
  44. :param account: Account instance
  45. """
  46. response = ssrf_proxy.get(url.strip(), follow_redirects=True, timeout=(10, 10))
  47. response.raise_for_status()
  48. content = response.content
  49. if len(content) > dsl_max_size:
  50. raise FileSizeLimitExceededError("File size exceeds the limit of 10MB")
  51. if not content:
  52. raise EmptyContentError("Empty content from url")
  53. try:
  54. data = content.decode("utf-8")
  55. except UnicodeDecodeError as e:
  56. raise ContentDecodingError(f"Error decoding content: {e}")
  57. return cls.import_and_create_new_app(tenant_id, data, args, account)
  58. @classmethod
  59. def check_dependencies_from_url(cls, tenant_id: str, url: str, account: Account) -> list[PluginDependency]:
  60. """
  61. Check dependencies from url
  62. """
  63. response = ssrf_proxy.get(url.strip(), follow_redirects=True, timeout=(10, 10))
  64. response.raise_for_status()
  65. content = response.content
  66. if len(content) > dsl_max_size:
  67. raise FileSizeLimitExceededError("File size exceeds the limit of 10MB")
  68. try:
  69. data = content.decode("utf-8")
  70. except UnicodeDecodeError as e:
  71. raise ContentDecodingError(f"Error decoding content: {e}")
  72. return cls.check_dependencies(tenant_id, data, account)
  73. @classmethod
  74. def check_dependencies(cls, tenant_id: str, data: str, account: Account) -> list[PluginDependency]:
  75. """
  76. Returns the leaked dependencies in current workspace
  77. """
  78. try:
  79. import_data = yaml.safe_load(data) or {}
  80. except yaml.YAMLError:
  81. raise InvalidYAMLFormatError("Invalid YAML format in data argument.")
  82. dependencies = [PluginDependency(**dep) for dep in import_data.get("dependencies", [])]
  83. if not dependencies:
  84. return []
  85. return DependenciesAnalysisService.check_dependencies(tenant_id=tenant_id, dependencies=dependencies)
  86. @classmethod
  87. def import_and_create_new_app(cls, tenant_id: str, data: str, args: dict, account: Account) -> App:
  88. """
  89. Import app dsl and create new app
  90. :param tenant_id: tenant id
  91. :param data: import data
  92. :param args: request args
  93. :param account: Account instance
  94. """
  95. try:
  96. import_data = yaml.safe_load(data)
  97. except yaml.YAMLError:
  98. raise InvalidYAMLFormatError("Invalid YAML format in data argument.")
  99. # check or repair dsl version
  100. import_data = _check_or_fix_dsl(import_data)
  101. app_data = import_data.get("app")
  102. if not app_data:
  103. raise MissingAppDataError("Missing app in data argument")
  104. # get app basic info
  105. name = args.get("name") or app_data.get("name")
  106. description = args.get("description") or app_data.get("description", "")
  107. icon_type = args.get("icon_type") or app_data.get("icon_type")
  108. icon = args.get("icon") or app_data.get("icon")
  109. icon_background = args.get("icon_background") or app_data.get("icon_background")
  110. use_icon_as_answer_icon = app_data.get("use_icon_as_answer_icon", False)
  111. # import dsl and create app
  112. app_mode = AppMode.value_of(app_data.get("mode"))
  113. if app_mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}:
  114. workflow_data = import_data.get("workflow")
  115. if not workflow_data or not isinstance(workflow_data, dict):
  116. raise MissingWorkflowDataError(
  117. "Missing workflow in data argument when app mode is advanced-chat or workflow"
  118. )
  119. app = cls._import_and_create_new_workflow_based_app(
  120. tenant_id=tenant_id,
  121. app_mode=app_mode,
  122. workflow_data=workflow_data,
  123. account=account,
  124. name=name,
  125. description=description,
  126. icon_type=icon_type,
  127. icon=icon,
  128. icon_background=icon_background,
  129. use_icon_as_answer_icon=use_icon_as_answer_icon,
  130. )
  131. elif app_mode in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.COMPLETION}:
  132. model_config = import_data.get("model_config")
  133. if not model_config or not isinstance(model_config, dict):
  134. raise MissingModelConfigError(
  135. "Missing model_config in data argument when app mode is chat, agent-chat or completion"
  136. )
  137. app = cls._import_and_create_new_model_config_based_app(
  138. tenant_id=tenant_id,
  139. app_mode=app_mode,
  140. model_config_data=model_config,
  141. account=account,
  142. name=name,
  143. description=description,
  144. icon_type=icon_type,
  145. icon=icon,
  146. icon_background=icon_background,
  147. use_icon_as_answer_icon=use_icon_as_answer_icon,
  148. )
  149. else:
  150. raise InvalidAppModeError("Invalid app mode")
  151. return app
  152. @classmethod
  153. def import_and_overwrite_workflow(cls, app_model: App, data: str, account: Account) -> Workflow:
  154. """
  155. Import app dsl and overwrite workflow
  156. :param app_model: App instance
  157. :param data: import data
  158. :param account: Account instance
  159. """
  160. try:
  161. import_data = yaml.safe_load(data)
  162. except yaml.YAMLError:
  163. raise InvalidYAMLFormatError("Invalid YAML format in data argument.")
  164. # check or repair dsl version
  165. import_data = _check_or_fix_dsl(import_data)
  166. app_data = import_data.get("app")
  167. if not app_data:
  168. raise MissingAppDataError("Missing app in data argument")
  169. # import dsl and overwrite app
  170. app_mode = AppMode.value_of(app_data.get("mode"))
  171. if app_mode not in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}:
  172. raise InvalidAppModeError("Only support import workflow in advanced-chat or workflow app.")
  173. if app_data.get("mode") != app_model.mode:
  174. raise ValueError(f"App mode {app_data.get('mode')} is not matched with current app mode {app_mode.value}")
  175. workflow_data = import_data.get("workflow")
  176. if not workflow_data or not isinstance(workflow_data, dict):
  177. raise MissingWorkflowDataError(
  178. "Missing workflow in data argument when app mode is advanced-chat or workflow"
  179. )
  180. return cls._import_and_overwrite_workflow_based_app(
  181. app_model=app_model,
  182. workflow_data=workflow_data,
  183. account=account,
  184. )
  185. @classmethod
  186. def export_dsl(cls, app_model: App, include_secret: bool = False) -> str:
  187. """
  188. Export app
  189. :param app_model: App instance
  190. :return:
  191. """
  192. app_mode = AppMode.value_of(app_model.mode)
  193. export_data = {
  194. "version": current_dsl_version,
  195. "kind": "app",
  196. "app": {
  197. "name": app_model.name,
  198. "mode": app_model.mode,
  199. "icon": "🤖" if app_model.icon_type == "image" else app_model.icon,
  200. "icon_background": "#FFEAD5" if app_model.icon_type == "image" else app_model.icon_background,
  201. "description": app_model.description,
  202. "use_icon_as_answer_icon": app_model.use_icon_as_answer_icon,
  203. },
  204. }
  205. if app_mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}:
  206. cls._append_workflow_export_data(
  207. export_data=export_data, app_model=app_model, include_secret=include_secret
  208. )
  209. else:
  210. cls._append_model_config_export_data(export_data, app_model)
  211. return yaml.dump(export_data, allow_unicode=True)
  212. @classmethod
  213. def _import_and_create_new_workflow_based_app(
  214. cls,
  215. tenant_id: str,
  216. app_mode: AppMode,
  217. workflow_data: Mapping[str, Any],
  218. account: Account,
  219. name: str,
  220. description: str,
  221. icon_type: str,
  222. icon: str,
  223. icon_background: str,
  224. use_icon_as_answer_icon: bool,
  225. ) -> App:
  226. """
  227. Import app dsl and create new workflow based app
  228. :param tenant_id: tenant id
  229. :param app_mode: app mode
  230. :param workflow_data: workflow data
  231. :param account: Account instance
  232. :param name: app name
  233. :param description: app description
  234. :param icon_type: app icon type, "emoji" or "image"
  235. :param icon: app icon
  236. :param icon_background: app icon background
  237. :param use_icon_as_answer_icon: use app icon as answer icon
  238. """
  239. if not workflow_data:
  240. raise MissingWorkflowDataError(
  241. "Missing workflow in data argument when app mode is advanced-chat or workflow"
  242. )
  243. app = cls._create_app(
  244. tenant_id=tenant_id,
  245. app_mode=app_mode,
  246. account=account,
  247. name=name,
  248. description=description,
  249. icon_type=icon_type,
  250. icon=icon,
  251. icon_background=icon_background,
  252. use_icon_as_answer_icon=use_icon_as_answer_icon,
  253. )
  254. # init draft workflow
  255. environment_variables_list = workflow_data.get("environment_variables") or []
  256. environment_variables = [
  257. variable_factory.build_variable_from_mapping(obj) for obj in environment_variables_list
  258. ]
  259. conversation_variables_list = workflow_data.get("conversation_variables") or []
  260. conversation_variables = [
  261. variable_factory.build_variable_from_mapping(obj) for obj in conversation_variables_list
  262. ]
  263. workflow_service = WorkflowService()
  264. draft_workflow = workflow_service.sync_draft_workflow(
  265. app_model=app,
  266. graph=workflow_data.get("graph", {}),
  267. features=workflow_data.get("features", {}),
  268. unique_hash=None,
  269. account=account,
  270. environment_variables=environment_variables,
  271. conversation_variables=conversation_variables,
  272. )
  273. workflow_service.publish_workflow(app_model=app, account=account, draft_workflow=draft_workflow)
  274. return app
  275. @classmethod
  276. def _import_and_overwrite_workflow_based_app(
  277. cls, app_model: App, workflow_data: Mapping[str, Any], account: Account
  278. ) -> Workflow:
  279. """
  280. Import app dsl and overwrite workflow based app
  281. :param app_model: App instance
  282. :param workflow_data: workflow data
  283. :param account: Account instance
  284. """
  285. if not workflow_data:
  286. raise MissingWorkflowDataError(
  287. "Missing workflow in data argument when app mode is advanced-chat or workflow"
  288. )
  289. # fetch draft workflow by app_model
  290. workflow_service = WorkflowService()
  291. current_draft_workflow = workflow_service.get_draft_workflow(app_model=app_model)
  292. if current_draft_workflow:
  293. unique_hash = current_draft_workflow.unique_hash
  294. else:
  295. unique_hash = None
  296. # sync draft workflow
  297. environment_variables_list = workflow_data.get("environment_variables") or []
  298. environment_variables = [
  299. variable_factory.build_variable_from_mapping(obj) for obj in environment_variables_list
  300. ]
  301. conversation_variables_list = workflow_data.get("conversation_variables") or []
  302. conversation_variables = [
  303. variable_factory.build_variable_from_mapping(obj) for obj in conversation_variables_list
  304. ]
  305. draft_workflow = workflow_service.sync_draft_workflow(
  306. app_model=app_model,
  307. graph=workflow_data.get("graph", {}),
  308. features=workflow_data.get("features", {}),
  309. unique_hash=unique_hash,
  310. account=account,
  311. environment_variables=environment_variables,
  312. conversation_variables=conversation_variables,
  313. )
  314. return draft_workflow
  315. @classmethod
  316. def _import_and_create_new_model_config_based_app(
  317. cls,
  318. tenant_id: str,
  319. app_mode: AppMode,
  320. model_config_data: Mapping[str, Any],
  321. account: Account,
  322. name: str,
  323. description: str,
  324. icon_type: str,
  325. icon: str,
  326. icon_background: str,
  327. use_icon_as_answer_icon: bool,
  328. ) -> App:
  329. """
  330. Import app dsl and create new model config based app
  331. :param tenant_id: tenant id
  332. :param app_mode: app mode
  333. :param model_config_data: model config data
  334. :param account: Account instance
  335. :param name: app name
  336. :param description: app description
  337. :param icon: app icon
  338. :param icon_background: app icon background
  339. """
  340. if not model_config_data:
  341. raise MissingModelConfigError(
  342. "Missing model_config in data argument when app mode is chat, agent-chat or completion"
  343. )
  344. app = cls._create_app(
  345. tenant_id=tenant_id,
  346. app_mode=app_mode,
  347. account=account,
  348. name=name,
  349. description=description,
  350. icon_type=icon_type,
  351. icon=icon,
  352. icon_background=icon_background,
  353. use_icon_as_answer_icon=use_icon_as_answer_icon,
  354. )
  355. app_model_config = AppModelConfig()
  356. app_model_config = app_model_config.from_model_config_dict(model_config_data)
  357. app_model_config.app_id = app.id
  358. app_model_config.created_by = account.id
  359. app_model_config.updated_by = account.id
  360. db.session.add(app_model_config)
  361. db.session.commit()
  362. app.app_model_config_id = app_model_config.id
  363. app_model_config_was_updated.send(app, app_model_config=app_model_config)
  364. return app
  365. @classmethod
  366. def _create_app(
  367. cls,
  368. tenant_id: str,
  369. app_mode: AppMode,
  370. account: Account,
  371. name: str,
  372. description: str,
  373. icon_type: str,
  374. icon: str,
  375. icon_background: str,
  376. use_icon_as_answer_icon: bool,
  377. ) -> App:
  378. """
  379. Create new app
  380. :param tenant_id: tenant id
  381. :param app_mode: app mode
  382. :param account: Account instance
  383. :param name: app name
  384. :param description: app description
  385. :param icon_type: app icon type, "emoji" or "image"
  386. :param icon: app icon
  387. :param icon_background: app icon background
  388. :param use_icon_as_answer_icon: use app icon as answer icon
  389. """
  390. app = App(
  391. tenant_id=tenant_id,
  392. mode=app_mode.value,
  393. name=name,
  394. description=description,
  395. icon_type=icon_type,
  396. icon=icon,
  397. icon_background=icon_background,
  398. enable_site=True,
  399. enable_api=True,
  400. use_icon_as_answer_icon=use_icon_as_answer_icon,
  401. created_by=account.id,
  402. updated_by=account.id,
  403. )
  404. db.session.add(app)
  405. db.session.commit()
  406. app_was_created.send(app, account=account)
  407. return app
  408. @classmethod
  409. def _append_workflow_export_data(cls, *, export_data: dict, app_model: App, include_secret: bool) -> None:
  410. """
  411. Append workflow export data
  412. :param export_data: export data
  413. :param app_model: App instance
  414. """
  415. workflow_service = WorkflowService()
  416. workflow = workflow_service.get_draft_workflow(app_model)
  417. if not workflow:
  418. raise ValueError("Missing draft workflow configuration, please check.")
  419. export_data["workflow"] = workflow.to_dict(include_secret=include_secret)
  420. dependencies = cls._extract_dependencies_from_workflow(workflow)
  421. export_data["dependencies"] = [
  422. jsonable_encoder(d.model_dump())
  423. for d in DependenciesAnalysisService.generate_dependencies(
  424. tenant_id=app_model.tenant_id, dependencies=dependencies
  425. )
  426. ]
  427. @classmethod
  428. def _append_model_config_export_data(cls, export_data: dict, app_model: App) -> None:
  429. """
  430. Append model config export data
  431. :param export_data: export data
  432. :param app_model: App instance
  433. """
  434. app_model_config = app_model.app_model_config
  435. if not app_model_config:
  436. raise ValueError("Missing app configuration, please check.")
  437. export_data["model_config"] = app_model_config.to_dict()
  438. dependencies = cls._extract_dependencies_from_model_config(app_model_config)
  439. export_data["dependencies"] = [
  440. jsonable_encoder(d.model_dump())
  441. for d in DependenciesAnalysisService.generate_dependencies(
  442. tenant_id=app_model.tenant_id, dependencies=dependencies
  443. )
  444. ]
  445. @classmethod
  446. def _extract_dependencies_from_workflow(cls, workflow: Workflow) -> list[str]:
  447. """
  448. Extract dependencies from workflow
  449. :param workflow: Workflow instance
  450. :return: dependencies list format like ["langgenius/google"]
  451. """
  452. graph = workflow.graph_dict
  453. dependencies = []
  454. for node in graph.get("nodes", []):
  455. try:
  456. typ = node.get("data", {}).get("type")
  457. match typ:
  458. case NodeType.TOOL.value:
  459. tool_entity = ToolNodeData(**node["data"])
  460. dependencies.append(
  461. DependenciesAnalysisService.analyze_tool_dependency(tool_entity.provider_id),
  462. )
  463. case NodeType.LLM.value:
  464. llm_entity = LLMNodeData(**node["data"])
  465. dependencies.append(
  466. DependenciesAnalysisService.analyze_model_provider_dependency(llm_entity.model.provider),
  467. )
  468. case NodeType.QUESTION_CLASSIFIER.value:
  469. question_classifier_entity = QuestionClassifierNodeData(**node["data"])
  470. dependencies.append(
  471. DependenciesAnalysisService.analyze_model_provider_dependency(
  472. question_classifier_entity.model.provider
  473. ),
  474. )
  475. case NodeType.PARAMETER_EXTRACTOR.value:
  476. parameter_extractor_entity = ParameterExtractorNodeData(**node["data"])
  477. dependencies.append(
  478. DependenciesAnalysisService.analyze_model_provider_dependency(
  479. parameter_extractor_entity.model.provider
  480. ),
  481. )
  482. case NodeType.KNOWLEDGE_RETRIEVAL.value:
  483. knowledge_retrieval_entity = KnowledgeRetrievalNodeData(**node["data"])
  484. if knowledge_retrieval_entity.retrieval_mode == "multiple":
  485. if knowledge_retrieval_entity.multiple_retrieval_config:
  486. if (
  487. knowledge_retrieval_entity.multiple_retrieval_config.reranking_mode
  488. == "reranking_model"
  489. ):
  490. if knowledge_retrieval_entity.multiple_retrieval_config.reranking_model:
  491. dependencies.append(
  492. DependenciesAnalysisService.analyze_model_provider_dependency(
  493. knowledge_retrieval_entity.multiple_retrieval_config.reranking_model.provider
  494. ),
  495. )
  496. elif (
  497. knowledge_retrieval_entity.multiple_retrieval_config.reranking_mode
  498. == "weighted_score"
  499. ):
  500. if knowledge_retrieval_entity.multiple_retrieval_config.weights:
  501. vector_setting = (
  502. knowledge_retrieval_entity.multiple_retrieval_config.weights.vector_setting
  503. )
  504. dependencies.append(
  505. DependenciesAnalysisService.analyze_model_provider_dependency(
  506. vector_setting.embedding_provider_name
  507. ),
  508. )
  509. elif knowledge_retrieval_entity.retrieval_mode == "single":
  510. model_config = knowledge_retrieval_entity.single_retrieval_config
  511. if model_config:
  512. dependencies.append(
  513. DependenciesAnalysisService.analyze_model_provider_dependency(
  514. model_config.model.provider
  515. ),
  516. )
  517. case _:
  518. # Handle default case or unknown node types
  519. pass
  520. except Exception as e:
  521. logger.exception("Error extracting node dependency", exc_info=e)
  522. return dependencies
  523. @classmethod
  524. def _extract_dependencies_from_model_config(cls, model_config: AppModelConfig) -> list[str]:
  525. """
  526. Extract dependencies from model config
  527. :param model_config: AppModelConfig instance
  528. :return: dependencies list format like ["langgenius/google:1.0.0@abcdef1234567890"]
  529. """
  530. dependencies = []
  531. try:
  532. # completion model
  533. model_dict = model_config.model_dict
  534. if model_dict:
  535. dependencies.append(
  536. DependenciesAnalysisService.analyze_model_provider_dependency(model_dict.get("provider"))
  537. )
  538. # reranking model
  539. dataset_configs = model_config.dataset_configs_dict
  540. if dataset_configs:
  541. for dataset_config in dataset_configs:
  542. if dataset_config.get("reranking_model"):
  543. dependencies.append(
  544. DependenciesAnalysisService.analyze_model_provider_dependency(
  545. dataset_config.get("reranking_model", {})
  546. .get("reranking_provider_name", {})
  547. .get("provider")
  548. )
  549. )
  550. # tools
  551. agent_configs = model_config.agent_mode_dict
  552. if agent_configs:
  553. for agent_config in agent_configs:
  554. if agent_config.get("tools"):
  555. for tool in agent_config.get("tools", []):
  556. dependencies.append(
  557. DependenciesAnalysisService.analyze_tool_dependency(tool.get("provider_id"))
  558. )
  559. except Exception as e:
  560. logger.exception("Error extracting model config dependency", exc_info=e)
  561. return dependencies
  562. def _check_or_fix_dsl(import_data: dict[str, Any]) -> Mapping[str, Any]:
  563. """
  564. Check or fix dsl
  565. :param import_data: import data
  566. :raises DSLVersionNotSupportedError: if the imported DSL version is newer than the current version
  567. """
  568. if not import_data.get("version"):
  569. import_data["version"] = "0.1.0"
  570. if not import_data.get("kind") or import_data.get("kind") != "app":
  571. import_data["kind"] = "app"
  572. imported_version = import_data.get("version")
  573. if imported_version != current_dsl_version:
  574. if imported_version and version.parse(imported_version) > version.parse(current_dsl_version):
  575. errmsg = (
  576. f"The imported DSL version {imported_version} is newer than "
  577. f"the current supported version {current_dsl_version}. "
  578. f"Please upgrade your Dify instance to import this configuration."
  579. )
  580. logger.warning(errmsg)
  581. # raise DSLVersionNotSupportedError(errmsg)
  582. else:
  583. logger.warning(
  584. f"DSL version {imported_version} is older than "
  585. f"the current version {current_dsl_version}. "
  586. f"This may cause compatibility issues."
  587. )
  588. return import_data