| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 | 
							- import json
 - from typing import Literal, Union, Dict, List, Any, Optional, IO
 - 
 - import requests
 - 
 - 
 - class DifyClient:
 -     def __init__(self, api_key, base_url: str = "https://api.dify.ai/v1"):
 -         self.api_key = api_key
 -         self.base_url = base_url
 - 
 -     def _send_request(
 -         self, method: str, endpoint: str, json: dict | None = None, params: dict | None = None, stream: bool = False
 -     ):
 -         headers = {
 -             "Authorization": f"Bearer {self.api_key}",
 -             "Content-Type": "application/json",
 -         }
 - 
 -         url = f"{self.base_url}{endpoint}"
 -         response = requests.request(method, url, json=json, params=params, headers=headers, stream=stream)
 - 
 -         return response
 - 
 -     def _send_request_with_files(self, method, endpoint, data, files):
 -         headers = {"Authorization": f"Bearer {self.api_key}"}
 - 
 -         url = f"{self.base_url}{endpoint}"
 -         response = requests.request(method, url, data=data, headers=headers, files=files)
 - 
 -         return response
 - 
 -     def message_feedback(self, message_id: str, rating: Literal["like", "dislike"], user: str):
 -         data = {"rating": rating, "user": user}
 -         return self._send_request("POST", f"/messages/{message_id}/feedbacks", data)
 - 
 -     def get_application_parameters(self, user: str):
 -         params = {"user": user}
 -         return self._send_request("GET", "/parameters", params=params)
 - 
 -     def file_upload(self, user: str, files: dict):
 -         data = {"user": user}
 -         return self._send_request_with_files("POST", "/files/upload", data=data, files=files)
 - 
 -     def text_to_audio(self, text: str, user: str, streaming: bool = False):
 -         data = {"text": text, "user": user, "streaming": streaming}
 -         return self._send_request("POST", "/text-to-audio", json=data)
 - 
 -     def get_meta(self, user: str):
 -         params = {"user": user}
 -         return self._send_request("GET", "/meta", params=params)
 - 
 -     def get_app_info(self):
 -         """Get basic application information including name, description, tags, and mode."""
 -         return self._send_request("GET", "/info")
 - 
 -     def get_app_site_info(self):
 -         """Get application site information."""
 -         return self._send_request("GET", "/site")
 - 
 -     def get_file_preview(self, file_id: str):
 -         """Get file preview by file ID."""
 -         return self._send_request("GET", f"/files/{file_id}/preview")
 - 
 - 
 - class CompletionClient(DifyClient):
 -     def create_completion_message(
 -         self, inputs: dict, response_mode: Literal["blocking", "streaming"], user: str, files: dict | None = None
 -     ):
 -         data = {
 -             "inputs": inputs,
 -             "response_mode": response_mode,
 -             "user": user,
 -             "files": files,
 -         }
 -         return self._send_request(
 -             "POST",
 -             "/completion-messages",
 -             data,
 -             stream=True if response_mode == "streaming" else False,
 -         )
 - 
 - 
 - class ChatClient(DifyClient):
 -     def create_chat_message(
 -         self,
 -         inputs: dict,
 -         query: str,
 -         user: str,
 -         response_mode: Literal["blocking", "streaming"] = "blocking",
 -         conversation_id: str | None = None,
 -         files: dict | None = None,
 -     ):
 -         data = {
 -             "inputs": inputs,
 -             "query": query,
 -             "user": user,
 -             "response_mode": response_mode,
 -             "files": files,
 -         }
 -         if conversation_id:
 -             data["conversation_id"] = conversation_id
 - 
 -         return self._send_request(
 -             "POST",
 -             "/chat-messages",
 -             data,
 -             stream=True if response_mode == "streaming" else False,
 -         )
 - 
 -     def get_suggested(self, message_id: str, user: str):
 -         params = {"user": user}
 -         return self._send_request("GET", f"/messages/{message_id}/suggested", params=params)
 - 
 -     def stop_message(self, task_id: str, user: str):
 -         data = {"user": user}
 -         return self._send_request("POST", f"/chat-messages/{task_id}/stop", data)
 - 
 -     def get_conversations(
 -         self,
 -         user: str,
 -         last_id: str | None = None,
 -         limit: int | None = None,
 -         pinned: bool | None = None,
 -     ):
 -         params = {"user": user, "last_id": last_id, "limit": limit, "pinned": pinned}
 -         return self._send_request("GET", "/conversations", params=params)
 - 
 -     def get_conversation_messages(
 -         self,
 -         user: str,
 -         conversation_id: str | None = None,
 -         first_id: str | None = None,
 -         limit: int | None = None,
 -     ):
 -         params = {"user": user}
 - 
 -         if conversation_id:
 -             params["conversation_id"] = conversation_id
 -         if first_id:
 -             params["first_id"] = first_id
 -         if limit:
 -             params["limit"] = limit
 - 
 -         return self._send_request("GET", "/messages", params=params)
 - 
 -     def rename_conversation(self, conversation_id: str, name: str, auto_generate: bool, user: str):
 -         data = {"name": name, "auto_generate": auto_generate, "user": user}
 -         return self._send_request("POST", f"/conversations/{conversation_id}/name", data)
 - 
 -     def delete_conversation(self, conversation_id: str, user: str):
 -         data = {"user": user}
 -         return self._send_request("DELETE", f"/conversations/{conversation_id}", data)
 - 
 -     def audio_to_text(self, audio_file: IO[bytes] | tuple, user: str):
 -         data = {"user": user}
 -         files = {"file": audio_file}
 -         return self._send_request_with_files("POST", "/audio-to-text", data, files)
 - 
 -     # Annotation APIs
 -     def annotation_reply_action(
 -         self,
 -         action: Literal["enable", "disable"],
 -         score_threshold: float,
 -         embedding_provider_name: str,
 -         embedding_model_name: str,
 -     ):
 -         """Enable or disable annotation reply feature."""
 -         # Backend API requires these fields to be non-None values
 -         if score_threshold is None or embedding_provider_name is None or embedding_model_name is None:
 -             raise ValueError("score_threshold, embedding_provider_name, and embedding_model_name cannot be None")
 - 
 -         data = {
 -             "score_threshold": score_threshold,
 -             "embedding_provider_name": embedding_provider_name,
 -             "embedding_model_name": embedding_model_name,
 -         }
 -         return self._send_request("POST", f"/apps/annotation-reply/{action}", json=data)
 - 
 -     def get_annotation_reply_status(self, action: Literal["enable", "disable"], job_id: str):
 -         """Get the status of an annotation reply action job."""
 -         return self._send_request("GET", f"/apps/annotation-reply/{action}/status/{job_id}")
 - 
 -     def list_annotations(self, page: int = 1, limit: int = 20, keyword: str = ""):
 -         """List annotations for the application."""
 -         params = {"page": page, "limit": limit}
 -         if keyword:
 -             params["keyword"] = keyword
 -         return self._send_request("GET", "/apps/annotations", params=params)
 - 
 -     def create_annotation(self, question: str, answer: str):
 -         """Create a new annotation."""
 -         data = {"question": question, "answer": answer}
 -         return self._send_request("POST", "/apps/annotations", json=data)
 - 
 -     def update_annotation(self, annotation_id: str, question: str, answer: str):
 -         """Update an existing annotation."""
 -         data = {"question": question, "answer": answer}
 -         return self._send_request("PUT", f"/apps/annotations/{annotation_id}", json=data)
 - 
 -     def delete_annotation(self, annotation_id: str):
 -         """Delete an annotation."""
 -         return self._send_request("DELETE", f"/apps/annotations/{annotation_id}")
 - 
 - 
 - class WorkflowClient(DifyClient):
 -     def run(self, inputs: dict, response_mode: Literal["blocking", "streaming"] = "streaming", user: str = "abc-123"):
 -         data = {"inputs": inputs, "response_mode": response_mode, "user": user}
 -         return self._send_request("POST", "/workflows/run", data)
 - 
 -     def stop(self, task_id, user):
 -         data = {"user": user}
 -         return self._send_request("POST", f"/workflows/tasks/{task_id}/stop", data)
 - 
 -     def get_result(self, workflow_run_id):
 -         return self._send_request("GET", f"/workflows/run/{workflow_run_id}")
 - 
 -     def get_workflow_logs(
 -         self,
 -         keyword: str = None,
 -         status: Literal["succeeded", "failed", "stopped"] | None = None,
 -         page: int = 1,
 -         limit: int = 20,
 -         created_at__before: str = None,
 -         created_at__after: str = None,
 -         created_by_end_user_session_id: str = None,
 -         created_by_account: str = None,
 -     ):
 -         """Get workflow execution logs with optional filtering."""
 -         params = {"page": page, "limit": limit}
 -         if keyword:
 -             params["keyword"] = keyword
 -         if status:
 -             params["status"] = status
 -         if created_at__before:
 -             params["created_at__before"] = created_at__before
 -         if created_at__after:
 -             params["created_at__after"] = created_at__after
 -         if created_by_end_user_session_id:
 -             params["created_by_end_user_session_id"] = created_by_end_user_session_id
 -         if created_by_account:
 -             params["created_by_account"] = created_by_account
 -         return self._send_request("GET", "/workflows/logs", params=params)
 - 
 -     def run_specific_workflow(
 -         self,
 -         workflow_id: str,
 -         inputs: dict,
 -         response_mode: Literal["blocking", "streaming"] = "streaming",
 -         user: str = "abc-123",
 -     ):
 -         """Run a specific workflow by workflow ID."""
 -         data = {"inputs": inputs, "response_mode": response_mode, "user": user}
 -         return self._send_request(
 -             "POST", f"/workflows/{workflow_id}/run", data, stream=True if response_mode == "streaming" else False
 -         )
 - 
 - 
 - class WorkspaceClient(DifyClient):
 -     """Client for workspace-related operations."""
 - 
 -     def get_available_models(self, model_type: str):
 -         """Get available models by model type."""
 -         url = f"/workspaces/current/models/model-types/{model_type}"
 -         return self._send_request("GET", url)
 - 
 - 
 - class KnowledgeBaseClient(DifyClient):
 -     def __init__(
 -         self,
 -         api_key: str,
 -         base_url: str = "https://api.dify.ai/v1",
 -         dataset_id: str | None = None,
 -     ):
 -         """
 -         Construct a KnowledgeBaseClient object.
 - 
 -         Args:
 -             api_key (str): API key of Dify.
 -             base_url (str, optional): Base URL of Dify API. Defaults to 'https://api.dify.ai/v1'.
 -             dataset_id (str, optional): ID of the dataset. Defaults to None. You don't need this if you just want to
 -                 create a new dataset. or list datasets. otherwise you need to set this.
 -         """
 -         super().__init__(api_key=api_key, base_url=base_url)
 -         self.dataset_id = dataset_id
 - 
 -     def _get_dataset_id(self):
 -         if self.dataset_id is None:
 -             raise ValueError("dataset_id is not set")
 -         return self.dataset_id
 - 
 -     def create_dataset(self, name: str, **kwargs):
 -         return self._send_request("POST", "/datasets", {"name": name}, **kwargs)
 - 
 -     def list_datasets(self, page: int = 1, page_size: int = 20, **kwargs):
 -         return self._send_request("GET", f"/datasets?page={page}&limit={page_size}", **kwargs)
 - 
 -     def create_document_by_text(self, name, text, extra_params: dict | None = None, **kwargs):
 -         """
 -         Create a document by text.
 - 
 -         :param name: Name of the document
 -         :param text: Text content of the document
 -         :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
 -             e.g.
 -             {
 -             'indexing_technique': 'high_quality',
 -             'process_rule': {
 -                 'rules': {
 -                     'pre_processing_rules': [
 -                         {'id': 'remove_extra_spaces', 'enabled': True},
 -                         {'id': 'remove_urls_emails', 'enabled': True}
 -                     ],
 -                     'segmentation': {
 -                         'separator': '\n',
 -                         'max_tokens': 500
 -                     }
 -                 },
 -                 'mode': 'custom'
 -             }
 -         }
 -         :return: Response from the API
 -         """
 -         data = {
 -             "indexing_technique": "high_quality",
 -             "process_rule": {"mode": "automatic"},
 -             "name": name,
 -             "text": text,
 -         }
 -         if extra_params is not None and isinstance(extra_params, dict):
 -             data.update(extra_params)
 -         url = f"/datasets/{self._get_dataset_id()}/document/create_by_text"
 -         return self._send_request("POST", url, json=data, **kwargs)
 - 
 -     def update_document_by_text(
 -         self, document_id: str, name: str, text: str, extra_params: dict | None = None, **kwargs
 -     ):
 -         """
 -         Update a document by text.
 - 
 -         :param document_id: ID of the document
 -         :param name: Name of the document
 -         :param text: Text content of the document
 -         :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
 -             e.g.
 -             {
 -             'indexing_technique': 'high_quality',
 -             'process_rule': {
 -                 'rules': {
 -                     'pre_processing_rules': [
 -                         {'id': 'remove_extra_spaces', 'enabled': True},
 -                         {'id': 'remove_urls_emails', 'enabled': True}
 -                     ],
 -                     'segmentation': {
 -                         'separator': '\n',
 -                         'max_tokens': 500
 -                     }
 -                 },
 -                 'mode': 'custom'
 -             }
 -         }
 -         :return: Response from the API
 -         """
 -         data = {"name": name, "text": text}
 -         if extra_params is not None and isinstance(extra_params, dict):
 -             data.update(extra_params)
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_text"
 -         return self._send_request("POST", url, json=data, **kwargs)
 - 
 -     def create_document_by_file(
 -         self, file_path: str, original_document_id: str | None = None, extra_params: dict | None = None
 -     ):
 -         """
 -         Create a document by file.
 - 
 -         :param file_path: Path to the file
 -         :param original_document_id: pass this ID if you want to replace the original document (optional)
 -         :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
 -             e.g.
 -             {
 -             'indexing_technique': 'high_quality',
 -             'process_rule': {
 -                 'rules': {
 -                     'pre_processing_rules': [
 -                         {'id': 'remove_extra_spaces', 'enabled': True},
 -                         {'id': 'remove_urls_emails', 'enabled': True}
 -                     ],
 -                     'segmentation': {
 -                         'separator': '\n',
 -                         'max_tokens': 500
 -                     }
 -                 },
 -                 'mode': 'custom'
 -             }
 -         }
 -         :return: Response from the API
 -         """
 -         files = {"file": open(file_path, "rb")}
 -         data = {
 -             "process_rule": {"mode": "automatic"},
 -             "indexing_technique": "high_quality",
 -         }
 -         if extra_params is not None and isinstance(extra_params, dict):
 -             data.update(extra_params)
 -         if original_document_id is not None:
 -             data["original_document_id"] = original_document_id
 -         url = f"/datasets/{self._get_dataset_id()}/document/create_by_file"
 -         return self._send_request_with_files("POST", url, {"data": json.dumps(data)}, files)
 - 
 -     def update_document_by_file(self, document_id: str, file_path: str, extra_params: dict | None = None):
 -         """
 -         Update a document by file.
 - 
 -         :param document_id: ID of the document
 -         :param file_path: Path to the file
 -         :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
 -             e.g.
 -             {
 -             'indexing_technique': 'high_quality',
 -             'process_rule': {
 -                 'rules': {
 -                     'pre_processing_rules': [
 -                         {'id': 'remove_extra_spaces', 'enabled': True},
 -                         {'id': 'remove_urls_emails', 'enabled': True}
 -                     ],
 -                     'segmentation': {
 -                         'separator': '\n',
 -                         'max_tokens': 500
 -                     }
 -                 },
 -                 'mode': 'custom'
 -             }
 -         }
 -         :return:
 -         """
 -         files = {"file": open(file_path, "rb")}
 -         data = {}
 -         if extra_params is not None and isinstance(extra_params, dict):
 -             data.update(extra_params)
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_file"
 -         return self._send_request_with_files("POST", url, {"data": json.dumps(data)}, files)
 - 
 -     def batch_indexing_status(self, batch_id: str, **kwargs):
 -         """
 -         Get the status of the batch indexing.
 - 
 -         :param batch_id: ID of the batch uploading
 -         :return: Response from the API
 -         """
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{batch_id}/indexing-status"
 -         return self._send_request("GET", url, **kwargs)
 - 
 -     def delete_dataset(self):
 -         """
 -         Delete this dataset.
 - 
 -         :return: Response from the API
 -         """
 -         url = f"/datasets/{self._get_dataset_id()}"
 -         return self._send_request("DELETE", url)
 - 
 -     def delete_document(self, document_id: str):
 -         """
 -         Delete a document.
 - 
 -         :param document_id: ID of the document
 -         :return: Response from the API
 -         """
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}"
 -         return self._send_request("DELETE", url)
 - 
 -     def list_documents(
 -         self,
 -         page: int | None = None,
 -         page_size: int | None = None,
 -         keyword: str | None = None,
 -         **kwargs,
 -     ):
 -         """
 -         Get a list of documents in this dataset.
 - 
 -         :return: Response from the API
 -         """
 -         params = {}
 -         if page is not None:
 -             params["page"] = page
 -         if page_size is not None:
 -             params["limit"] = page_size
 -         if keyword is not None:
 -             params["keyword"] = keyword
 -         url = f"/datasets/{self._get_dataset_id()}/documents"
 -         return self._send_request("GET", url, params=params, **kwargs)
 - 
 -     def add_segments(self, document_id: str, segments: list[dict], **kwargs):
 -         """
 -         Add segments to a document.
 - 
 -         :param document_id: ID of the document
 -         :param segments: List of segments to add, example: [{"content": "1", "answer": "1", "keyword": ["a"]}]
 -         :return: Response from the API
 -         """
 -         data = {"segments": segments}
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
 -         return self._send_request("POST", url, json=data, **kwargs)
 - 
 -     def query_segments(
 -         self,
 -         document_id: str,
 -         keyword: str | None = None,
 -         status: str | None = None,
 -         **kwargs,
 -     ):
 -         """
 -         Query segments in this document.
 - 
 -         :param document_id: ID of the document
 -         :param keyword: query keyword, optional
 -         :param status: status of the segment, optional, e.g. completed
 -         """
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
 -         params = {}
 -         if keyword is not None:
 -             params["keyword"] = keyword
 -         if status is not None:
 -             params["status"] = status
 -         if "params" in kwargs:
 -             params.update(kwargs["params"])
 -         return self._send_request("GET", url, params=params, **kwargs)
 - 
 -     def delete_document_segment(self, document_id: str, segment_id: str):
 -         """
 -         Delete a segment from a document.
 - 
 -         :param document_id: ID of the document
 -         :param segment_id: ID of the segment
 -         :return: Response from the API
 -         """
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
 -         return self._send_request("DELETE", url)
 - 
 -     def update_document_segment(self, document_id: str, segment_id: str, segment_data: dict, **kwargs):
 -         """
 -         Update a segment in a document.
 - 
 -         :param document_id: ID of the document
 -         :param segment_id: ID of the segment
 -         :param segment_data: Data of the segment, example: {"content": "1", "answer": "1", "keyword": ["a"], "enabled": True}
 -         :return: Response from the API
 -         """
 -         data = {"segment": segment_data}
 -         url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
 -         return self._send_request("POST", url, json=data, **kwargs)
 - 
 -     # Advanced Knowledge Base APIs
 -     def hit_testing(
 -         self, query: str, retrieval_model: Dict[str, Any] = None, external_retrieval_model: Dict[str, Any] = None
 -     ):
 -         """Perform hit testing on the dataset."""
 -         data = {"query": query}
 -         if retrieval_model:
 -             data["retrieval_model"] = retrieval_model
 -         if external_retrieval_model:
 -             data["external_retrieval_model"] = external_retrieval_model
 -         url = f"/datasets/{self._get_dataset_id()}/hit-testing"
 -         return self._send_request("POST", url, json=data)
 - 
 -     def get_dataset_metadata(self):
 -         """Get dataset metadata."""
 -         url = f"/datasets/{self._get_dataset_id()}/metadata"
 -         return self._send_request("GET", url)
 - 
 -     def create_dataset_metadata(self, metadata_data: Dict[str, Any]):
 -         """Create dataset metadata."""
 -         url = f"/datasets/{self._get_dataset_id()}/metadata"
 -         return self._send_request("POST", url, json=metadata_data)
 - 
 -     def update_dataset_metadata(self, metadata_id: str, metadata_data: Dict[str, Any]):
 -         """Update dataset metadata."""
 -         url = f"/datasets/{self._get_dataset_id()}/metadata/{metadata_id}"
 -         return self._send_request("PATCH", url, json=metadata_data)
 - 
 -     def get_built_in_metadata(self):
 -         """Get built-in metadata."""
 -         url = f"/datasets/{self._get_dataset_id()}/metadata/built-in"
 -         return self._send_request("GET", url)
 - 
 -     def manage_built_in_metadata(self, action: str, metadata_data: Dict[str, Any] = None):
 -         """Manage built-in metadata with specified action."""
 -         data = metadata_data or {}
 -         url = f"/datasets/{self._get_dataset_id()}/metadata/built-in/{action}"
 -         return self._send_request("POST", url, json=data)
 - 
 -     def update_documents_metadata(self, operation_data: List[Dict[str, Any]]):
 -         """Update metadata for multiple documents."""
 -         url = f"/datasets/{self._get_dataset_id()}/documents/metadata"
 -         data = {"operation_data": operation_data}
 -         return self._send_request("POST", url, json=data)
 - 
 -     # Dataset Tags APIs
 -     def list_dataset_tags(self):
 -         """List all dataset tags."""
 -         return self._send_request("GET", "/datasets/tags")
 - 
 -     def bind_dataset_tags(self, tag_ids: List[str]):
 -         """Bind tags to dataset."""
 -         data = {"tag_ids": tag_ids, "target_id": self._get_dataset_id()}
 -         return self._send_request("POST", "/datasets/tags/binding", json=data)
 - 
 -     def unbind_dataset_tag(self, tag_id: str):
 -         """Unbind a single tag from dataset."""
 -         data = {"tag_id": tag_id, "target_id": self._get_dataset_id()}
 -         return self._send_request("POST", "/datasets/tags/unbinding", json=data)
 - 
 -     def get_dataset_tags(self):
 -         """Get tags for current dataset."""
 -         url = f"/datasets/{self._get_dataset_id()}/tags"
 -         return self._send_request("GET", url)
 - 
 -     # RAG Pipeline APIs
 -     def get_datasource_plugins(self, is_published: bool = True):
 -         """Get datasource plugins for RAG pipeline."""
 -         params = {"is_published": is_published}
 -         url = f"/datasets/{self._get_dataset_id()}/pipeline/datasource-plugins"
 -         return self._send_request("GET", url, params=params)
 - 
 -     def run_datasource_node(
 -         self,
 -         node_id: str,
 -         inputs: Dict[str, Any],
 -         datasource_type: str,
 -         is_published: bool = True,
 -         credential_id: str = None,
 -     ):
 -         """Run a datasource node in RAG pipeline."""
 -         data = {"inputs": inputs, "datasource_type": datasource_type, "is_published": is_published}
 -         if credential_id:
 -             data["credential_id"] = credential_id
 -         url = f"/datasets/{self._get_dataset_id()}/pipeline/datasource/nodes/{node_id}/run"
 -         return self._send_request("POST", url, json=data, stream=True)
 - 
 -     def run_rag_pipeline(
 -         self,
 -         inputs: Dict[str, Any],
 -         datasource_type: str,
 -         datasource_info_list: List[Dict[str, Any]],
 -         start_node_id: str,
 -         is_published: bool = True,
 -         response_mode: Literal["streaming", "blocking"] = "blocking",
 -     ):
 -         """Run RAG pipeline."""
 -         data = {
 -             "inputs": inputs,
 -             "datasource_type": datasource_type,
 -             "datasource_info_list": datasource_info_list,
 -             "start_node_id": start_node_id,
 -             "is_published": is_published,
 -             "response_mode": response_mode,
 -         }
 -         url = f"/datasets/{self._get_dataset_id()}/pipeline/run"
 -         return self._send_request("POST", url, json=data, stream=response_mode == "streaming")
 - 
 -     def upload_pipeline_file(self, file_path: str):
 -         """Upload file for RAG pipeline."""
 -         with open(file_path, "rb") as f:
 -             files = {"file": f}
 -             return self._send_request_with_files("POST", "/datasets/pipeline/file-upload", {}, files)
 
 
  |