datasets.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. import flask_restful
  2. from flask import request
  3. from flask_login import current_user
  4. from flask_restful import Resource, marshal, marshal_with, reqparse
  5. from werkzeug.exceptions import Forbidden, NotFound
  6. import services
  7. from configs import dify_config
  8. from controllers.console import api
  9. from controllers.console.apikey import api_key_fields, api_key_list
  10. from controllers.console.app.error import ProviderNotInitializeError
  11. from controllers.console.datasets.error import DatasetInUseError, DatasetNameDuplicateError, IndexingEstimateError
  12. from controllers.console.setup import setup_required
  13. from controllers.console.wraps import account_initialization_required
  14. from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
  15. from core.indexing_runner import IndexingRunner
  16. from core.model_runtime.entities.model_entities import ModelType
  17. from core.provider_manager import ProviderManager
  18. from core.rag.datasource.vdb.vector_type import VectorType
  19. from core.rag.extractor.entity.extract_setting import ExtractSetting
  20. from core.rag.retrieval.retrieval_methods import RetrievalMethod
  21. from extensions.ext_database import db
  22. from fields.app_fields import related_app_list
  23. from fields.dataset_fields import dataset_detail_fields, dataset_query_detail_fields
  24. from fields.document_fields import document_status_fields
  25. from libs.login import login_required
  26. from models.dataset import Dataset, DatasetPermissionEnum, Document, DocumentSegment
  27. from models.model import ApiToken, UploadFile
  28. from services.dataset_service import DatasetPermissionService, DatasetService, DocumentService
  29. def _validate_name(name):
  30. if not name or len(name) < 1 or len(name) > 40:
  31. raise ValueError("Name must be between 1 to 40 characters.")
  32. return name
  33. def _validate_description_length(description):
  34. if len(description) > 400:
  35. raise ValueError("Description cannot exceed 400 characters.")
  36. return description
  37. class DatasetListApi(Resource):
  38. @setup_required
  39. @login_required
  40. @account_initialization_required
  41. def get(self):
  42. page = request.args.get("page", default=1, type=int)
  43. limit = request.args.get("limit", default=20, type=int)
  44. ids = request.args.getlist("ids")
  45. provider = request.args.get("provider", default="vendor")
  46. search = request.args.get("keyword", default=None, type=str)
  47. tag_ids = request.args.getlist("tag_ids")
  48. if ids:
  49. datasets, total = DatasetService.get_datasets_by_ids(ids, current_user.current_tenant_id)
  50. else:
  51. datasets, total = DatasetService.get_datasets(
  52. page, limit, provider, current_user.current_tenant_id, current_user, search, tag_ids
  53. )
  54. # check embedding setting
  55. provider_manager = ProviderManager()
  56. configurations = provider_manager.get_configurations(tenant_id=current_user.current_tenant_id)
  57. embedding_models = configurations.get_models(model_type=ModelType.TEXT_EMBEDDING, only_active=True)
  58. model_names = []
  59. for embedding_model in embedding_models:
  60. model_names.append(f"{embedding_model.model}:{embedding_model.provider.provider}")
  61. data = marshal(datasets, dataset_detail_fields)
  62. for item in data:
  63. if item["indexing_technique"] == "high_quality":
  64. item_model = f"{item['embedding_model']}:{item['embedding_model_provider']}"
  65. if item_model in model_names:
  66. item["embedding_available"] = True
  67. else:
  68. item["embedding_available"] = False
  69. else:
  70. item["embedding_available"] = True
  71. if item.get("permission") == "partial_members":
  72. part_users_list = DatasetPermissionService.get_dataset_partial_member_list(item["id"])
  73. item.update({"partial_member_list": part_users_list})
  74. else:
  75. item.update({"partial_member_list": []})
  76. response = {"data": data, "has_more": len(datasets) == limit, "limit": limit, "total": total, "page": page}
  77. return response, 200
  78. @setup_required
  79. @login_required
  80. @account_initialization_required
  81. def post(self):
  82. parser = reqparse.RequestParser()
  83. parser.add_argument(
  84. "name",
  85. nullable=False,
  86. required=True,
  87. help="type is required. Name must be between 1 to 40 characters.",
  88. type=_validate_name,
  89. )
  90. parser.add_argument(
  91. "indexing_technique",
  92. type=str,
  93. location="json",
  94. choices=Dataset.INDEXING_TECHNIQUE_LIST,
  95. nullable=True,
  96. help="Invalid indexing technique.",
  97. )
  98. args = parser.parse_args()
  99. # The role of the current user in the ta table must be admin, owner, or editor, or dataset_operator
  100. if not current_user.is_dataset_editor:
  101. raise Forbidden()
  102. try:
  103. dataset = DatasetService.create_empty_dataset(
  104. tenant_id=current_user.current_tenant_id,
  105. name=args["name"],
  106. indexing_technique=args["indexing_technique"],
  107. account=current_user,
  108. permission=DatasetPermissionEnum.ONLY_ME,
  109. )
  110. except services.errors.dataset.DatasetNameDuplicateError:
  111. raise DatasetNameDuplicateError()
  112. return marshal(dataset, dataset_detail_fields), 201
  113. class DatasetApi(Resource):
  114. @setup_required
  115. @login_required
  116. @account_initialization_required
  117. def get(self, dataset_id):
  118. dataset_id_str = str(dataset_id)
  119. dataset = DatasetService.get_dataset(dataset_id_str)
  120. if dataset is None:
  121. raise NotFound("Dataset not found.")
  122. try:
  123. DatasetService.check_dataset_permission(dataset, current_user)
  124. except services.errors.account.NoPermissionError as e:
  125. raise Forbidden(str(e))
  126. data = marshal(dataset, dataset_detail_fields)
  127. if data.get("permission") == "partial_members":
  128. part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
  129. data.update({"partial_member_list": part_users_list})
  130. # check embedding setting
  131. provider_manager = ProviderManager()
  132. configurations = provider_manager.get_configurations(tenant_id=current_user.current_tenant_id)
  133. embedding_models = configurations.get_models(model_type=ModelType.TEXT_EMBEDDING, only_active=True)
  134. model_names = []
  135. for embedding_model in embedding_models:
  136. model_names.append(f"{embedding_model.model}:{embedding_model.provider.provider}")
  137. if data["indexing_technique"] == "high_quality":
  138. item_model = f"{data['embedding_model']}:{data['embedding_model_provider']}"
  139. if item_model in model_names:
  140. data["embedding_available"] = True
  141. else:
  142. data["embedding_available"] = False
  143. else:
  144. data["embedding_available"] = True
  145. if data.get("permission") == "partial_members":
  146. part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
  147. data.update({"partial_member_list": part_users_list})
  148. return data, 200
  149. @setup_required
  150. @login_required
  151. @account_initialization_required
  152. def patch(self, dataset_id):
  153. dataset_id_str = str(dataset_id)
  154. dataset = DatasetService.get_dataset(dataset_id_str)
  155. if dataset is None:
  156. raise NotFound("Dataset not found.")
  157. parser = reqparse.RequestParser()
  158. parser.add_argument(
  159. "name",
  160. nullable=False,
  161. help="type is required. Name must be between 1 to 40 characters.",
  162. type=_validate_name,
  163. )
  164. parser.add_argument("description", location="json", store_missing=False, type=_validate_description_length)
  165. parser.add_argument(
  166. "indexing_technique",
  167. type=str,
  168. location="json",
  169. choices=Dataset.INDEXING_TECHNIQUE_LIST,
  170. nullable=True,
  171. help="Invalid indexing technique.",
  172. )
  173. parser.add_argument(
  174. "permission",
  175. type=str,
  176. location="json",
  177. choices=(DatasetPermissionEnum.ONLY_ME, DatasetPermissionEnum.ALL_TEAM, DatasetPermissionEnum.PARTIAL_TEAM),
  178. help="Invalid permission.",
  179. )
  180. parser.add_argument("embedding_model", type=str, location="json", help="Invalid embedding model.")
  181. parser.add_argument(
  182. "embedding_model_provider", type=str, location="json", help="Invalid embedding model provider."
  183. )
  184. parser.add_argument("retrieval_model", type=dict, location="json", help="Invalid retrieval model.")
  185. parser.add_argument("partial_member_list", type=list, location="json", help="Invalid parent user list.")
  186. args = parser.parse_args()
  187. data = request.get_json()
  188. # check embedding model setting
  189. if data.get("indexing_technique") == "high_quality":
  190. DatasetService.check_embedding_model_setting(
  191. dataset.tenant_id, data.get("embedding_model_provider"), data.get("embedding_model")
  192. )
  193. # The role of the current user in the ta table must be admin, owner, editor, or dataset_operator
  194. DatasetPermissionService.check_permission(
  195. current_user, dataset, data.get("permission"), data.get("partial_member_list")
  196. )
  197. dataset = DatasetService.update_dataset(dataset_id_str, args, current_user)
  198. if dataset is None:
  199. raise NotFound("Dataset not found.")
  200. result_data = marshal(dataset, dataset_detail_fields)
  201. tenant_id = current_user.current_tenant_id
  202. if data.get("partial_member_list") and data.get("permission") == "partial_members":
  203. DatasetPermissionService.update_partial_member_list(
  204. tenant_id, dataset_id_str, data.get("partial_member_list")
  205. )
  206. # clear partial member list when permission is only_me or all_team_members
  207. elif (
  208. data.get("permission") == DatasetPermissionEnum.ONLY_ME
  209. or data.get("permission") == DatasetPermissionEnum.ALL_TEAM
  210. ):
  211. DatasetPermissionService.clear_partial_member_list(dataset_id_str)
  212. partial_member_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
  213. result_data.update({"partial_member_list": partial_member_list})
  214. return result_data, 200
  215. @setup_required
  216. @login_required
  217. @account_initialization_required
  218. def delete(self, dataset_id):
  219. dataset_id_str = str(dataset_id)
  220. # The role of the current user in the ta table must be admin, owner, or editor
  221. if not current_user.is_editor or current_user.is_dataset_operator:
  222. raise Forbidden()
  223. try:
  224. if DatasetService.delete_dataset(dataset_id_str, current_user):
  225. DatasetPermissionService.clear_partial_member_list(dataset_id_str)
  226. return {"result": "success"}, 204
  227. else:
  228. raise NotFound("Dataset not found.")
  229. except services.errors.dataset.DatasetInUseError:
  230. raise DatasetInUseError()
  231. class DatasetUseCheckApi(Resource):
  232. @setup_required
  233. @login_required
  234. @account_initialization_required
  235. def get(self, dataset_id):
  236. dataset_id_str = str(dataset_id)
  237. dataset_is_using = DatasetService.dataset_use_check(dataset_id_str)
  238. return {"is_using": dataset_is_using}, 200
  239. class DatasetQueryApi(Resource):
  240. @setup_required
  241. @login_required
  242. @account_initialization_required
  243. def get(self, dataset_id):
  244. dataset_id_str = str(dataset_id)
  245. dataset = DatasetService.get_dataset(dataset_id_str)
  246. if dataset is None:
  247. raise NotFound("Dataset not found.")
  248. try:
  249. DatasetService.check_dataset_permission(dataset, current_user)
  250. except services.errors.account.NoPermissionError as e:
  251. raise Forbidden(str(e))
  252. page = request.args.get("page", default=1, type=int)
  253. limit = request.args.get("limit", default=20, type=int)
  254. dataset_queries, total = DatasetService.get_dataset_queries(dataset_id=dataset.id, page=page, per_page=limit)
  255. response = {
  256. "data": marshal(dataset_queries, dataset_query_detail_fields),
  257. "has_more": len(dataset_queries) == limit,
  258. "limit": limit,
  259. "total": total,
  260. "page": page,
  261. }
  262. return response, 200
  263. class DatasetIndexingEstimateApi(Resource):
  264. @setup_required
  265. @login_required
  266. @account_initialization_required
  267. def post(self):
  268. parser = reqparse.RequestParser()
  269. parser.add_argument("info_list", type=dict, required=True, nullable=True, location="json")
  270. parser.add_argument("process_rule", type=dict, required=True, nullable=True, location="json")
  271. parser.add_argument(
  272. "indexing_technique",
  273. type=str,
  274. required=True,
  275. choices=Dataset.INDEXING_TECHNIQUE_LIST,
  276. nullable=True,
  277. location="json",
  278. )
  279. parser.add_argument("doc_form", type=str, default="text_model", required=False, nullable=False, location="json")
  280. parser.add_argument("dataset_id", type=str, required=False, nullable=False, location="json")
  281. parser.add_argument(
  282. "doc_language", type=str, default="English", required=False, nullable=False, location="json"
  283. )
  284. args = parser.parse_args()
  285. # validate args
  286. DocumentService.estimate_args_validate(args)
  287. extract_settings = []
  288. if args["info_list"]["data_source_type"] == "upload_file":
  289. file_ids = args["info_list"]["file_info_list"]["file_ids"]
  290. file_details = (
  291. db.session.query(UploadFile)
  292. .filter(UploadFile.tenant_id == current_user.current_tenant_id, UploadFile.id.in_(file_ids))
  293. .all()
  294. )
  295. if file_details is None:
  296. raise NotFound("File not found.")
  297. if file_details:
  298. for file_detail in file_details:
  299. extract_setting = ExtractSetting(
  300. datasource_type="upload_file", upload_file=file_detail, document_model=args["doc_form"]
  301. )
  302. extract_settings.append(extract_setting)
  303. elif args["info_list"]["data_source_type"] == "notion_import":
  304. notion_info_list = args["info_list"]["notion_info_list"]
  305. for notion_info in notion_info_list:
  306. workspace_id = notion_info["workspace_id"]
  307. for page in notion_info["pages"]:
  308. extract_setting = ExtractSetting(
  309. datasource_type="notion_import",
  310. notion_info={
  311. "notion_workspace_id": workspace_id,
  312. "notion_obj_id": page["page_id"],
  313. "notion_page_type": page["type"],
  314. "tenant_id": current_user.current_tenant_id,
  315. },
  316. document_model=args["doc_form"],
  317. )
  318. extract_settings.append(extract_setting)
  319. elif args["info_list"]["data_source_type"] == "website_crawl":
  320. website_info_list = args["info_list"]["website_info_list"]
  321. for url in website_info_list["urls"]:
  322. extract_setting = ExtractSetting(
  323. datasource_type="website_crawl",
  324. website_info={
  325. "provider": website_info_list["provider"],
  326. "job_id": website_info_list["job_id"],
  327. "url": url,
  328. "tenant_id": current_user.current_tenant_id,
  329. "mode": "crawl",
  330. "only_main_content": website_info_list["only_main_content"],
  331. },
  332. document_model=args["doc_form"],
  333. )
  334. extract_settings.append(extract_setting)
  335. else:
  336. raise ValueError("Data source type not support")
  337. indexing_runner = IndexingRunner()
  338. try:
  339. response = indexing_runner.indexing_estimate(
  340. current_user.current_tenant_id,
  341. extract_settings,
  342. args["process_rule"],
  343. args["doc_form"],
  344. args["doc_language"],
  345. args["dataset_id"],
  346. args["indexing_technique"],
  347. )
  348. except LLMBadRequestError:
  349. raise ProviderNotInitializeError(
  350. "No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
  351. )
  352. except ProviderTokenNotInitError as ex:
  353. raise ProviderNotInitializeError(ex.description)
  354. except Exception as e:
  355. raise IndexingEstimateError(str(e))
  356. return response, 200
  357. class DatasetRelatedAppListApi(Resource):
  358. @setup_required
  359. @login_required
  360. @account_initialization_required
  361. @marshal_with(related_app_list)
  362. def get(self, dataset_id):
  363. dataset_id_str = str(dataset_id)
  364. dataset = DatasetService.get_dataset(dataset_id_str)
  365. if dataset is None:
  366. raise NotFound("Dataset not found.")
  367. try:
  368. DatasetService.check_dataset_permission(dataset, current_user)
  369. except services.errors.account.NoPermissionError as e:
  370. raise Forbidden(str(e))
  371. app_dataset_joins = DatasetService.get_related_apps(dataset.id)
  372. related_apps = []
  373. for app_dataset_join in app_dataset_joins:
  374. app_model = app_dataset_join.app
  375. if app_model:
  376. related_apps.append(app_model)
  377. return {"data": related_apps, "total": len(related_apps)}, 200
  378. class DatasetIndexingStatusApi(Resource):
  379. @setup_required
  380. @login_required
  381. @account_initialization_required
  382. def get(self, dataset_id):
  383. dataset_id = str(dataset_id)
  384. documents = (
  385. db.session.query(Document)
  386. .filter(Document.dataset_id == dataset_id, Document.tenant_id == current_user.current_tenant_id)
  387. .all()
  388. )
  389. documents_status = []
  390. for document in documents:
  391. completed_segments = DocumentSegment.query.filter(
  392. DocumentSegment.completed_at.isnot(None),
  393. DocumentSegment.document_id == str(document.id),
  394. DocumentSegment.status != "re_segment",
  395. ).count()
  396. total_segments = DocumentSegment.query.filter(
  397. DocumentSegment.document_id == str(document.id), DocumentSegment.status != "re_segment"
  398. ).count()
  399. document.completed_segments = completed_segments
  400. document.total_segments = total_segments
  401. documents_status.append(marshal(document, document_status_fields))
  402. data = {"data": documents_status}
  403. return data
  404. class DatasetApiKeyApi(Resource):
  405. max_keys = 10
  406. token_prefix = "dataset-"
  407. resource_type = "dataset"
  408. @setup_required
  409. @login_required
  410. @account_initialization_required
  411. @marshal_with(api_key_list)
  412. def get(self):
  413. keys = (
  414. db.session.query(ApiToken)
  415. .filter(ApiToken.type == self.resource_type, ApiToken.tenant_id == current_user.current_tenant_id)
  416. .all()
  417. )
  418. return {"items": keys}
  419. @setup_required
  420. @login_required
  421. @account_initialization_required
  422. @marshal_with(api_key_fields)
  423. def post(self):
  424. # The role of the current user in the ta table must be admin or owner
  425. if not current_user.is_admin_or_owner:
  426. raise Forbidden()
  427. current_key_count = (
  428. db.session.query(ApiToken)
  429. .filter(ApiToken.type == self.resource_type, ApiToken.tenant_id == current_user.current_tenant_id)
  430. .count()
  431. )
  432. if current_key_count >= self.max_keys:
  433. flask_restful.abort(
  434. 400,
  435. message=f"Cannot create more than {self.max_keys} API keys for this resource type.",
  436. code="max_keys_exceeded",
  437. )
  438. key = ApiToken.generate_api_key(self.token_prefix, 24)
  439. api_token = ApiToken()
  440. api_token.tenant_id = current_user.current_tenant_id
  441. api_token.token = key
  442. api_token.type = self.resource_type
  443. db.session.add(api_token)
  444. db.session.commit()
  445. return api_token, 200
  446. class DatasetApiDeleteApi(Resource):
  447. resource_type = "dataset"
  448. @setup_required
  449. @login_required
  450. @account_initialization_required
  451. def delete(self, api_key_id):
  452. api_key_id = str(api_key_id)
  453. # The role of the current user in the ta table must be admin or owner
  454. if not current_user.is_admin_or_owner:
  455. raise Forbidden()
  456. key = (
  457. db.session.query(ApiToken)
  458. .filter(
  459. ApiToken.tenant_id == current_user.current_tenant_id,
  460. ApiToken.type == self.resource_type,
  461. ApiToken.id == api_key_id,
  462. )
  463. .first()
  464. )
  465. if key is None:
  466. flask_restful.abort(404, message="API key not found")
  467. db.session.query(ApiToken).filter(ApiToken.id == api_key_id).delete()
  468. db.session.commit()
  469. return {"result": "success"}, 204
  470. class DatasetApiBaseUrlApi(Resource):
  471. @setup_required
  472. @login_required
  473. @account_initialization_required
  474. def get(self):
  475. return {"api_base_url": (dify_config.SERVICE_API_URL or request.host_url.rstrip("/")) + "/v1"}
  476. class DatasetRetrievalSettingApi(Resource):
  477. @setup_required
  478. @login_required
  479. @account_initialization_required
  480. def get(self):
  481. vector_type = dify_config.VECTOR_STORE
  482. match vector_type:
  483. case (
  484. VectorType.MILVUS
  485. | VectorType.RELYT
  486. | VectorType.PGVECTOR
  487. | VectorType.TIDB_VECTOR
  488. | VectorType.CHROMA
  489. | VectorType.TENCENT
  490. ):
  491. return {"retrieval_method": [RetrievalMethod.SEMANTIC_SEARCH.value]}
  492. case (
  493. VectorType.QDRANT
  494. | VectorType.WEAVIATE
  495. | VectorType.OPENSEARCH
  496. | VectorType.ANALYTICDB
  497. | VectorType.MYSCALE
  498. | VectorType.ORACLE
  499. | VectorType.ELASTICSEARCH
  500. ):
  501. return {
  502. "retrieval_method": [
  503. RetrievalMethod.SEMANTIC_SEARCH.value,
  504. RetrievalMethod.FULL_TEXT_SEARCH.value,
  505. RetrievalMethod.HYBRID_SEARCH.value,
  506. ]
  507. }
  508. case _:
  509. raise ValueError(f"Unsupported vector db type {vector_type}.")
  510. class DatasetRetrievalSettingMockApi(Resource):
  511. @setup_required
  512. @login_required
  513. @account_initialization_required
  514. def get(self, vector_type):
  515. match vector_type:
  516. case (
  517. VectorType.MILVUS
  518. | VectorType.RELYT
  519. | VectorType.TIDB_VECTOR
  520. | VectorType.CHROMA
  521. | VectorType.TENCENT
  522. | VectorType.PGVECTO_RS
  523. ):
  524. return {"retrieval_method": [RetrievalMethod.SEMANTIC_SEARCH.value]}
  525. case (
  526. VectorType.QDRANT
  527. | VectorType.WEAVIATE
  528. | VectorType.OPENSEARCH
  529. | VectorType.ANALYTICDB
  530. | VectorType.MYSCALE
  531. | VectorType.ORACLE
  532. | VectorType.ELASTICSEARCH
  533. | VectorType.PGVECTOR
  534. ):
  535. return {
  536. "retrieval_method": [
  537. RetrievalMethod.SEMANTIC_SEARCH.value,
  538. RetrievalMethod.FULL_TEXT_SEARCH.value,
  539. RetrievalMethod.HYBRID_SEARCH.value,
  540. ]
  541. }
  542. case _:
  543. raise ValueError(f"Unsupported vector db type {vector_type}.")
  544. class DatasetErrorDocs(Resource):
  545. @setup_required
  546. @login_required
  547. @account_initialization_required
  548. def get(self, dataset_id):
  549. dataset_id_str = str(dataset_id)
  550. dataset = DatasetService.get_dataset(dataset_id_str)
  551. if dataset is None:
  552. raise NotFound("Dataset not found.")
  553. results = DocumentService.get_error_documents_by_dataset_id(dataset_id_str)
  554. return {"data": [marshal(item, document_status_fields) for item in results], "total": len(results)}, 200
  555. class DatasetPermissionUserListApi(Resource):
  556. @setup_required
  557. @login_required
  558. @account_initialization_required
  559. def get(self, dataset_id):
  560. dataset_id_str = str(dataset_id)
  561. dataset = DatasetService.get_dataset(dataset_id_str)
  562. if dataset is None:
  563. raise NotFound("Dataset not found.")
  564. try:
  565. DatasetService.check_dataset_permission(dataset, current_user)
  566. except services.errors.account.NoPermissionError as e:
  567. raise Forbidden(str(e))
  568. partial_members_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
  569. return {
  570. "data": partial_members_list,
  571. }, 200
  572. api.add_resource(DatasetListApi, "/datasets")
  573. api.add_resource(DatasetApi, "/datasets/<uuid:dataset_id>")
  574. api.add_resource(DatasetUseCheckApi, "/datasets/<uuid:dataset_id>/use-check")
  575. api.add_resource(DatasetQueryApi, "/datasets/<uuid:dataset_id>/queries")
  576. api.add_resource(DatasetErrorDocs, "/datasets/<uuid:dataset_id>/error-docs")
  577. api.add_resource(DatasetIndexingEstimateApi, "/datasets/indexing-estimate")
  578. api.add_resource(DatasetRelatedAppListApi, "/datasets/<uuid:dataset_id>/related-apps")
  579. api.add_resource(DatasetIndexingStatusApi, "/datasets/<uuid:dataset_id>/indexing-status")
  580. api.add_resource(DatasetApiKeyApi, "/datasets/api-keys")
  581. api.add_resource(DatasetApiDeleteApi, "/datasets/api-keys/<uuid:api_key_id>")
  582. api.add_resource(DatasetApiBaseUrlApi, "/datasets/api-base-info")
  583. api.add_resource(DatasetRetrievalSettingApi, "/datasets/retrieval-setting")
  584. api.add_resource(DatasetRetrievalSettingMockApi, "/datasets/retrieval-setting/<string:vector_type>")
  585. api.add_resource(DatasetPermissionUserListApi, "/datasets/<uuid:dataset_id>/permission-part-users")