You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. import json
  2. from typing import Literal, Union, Dict, List, Any, Optional, IO
  3. import requests
  4. class DifyClient:
  5. def __init__(self, api_key, base_url: str = "https://api.dify.ai/v1"):
  6. self.api_key = api_key
  7. self.base_url = base_url
  8. def _send_request(
  9. self, method: str, endpoint: str, json: dict | None = None, params: dict | None = None, stream: bool = False
  10. ):
  11. headers = {
  12. "Authorization": f"Bearer {self.api_key}",
  13. "Content-Type": "application/json",
  14. }
  15. url = f"{self.base_url}{endpoint}"
  16. response = requests.request(method, url, json=json, params=params, headers=headers, stream=stream)
  17. return response
  18. def _send_request_with_files(self, method, endpoint, data, files):
  19. headers = {"Authorization": f"Bearer {self.api_key}"}
  20. url = f"{self.base_url}{endpoint}"
  21. response = requests.request(method, url, data=data, headers=headers, files=files)
  22. return response
  23. def message_feedback(self, message_id: str, rating: Literal["like", "dislike"], user: str):
  24. data = {"rating": rating, "user": user}
  25. return self._send_request("POST", f"/messages/{message_id}/feedbacks", data)
  26. def get_application_parameters(self, user: str):
  27. params = {"user": user}
  28. return self._send_request("GET", "/parameters", params=params)
  29. def file_upload(self, user: str, files: dict):
  30. data = {"user": user}
  31. return self._send_request_with_files("POST", "/files/upload", data=data, files=files)
  32. def text_to_audio(self, text: str, user: str, streaming: bool = False):
  33. data = {"text": text, "user": user, "streaming": streaming}
  34. return self._send_request("POST", "/text-to-audio", json=data)
  35. def get_meta(self, user: str):
  36. params = {"user": user}
  37. return self._send_request("GET", "/meta", params=params)
  38. def get_app_info(self):
  39. """Get basic application information including name, description, tags, and mode."""
  40. return self._send_request("GET", "/info")
  41. def get_app_site_info(self):
  42. """Get application site information."""
  43. return self._send_request("GET", "/site")
  44. def get_file_preview(self, file_id: str):
  45. """Get file preview by file ID."""
  46. return self._send_request("GET", f"/files/{file_id}/preview")
  47. class CompletionClient(DifyClient):
  48. def create_completion_message(
  49. self, inputs: dict, response_mode: Literal["blocking", "streaming"], user: str, files: dict | None = None
  50. ):
  51. data = {
  52. "inputs": inputs,
  53. "response_mode": response_mode,
  54. "user": user,
  55. "files": files,
  56. }
  57. return self._send_request(
  58. "POST",
  59. "/completion-messages",
  60. data,
  61. stream=True if response_mode == "streaming" else False,
  62. )
  63. class ChatClient(DifyClient):
  64. def create_chat_message(
  65. self,
  66. inputs: dict,
  67. query: str,
  68. user: str,
  69. response_mode: Literal["blocking", "streaming"] = "blocking",
  70. conversation_id: str | None = None,
  71. files: dict | None = None,
  72. ):
  73. data = {
  74. "inputs": inputs,
  75. "query": query,
  76. "user": user,
  77. "response_mode": response_mode,
  78. "files": files,
  79. }
  80. if conversation_id:
  81. data["conversation_id"] = conversation_id
  82. return self._send_request(
  83. "POST",
  84. "/chat-messages",
  85. data,
  86. stream=True if response_mode == "streaming" else False,
  87. )
  88. def get_suggested(self, message_id: str, user: str):
  89. params = {"user": user}
  90. return self._send_request("GET", f"/messages/{message_id}/suggested", params=params)
  91. def stop_message(self, task_id: str, user: str):
  92. data = {"user": user}
  93. return self._send_request("POST", f"/chat-messages/{task_id}/stop", data)
  94. def get_conversations(
  95. self,
  96. user: str,
  97. last_id: str | None = None,
  98. limit: int | None = None,
  99. pinned: bool | None = None,
  100. ):
  101. params = {"user": user, "last_id": last_id, "limit": limit, "pinned": pinned}
  102. return self._send_request("GET", "/conversations", params=params)
  103. def get_conversation_messages(
  104. self,
  105. user: str,
  106. conversation_id: str | None = None,
  107. first_id: str | None = None,
  108. limit: int | None = None,
  109. ):
  110. params = {"user": user}
  111. if conversation_id:
  112. params["conversation_id"] = conversation_id
  113. if first_id:
  114. params["first_id"] = first_id
  115. if limit:
  116. params["limit"] = limit
  117. return self._send_request("GET", "/messages", params=params)
  118. def rename_conversation(self, conversation_id: str, name: str, auto_generate: bool, user: str):
  119. data = {"name": name, "auto_generate": auto_generate, "user": user}
  120. return self._send_request("POST", f"/conversations/{conversation_id}/name", data)
  121. def delete_conversation(self, conversation_id: str, user: str):
  122. data = {"user": user}
  123. return self._send_request("DELETE", f"/conversations/{conversation_id}", data)
  124. def audio_to_text(self, audio_file: IO[bytes] | tuple, user: str):
  125. data = {"user": user}
  126. files = {"file": audio_file}
  127. return self._send_request_with_files("POST", "/audio-to-text", data, files)
  128. # Annotation APIs
  129. def annotation_reply_action(
  130. self,
  131. action: Literal["enable", "disable"],
  132. score_threshold: float,
  133. embedding_provider_name: str,
  134. embedding_model_name: str,
  135. ):
  136. """Enable or disable annotation reply feature."""
  137. # Backend API requires these fields to be non-None values
  138. if score_threshold is None or embedding_provider_name is None or embedding_model_name is None:
  139. raise ValueError("score_threshold, embedding_provider_name, and embedding_model_name cannot be None")
  140. data = {
  141. "score_threshold": score_threshold,
  142. "embedding_provider_name": embedding_provider_name,
  143. "embedding_model_name": embedding_model_name,
  144. }
  145. return self._send_request("POST", f"/apps/annotation-reply/{action}", json=data)
  146. def get_annotation_reply_status(self, action: Literal["enable", "disable"], job_id: str):
  147. """Get the status of an annotation reply action job."""
  148. return self._send_request("GET", f"/apps/annotation-reply/{action}/status/{job_id}")
  149. def list_annotations(self, page: int = 1, limit: int = 20, keyword: str = ""):
  150. """List annotations for the application."""
  151. params = {"page": page, "limit": limit}
  152. if keyword:
  153. params["keyword"] = keyword
  154. return self._send_request("GET", "/apps/annotations", params=params)
  155. def create_annotation(self, question: str, answer: str):
  156. """Create a new annotation."""
  157. data = {"question": question, "answer": answer}
  158. return self._send_request("POST", "/apps/annotations", json=data)
  159. def update_annotation(self, annotation_id: str, question: str, answer: str):
  160. """Update an existing annotation."""
  161. data = {"question": question, "answer": answer}
  162. return self._send_request("PUT", f"/apps/annotations/{annotation_id}", json=data)
  163. def delete_annotation(self, annotation_id: str):
  164. """Delete an annotation."""
  165. return self._send_request("DELETE", f"/apps/annotations/{annotation_id}")
  166. class WorkflowClient(DifyClient):
  167. def run(self, inputs: dict, response_mode: Literal["blocking", "streaming"] = "streaming", user: str = "abc-123"):
  168. data = {"inputs": inputs, "response_mode": response_mode, "user": user}
  169. return self._send_request("POST", "/workflows/run", data)
  170. def stop(self, task_id, user):
  171. data = {"user": user}
  172. return self._send_request("POST", f"/workflows/tasks/{task_id}/stop", data)
  173. def get_result(self, workflow_run_id):
  174. return self._send_request("GET", f"/workflows/run/{workflow_run_id}")
  175. def get_workflow_logs(
  176. self,
  177. keyword: str = None,
  178. status: Literal["succeeded", "failed", "stopped"] | None = None,
  179. page: int = 1,
  180. limit: int = 20,
  181. created_at__before: str = None,
  182. created_at__after: str = None,
  183. created_by_end_user_session_id: str = None,
  184. created_by_account: str = None,
  185. ):
  186. """Get workflow execution logs with optional filtering."""
  187. params = {"page": page, "limit": limit}
  188. if keyword:
  189. params["keyword"] = keyword
  190. if status:
  191. params["status"] = status
  192. if created_at__before:
  193. params["created_at__before"] = created_at__before
  194. if created_at__after:
  195. params["created_at__after"] = created_at__after
  196. if created_by_end_user_session_id:
  197. params["created_by_end_user_session_id"] = created_by_end_user_session_id
  198. if created_by_account:
  199. params["created_by_account"] = created_by_account
  200. return self._send_request("GET", "/workflows/logs", params=params)
  201. def run_specific_workflow(
  202. self,
  203. workflow_id: str,
  204. inputs: dict,
  205. response_mode: Literal["blocking", "streaming"] = "streaming",
  206. user: str = "abc-123",
  207. ):
  208. """Run a specific workflow by workflow ID."""
  209. data = {"inputs": inputs, "response_mode": response_mode, "user": user}
  210. return self._send_request(
  211. "POST", f"/workflows/{workflow_id}/run", data, stream=True if response_mode == "streaming" else False
  212. )
  213. class WorkspaceClient(DifyClient):
  214. """Client for workspace-related operations."""
  215. def get_available_models(self, model_type: str):
  216. """Get available models by model type."""
  217. url = f"/workspaces/current/models/model-types/{model_type}"
  218. return self._send_request("GET", url)
  219. class KnowledgeBaseClient(DifyClient):
  220. def __init__(
  221. self,
  222. api_key: str,
  223. base_url: str = "https://api.dify.ai/v1",
  224. dataset_id: str | None = None,
  225. ):
  226. """
  227. Construct a KnowledgeBaseClient object.
  228. Args:
  229. api_key (str): API key of Dify.
  230. base_url (str, optional): Base URL of Dify API. Defaults to 'https://api.dify.ai/v1'.
  231. dataset_id (str, optional): ID of the dataset. Defaults to None. You don't need this if you just want to
  232. create a new dataset. or list datasets. otherwise you need to set this.
  233. """
  234. super().__init__(api_key=api_key, base_url=base_url)
  235. self.dataset_id = dataset_id
  236. def _get_dataset_id(self):
  237. if self.dataset_id is None:
  238. raise ValueError("dataset_id is not set")
  239. return self.dataset_id
  240. def create_dataset(self, name: str, **kwargs):
  241. return self._send_request("POST", "/datasets", {"name": name}, **kwargs)
  242. def list_datasets(self, page: int = 1, page_size: int = 20, **kwargs):
  243. return self._send_request("GET", f"/datasets?page={page}&limit={page_size}", **kwargs)
  244. def create_document_by_text(self, name, text, extra_params: dict | None = None, **kwargs):
  245. """
  246. Create a document by text.
  247. :param name: Name of the document
  248. :param text: Text content of the document
  249. :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
  250. e.g.
  251. {
  252. 'indexing_technique': 'high_quality',
  253. 'process_rule': {
  254. 'rules': {
  255. 'pre_processing_rules': [
  256. {'id': 'remove_extra_spaces', 'enabled': True},
  257. {'id': 'remove_urls_emails', 'enabled': True}
  258. ],
  259. 'segmentation': {
  260. 'separator': '\n',
  261. 'max_tokens': 500
  262. }
  263. },
  264. 'mode': 'custom'
  265. }
  266. }
  267. :return: Response from the API
  268. """
  269. data = {
  270. "indexing_technique": "high_quality",
  271. "process_rule": {"mode": "automatic"},
  272. "name": name,
  273. "text": text,
  274. }
  275. if extra_params is not None and isinstance(extra_params, dict):
  276. data.update(extra_params)
  277. url = f"/datasets/{self._get_dataset_id()}/document/create_by_text"
  278. return self._send_request("POST", url, json=data, **kwargs)
  279. def update_document_by_text(
  280. self, document_id: str, name: str, text: str, extra_params: dict | None = None, **kwargs
  281. ):
  282. """
  283. Update a document by text.
  284. :param document_id: ID of the document
  285. :param name: Name of the document
  286. :param text: Text content of the document
  287. :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
  288. e.g.
  289. {
  290. 'indexing_technique': 'high_quality',
  291. 'process_rule': {
  292. 'rules': {
  293. 'pre_processing_rules': [
  294. {'id': 'remove_extra_spaces', 'enabled': True},
  295. {'id': 'remove_urls_emails', 'enabled': True}
  296. ],
  297. 'segmentation': {
  298. 'separator': '\n',
  299. 'max_tokens': 500
  300. }
  301. },
  302. 'mode': 'custom'
  303. }
  304. }
  305. :return: Response from the API
  306. """
  307. data = {"name": name, "text": text}
  308. if extra_params is not None and isinstance(extra_params, dict):
  309. data.update(extra_params)
  310. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_text"
  311. return self._send_request("POST", url, json=data, **kwargs)
  312. def create_document_by_file(
  313. self, file_path: str, original_document_id: str | None = None, extra_params: dict | None = None
  314. ):
  315. """
  316. Create a document by file.
  317. :param file_path: Path to the file
  318. :param original_document_id: pass this ID if you want to replace the original document (optional)
  319. :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
  320. e.g.
  321. {
  322. 'indexing_technique': 'high_quality',
  323. 'process_rule': {
  324. 'rules': {
  325. 'pre_processing_rules': [
  326. {'id': 'remove_extra_spaces', 'enabled': True},
  327. {'id': 'remove_urls_emails', 'enabled': True}
  328. ],
  329. 'segmentation': {
  330. 'separator': '\n',
  331. 'max_tokens': 500
  332. }
  333. },
  334. 'mode': 'custom'
  335. }
  336. }
  337. :return: Response from the API
  338. """
  339. files = {"file": open(file_path, "rb")}
  340. data = {
  341. "process_rule": {"mode": "automatic"},
  342. "indexing_technique": "high_quality",
  343. }
  344. if extra_params is not None and isinstance(extra_params, dict):
  345. data.update(extra_params)
  346. if original_document_id is not None:
  347. data["original_document_id"] = original_document_id
  348. url = f"/datasets/{self._get_dataset_id()}/document/create_by_file"
  349. return self._send_request_with_files("POST", url, {"data": json.dumps(data)}, files)
  350. def update_document_by_file(self, document_id: str, file_path: str, extra_params: dict | None = None):
  351. """
  352. Update a document by file.
  353. :param document_id: ID of the document
  354. :param file_path: Path to the file
  355. :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
  356. e.g.
  357. {
  358. 'indexing_technique': 'high_quality',
  359. 'process_rule': {
  360. 'rules': {
  361. 'pre_processing_rules': [
  362. {'id': 'remove_extra_spaces', 'enabled': True},
  363. {'id': 'remove_urls_emails', 'enabled': True}
  364. ],
  365. 'segmentation': {
  366. 'separator': '\n',
  367. 'max_tokens': 500
  368. }
  369. },
  370. 'mode': 'custom'
  371. }
  372. }
  373. :return:
  374. """
  375. files = {"file": open(file_path, "rb")}
  376. data = {}
  377. if extra_params is not None and isinstance(extra_params, dict):
  378. data.update(extra_params)
  379. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_file"
  380. return self._send_request_with_files("POST", url, {"data": json.dumps(data)}, files)
  381. def batch_indexing_status(self, batch_id: str, **kwargs):
  382. """
  383. Get the status of the batch indexing.
  384. :param batch_id: ID of the batch uploading
  385. :return: Response from the API
  386. """
  387. url = f"/datasets/{self._get_dataset_id()}/documents/{batch_id}/indexing-status"
  388. return self._send_request("GET", url, **kwargs)
  389. def delete_dataset(self):
  390. """
  391. Delete this dataset.
  392. :return: Response from the API
  393. """
  394. url = f"/datasets/{self._get_dataset_id()}"
  395. return self._send_request("DELETE", url)
  396. def delete_document(self, document_id: str):
  397. """
  398. Delete a document.
  399. :param document_id: ID of the document
  400. :return: Response from the API
  401. """
  402. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}"
  403. return self._send_request("DELETE", url)
  404. def list_documents(
  405. self,
  406. page: int | None = None,
  407. page_size: int | None = None,
  408. keyword: str | None = None,
  409. **kwargs,
  410. ):
  411. """
  412. Get a list of documents in this dataset.
  413. :return: Response from the API
  414. """
  415. params = {}
  416. if page is not None:
  417. params["page"] = page
  418. if page_size is not None:
  419. params["limit"] = page_size
  420. if keyword is not None:
  421. params["keyword"] = keyword
  422. url = f"/datasets/{self._get_dataset_id()}/documents"
  423. return self._send_request("GET", url, params=params, **kwargs)
  424. def add_segments(self, document_id: str, segments: list[dict], **kwargs):
  425. """
  426. Add segments to a document.
  427. :param document_id: ID of the document
  428. :param segments: List of segments to add, example: [{"content": "1", "answer": "1", "keyword": ["a"]}]
  429. :return: Response from the API
  430. """
  431. data = {"segments": segments}
  432. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
  433. return self._send_request("POST", url, json=data, **kwargs)
  434. def query_segments(
  435. self,
  436. document_id: str,
  437. keyword: str | None = None,
  438. status: str | None = None,
  439. **kwargs,
  440. ):
  441. """
  442. Query segments in this document.
  443. :param document_id: ID of the document
  444. :param keyword: query keyword, optional
  445. :param status: status of the segment, optional, e.g. completed
  446. """
  447. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
  448. params = {}
  449. if keyword is not None:
  450. params["keyword"] = keyword
  451. if status is not None:
  452. params["status"] = status
  453. if "params" in kwargs:
  454. params.update(kwargs["params"])
  455. return self._send_request("GET", url, params=params, **kwargs)
  456. def delete_document_segment(self, document_id: str, segment_id: str):
  457. """
  458. Delete a segment from a document.
  459. :param document_id: ID of the document
  460. :param segment_id: ID of the segment
  461. :return: Response from the API
  462. """
  463. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
  464. return self._send_request("DELETE", url)
  465. def update_document_segment(self, document_id: str, segment_id: str, segment_data: dict, **kwargs):
  466. """
  467. Update a segment in a document.
  468. :param document_id: ID of the document
  469. :param segment_id: ID of the segment
  470. :param segment_data: Data of the segment, example: {"content": "1", "answer": "1", "keyword": ["a"], "enabled": True}
  471. :return: Response from the API
  472. """
  473. data = {"segment": segment_data}
  474. url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
  475. return self._send_request("POST", url, json=data, **kwargs)
  476. # Advanced Knowledge Base APIs
  477. def hit_testing(
  478. self, query: str, retrieval_model: Dict[str, Any] = None, external_retrieval_model: Dict[str, Any] = None
  479. ):
  480. """Perform hit testing on the dataset."""
  481. data = {"query": query}
  482. if retrieval_model:
  483. data["retrieval_model"] = retrieval_model
  484. if external_retrieval_model:
  485. data["external_retrieval_model"] = external_retrieval_model
  486. url = f"/datasets/{self._get_dataset_id()}/hit-testing"
  487. return self._send_request("POST", url, json=data)
  488. def get_dataset_metadata(self):
  489. """Get dataset metadata."""
  490. url = f"/datasets/{self._get_dataset_id()}/metadata"
  491. return self._send_request("GET", url)
  492. def create_dataset_metadata(self, metadata_data: Dict[str, Any]):
  493. """Create dataset metadata."""
  494. url = f"/datasets/{self._get_dataset_id()}/metadata"
  495. return self._send_request("POST", url, json=metadata_data)
  496. def update_dataset_metadata(self, metadata_id: str, metadata_data: Dict[str, Any]):
  497. """Update dataset metadata."""
  498. url = f"/datasets/{self._get_dataset_id()}/metadata/{metadata_id}"
  499. return self._send_request("PATCH", url, json=metadata_data)
  500. def get_built_in_metadata(self):
  501. """Get built-in metadata."""
  502. url = f"/datasets/{self._get_dataset_id()}/metadata/built-in"
  503. return self._send_request("GET", url)
  504. def manage_built_in_metadata(self, action: str, metadata_data: Dict[str, Any] = None):
  505. """Manage built-in metadata with specified action."""
  506. data = metadata_data or {}
  507. url = f"/datasets/{self._get_dataset_id()}/metadata/built-in/{action}"
  508. return self._send_request("POST", url, json=data)
  509. def update_documents_metadata(self, operation_data: List[Dict[str, Any]]):
  510. """Update metadata for multiple documents."""
  511. url = f"/datasets/{self._get_dataset_id()}/documents/metadata"
  512. data = {"operation_data": operation_data}
  513. return self._send_request("POST", url, json=data)
  514. # Dataset Tags APIs
  515. def list_dataset_tags(self):
  516. """List all dataset tags."""
  517. return self._send_request("GET", "/datasets/tags")
  518. def bind_dataset_tags(self, tag_ids: List[str]):
  519. """Bind tags to dataset."""
  520. data = {"tag_ids": tag_ids, "target_id": self._get_dataset_id()}
  521. return self._send_request("POST", "/datasets/tags/binding", json=data)
  522. def unbind_dataset_tag(self, tag_id: str):
  523. """Unbind a single tag from dataset."""
  524. data = {"tag_id": tag_id, "target_id": self._get_dataset_id()}
  525. return self._send_request("POST", "/datasets/tags/unbinding", json=data)
  526. def get_dataset_tags(self):
  527. """Get tags for current dataset."""
  528. url = f"/datasets/{self._get_dataset_id()}/tags"
  529. return self._send_request("GET", url)
  530. # RAG Pipeline APIs
  531. def get_datasource_plugins(self, is_published: bool = True):
  532. """Get datasource plugins for RAG pipeline."""
  533. params = {"is_published": is_published}
  534. url = f"/datasets/{self._get_dataset_id()}/pipeline/datasource-plugins"
  535. return self._send_request("GET", url, params=params)
  536. def run_datasource_node(
  537. self,
  538. node_id: str,
  539. inputs: Dict[str, Any],
  540. datasource_type: str,
  541. is_published: bool = True,
  542. credential_id: str = None,
  543. ):
  544. """Run a datasource node in RAG pipeline."""
  545. data = {"inputs": inputs, "datasource_type": datasource_type, "is_published": is_published}
  546. if credential_id:
  547. data["credential_id"] = credential_id
  548. url = f"/datasets/{self._get_dataset_id()}/pipeline/datasource/nodes/{node_id}/run"
  549. return self._send_request("POST", url, json=data, stream=True)
  550. def run_rag_pipeline(
  551. self,
  552. inputs: Dict[str, Any],
  553. datasource_type: str,
  554. datasource_info_list: List[Dict[str, Any]],
  555. start_node_id: str,
  556. is_published: bool = True,
  557. response_mode: Literal["streaming", "blocking"] = "blocking",
  558. ):
  559. """Run RAG pipeline."""
  560. data = {
  561. "inputs": inputs,
  562. "datasource_type": datasource_type,
  563. "datasource_info_list": datasource_info_list,
  564. "start_node_id": start_node_id,
  565. "is_published": is_published,
  566. "response_mode": response_mode,
  567. }
  568. url = f"/datasets/{self._get_dataset_id()}/pipeline/run"
  569. return self._send_request("POST", url, json=data, stream=response_mode == "streaming")
  570. def upload_pipeline_file(self, file_path: str):
  571. """Upload file for RAG pipeline."""
  572. with open(file_path, "rb") as f:
  573. files = {"file": f}
  574. return self._send_request_with_files("POST", "/datasets/pipeline/file-upload", {}, files)