From d63ab48adcd38645dad93600f60fcf832287463f Mon Sep 17 00:00:00 2001 From: Kevin REMY Date: Thu, 11 Jun 2026 22:40:57 +0200 Subject: [PATCH 1/2] Sync sources with main repository --- dataikuapi/dss/admin.py | 11 ++++- dataikuapi/dss/llm_tracing/__init__.py | 1 + dataikuapi/dss/messaging_channel.py | 8 ++-- dataikuapi/dss/ml.py | 16 +++---- dataikuapi/dss/project.py | 64 ++++++++++++++++++++++++-- dataikuapi/dssclient.py | 17 +++---- dataikuapi/govern/artifact.py | 10 ++-- dataikuapi/govern_client.py | 6 --- 8 files changed, 96 insertions(+), 37 deletions(-) diff --git a/dataikuapi/dss/admin.py b/dataikuapi/dss/admin.py index f83ac674..cd06e46f 100644 --- a/dataikuapi/dss/admin.py +++ b/dataikuapi/dss/admin.py @@ -27,7 +27,7 @@ def to_connection(self): :rtype: :class:`DSSConnection` """ - return DSSConnection(self.client, self["name"]) + return DSSConnection(self.client, self.name) @property def name(self): @@ -36,6 +36,8 @@ def name(self): :rtype: string """ + if "name" in self: + return self["name"] return self["id"] @property @@ -46,6 +48,8 @@ def type(self): :return: a DSS connection type, like PostgreSQL, EC2, Azure, ... :rtype: string """ + if "type" in self: + return self["type"] return self["label"] class DSSConnectionInfo(dict): @@ -702,6 +706,11 @@ def get_client_as(self): extra_headers={"X-DKU-ProxyUser": self.login}, client_certificate=self.client._session.cert) client_as._session.verify = self.client._session.verify return client_as + elif self.client.jwt_bearer_token is not None: + client_as = DSSClient(self.client.host, jwt_bearer_token = self.client.jwt_bearer_token, + extra_headers={"X-DKU-ProxyUser": self.login}, client_certificate=self.client._session.cert) + client_as._session.verify = self.client._session.verify + return client_as else: raise ValueError("Don't know how to proxy this client") diff --git a/dataikuapi/dss/llm_tracing/__init__.py b/dataikuapi/dss/llm_tracing/__init__.py index c3752a55..c3d54c90 100644 --- a/dataikuapi/dss/llm_tracing/__init__.py +++ b/dataikuapi/dss/llm_tracing/__init__.py @@ -97,6 +97,7 @@ def __init__(self,name): @staticmethod def create_event(name): sb = SpanBuilder(name) + sb.span["type"] = "event" ts = int(time.time() * 1000) sb.span["timestamp"] = datetime.datetime.fromtimestamp(ts / 1000, datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ") return sb diff --git a/dataikuapi/dss/messaging_channel.py b/dataikuapi/dss/messaging_channel.py index e85ec00b..2e9fdad7 100644 --- a/dataikuapi/dss/messaging_channel.py +++ b/dataikuapi/dss/messaging_channel.py @@ -285,9 +285,9 @@ def send(self, project_key, to, subject, body, attachments=None, plain_text=Fals channel = client.get_messaging_channel("other-mail-channel-id") for file in paths: - with open(file) as f: - # Optionally include file type ("text/csv") - attachments.append(file, f.read(), "text/csv") + with open(file) as f: + # Optionally include file type ("text/csv") + attachments.append([file, f.read(), "text/csv"]) channel.send("PROJECT_KEY", ["joe@dataiku.com"], "Subject", "Body in plain text", attachments=attachments, False) :param project_key: project issuing the email. The user must have "Write content" permission on the specified project. @@ -299,7 +299,7 @@ def send(self, project_key, to, subject, body, attachments=None, plain_text=Fals :param body: email body (in plain text or HTML format) :type body: str :param attachments: files to be attached to the mail, defaults to None - :type attachments: list[BufferedReader] + :type attachments: list[list[str, bytes, str]] :param plain_text: True to send email as plain text, False to send it as HTML. Defaults to False. :type plain_text: bool :param sender: sender email address. Use None to use the sender defined at the channel level. diff --git a/dataikuapi/dss/ml.py b/dataikuapi/dss/ml.py index 9efd4010..1d5f60b2 100644 --- a/dataikuapi/dss/ml.py +++ b/dataikuapi/dss/ml.py @@ -4129,7 +4129,7 @@ def get_partial_dependencies(self): class DSSTrainedTimeseriesForecastingModelDetails(DSSTrainedPredictionModelDetails): """ - Object to read details of a timeseries forecasting model, for instance the per time series metrics + Object to read details of a time series forecasting model, for instance the per time series metrics .. important:: Do not create this object directly, use :meth:`DSSMLTask.get_trained_model_details()` instead @@ -4139,10 +4139,10 @@ def __init__(self, details, snippet, saved_model=None, saved_model_version=None, def compute_residuals(self, wait=True): """ - Launch computation of residuals for this trained timeseries model. + Launch computation of residuals for this trained time series model. :param wait: a flag to wait for the operation to complete (defaults to **True**) - :returns: if wait is True, a dictionary containing the residuals per-timeseries, else a future to wait on the result + :returns: if wait is True, a dictionary containing the residuals per time series, else a future to wait on the result :rtype: Union[:class:`dict`, :class:`dataikuapi.dss.future.DSSFuture`] """ if self.mltask is not None: @@ -4166,7 +4166,7 @@ def get_residuals(self): """ Retrieve a list of residuals for this trained time-series models - :returns: A dictionary, which contains a residuals object per-timeseries + :returns: A dictionary, which contains a residuals object per time series :rtype: dict """ if self.mltask is not None: @@ -4184,7 +4184,7 @@ def get_residuals(self): def compute_permutation_importance(self, wait=True, n_iterations=None, per_identifier=None): """ - Launch computation of permutation importance for this trained timeseries model. + Launch feature importance computation (measured using permutation importance) for this trained time series model. :param wait: a flag to wait for the operation to complete (defaults to **True**) :returns: if wait is True, a dictionary, else a future to wait on the result @@ -4238,7 +4238,7 @@ def get_permutation_importance(self): def get_per_timeseries_metrics(self): """ - Returns per timeseries performance metrics for this model. + Returns per time series performance metrics for this model. :returns: a dict of performance metrics values :rtype: dict @@ -4257,9 +4257,9 @@ def get_per_timeseries_metrics(self): def get_per_timeseries_evaluation_forecasts(self): """ - Returns per timeseries evaluation forecasts for this model. + Returns per time series evaluation forecasts for this model. - :returns: a dict of evaluation forecasts per timeseries identifier + :returns: a dict of evaluation forecasts per time series identifier :rtype: dict """ if self.mltask is not None: diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index d38a67c6..fe826f69 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -2732,7 +2732,7 @@ def get_knowledge_bank(self, id): """ return DSSKnowledgeBank(self.client, self.project_key, id) - def create_knowledge_bank(self, name, vector_store_type, embedding_llm_id, settings=None): + def create_knowledge_bank(self, name, vector_store_type, embedding_llm_id=None, settings=None): """ Create a new knowledge bank in the project, and return a handle to interact with it @@ -2751,22 +2751,76 @@ def create_knowledge_bank(self, name, vector_store_type, embedding_llm_id, setti * MILVUS_LOCAL * MILVUS_REMOTE - :param str embedding_llm_id: The id of the embedding LLM. It has to have the TEXT_EMBEDDING_EXTRACTION purpose. + :param Optional[str] embedding_llm_id: The id of the embedding LLM. + For managed KBs, must have the TEXT_EMBEDDING_EXTRACTION purpose. + For unmanaged KBs, may be omitted when the vector store type does not require embeddings. :param Optional[dict] settings: Additional settings for the knowledge bank, including: + * "managed" (bool) set to False to create an unmanaged knowledge bank * "connection" (str) the connection name, for remote vector stores * "indexName" (str) the index name, for remote vector stores, including Pinecone * "pineconeIndexName" (str) legacy alias accepted for Pinecone vector stores + * "columnMapping" (dict) mappings for vector store columns, used only for unmanaged knowledge banks. Expected format: + ``{"id": "", "vector": "", "content": "", "metadata": ""}`` + (keys depend on the selected vector store type; values are remote schema column names) + * "metadataColumnsSchema" (list[dict]) metadata schema, where each item has format: + ``{"name": "", "type": ""}`` + Optional per-column keys include ``"filterable"`` (bool) and ``"storageName"`` (str, physical + column path when different from ``name``). Validation always prefers ``storageName`` when provided. + Without ``storageName``, ``name`` is accepted only when it resolves unambiguously to a single remote + column path. + If a top-level column and a nested mapped metadata field share the same name, ``storageName`` is + required. * "managedFolderId" (str) the id of the managed folder containing the extracted images. The images may be referenced by their path in the knowledge bank, and stored in this folder. + For unmanaged knowledge banks, the caller is responsible for checking index existence and schema + compatibility before creation. + + Example managed knowledge bank creation: + + .. code-block:: python + + kb = project.create_knowledge_bank( + name="my_managed_kb", + vector_store_type="CHROMA", + embedding_llm_id="MY_EMBEDDING_LLM_ID" + ) + + Example unmanaged knowledge bank creation: + + .. code-block:: python + + settings = { + "managed": False, + "connection": "my_vector_store_connection", + "indexName": "my_existing_index", + "columnMapping": { + "id": "doc_id", + "vector": "embedding", + "content": "text", + "metadata": "metadata_json" + }, + "metadataColumnsSchema": [ + {"name": "source", "type": "string", "storageName": "metadata_json.source", "filterable": True}, + {"name": "author", "type": "string", "filterable": True} + ] + } + + kb = project.create_knowledge_bank( + name="my_unmanaged_kb", + vector_store_type="ELASTICSEARCH", + embedding_llm_id="MY_EMBEDDING_LLM_ID", + settings=settings + ) + :returns: a :class:`dataikuapi.dss.knowledgebank.DSSKnowledgeBank` handle to interact with the newly-created knowledge bank """ - if settings is None: - settings = {} + settings = dict(settings or {}) settings['name'] = name settings['vectorStoreType'] = vector_store_type - settings['embeddingLLMId'] = embedding_llm_id + if embedding_llm_id is not None: + settings['embeddingLLMId'] = embedding_llm_id if "managedFolderId" in settings: settings["multimodalColumn"] = "DKU_MULTIMODAL_CONTENT" diff --git a/dataikuapi/dssclient.py b/dataikuapi/dssclient.py index 35ddbb13..0827b51a 100644 --- a/dataikuapi/dssclient.py +++ b/dataikuapi/dssclient.py @@ -43,7 +43,8 @@ class DSSClient(object): """Entry point for the DSS API client""" - def __init__(self, host, api_key=None, internal_ticket=None, extra_headers=None, no_check_certificate=False, client_certificate=None, **kwargs): + def __init__(self, host, api_key=None, internal_ticket=None, extra_headers=None, no_check_certificate=False, client_certificate=None, + jwt_bearer_token=None, **kwargs): """Initialize a new DSS API client. Args: @@ -68,6 +69,7 @@ def __init__(self, host, api_key=None, internal_ticket=None, extra_headers=None, self.api_key = api_key self.internal_ticket = internal_ticket + self.jwt_bearer_token = jwt_bearer_token self.host = host self._session = Session() if no_check_certificate: @@ -79,8 +81,10 @@ def __init__(self, host, api_key=None, internal_ticket=None, extra_headers=None, self._session.auth = HTTPBasicAuth(self.api_key, "") elif self.internal_ticket is not None: self._session.headers.update({"X-DKU-APITicket" : self.internal_ticket}) + elif jwt_bearer_token is not None: + self._session.headers.update({"Authorization": "Bearer " + jwt_bearer_token}) else: - raise ValueError("API Key is required") + raise ValueError("Authentication is required. You must provide either api_key, internal_ticket or jwt_bearer_token.") if extra_headers is not None: self._session.headers.update(extra_headers) @@ -1728,6 +1732,9 @@ def get_ticket_from_browser_headers(self, headers_dict): """ return self._perform_json("POST", "/auth/ticket-from-browser-headers", body=headers_dict)["msg"] + def get_ticket(self): + """Get a temporary auth ticket for the current user""" + return self._perform_json("POST", "/auth/ticket")["ticket"] ######################################################## # Container execution @@ -1828,9 +1835,6 @@ def get_licensing_status(self): """ Returns a dictionary with information about licensing status of this DSS instance - Note: - The API is not available on Cloud. Use the Launchpad or Launchpad API. - :rtype: dict """ return self._perform_json("GET", "/admin/licensing/status") @@ -1839,9 +1843,6 @@ def set_license(self, license): """ Sets a new licence for DSS - Note: - The API is not available on Cloud. Use the Launchpad or Launchpad API. - :param license: license (content of license file) :return: None """ diff --git a/dataikuapi/govern/artifact.py b/dataikuapi/govern/artifact.py index 5a3f7186..6841f92b 100644 --- a/dataikuapi/govern/artifact.py +++ b/dataikuapi/govern/artifact.py @@ -169,8 +169,9 @@ def get_details(self): def update_status(self, signoff_status, users_to_notify=None, reload_conf_for_reset=False): """ - Change the status of the sign-off, takes as input the target status, optionally a list of users to notify and a boolean to indicate if the sign-off configuration should be updated from the blueprint version. - Only the users included in the groups of feedback and approval are able to give feedback or approval and can be notified, + Change the status of the sign-off, takes as input the target status, optionally the list of users to notify and a boolean to indicate if the sign-off configuration should be updated from the blueprint version. + If users_to_notify is not set, all users configured to give feedback or approval will be notified. + Additionally, only the users included in the feedback groups and the approval list can be notified (any other user will be ignored), the complete list is available using: :meth:`~dataikuapi.govern.artifact.GovernArtifactSignoff.get_details`. For the feedback, the users will be notified as part of a chosen group of feedback and the group must be specified. @@ -180,14 +181,13 @@ def update_status(self, signoff_status, users_to_notify=None, reload_conf_for_re The list should be a list of dict containing two keys "userLogin" and "groupId" for each user to notify. The "groupId" key is mandatory for feedback notification and forbidden for the final approval notification. All users that are not in the sign-off configuration will be ignored. - :param boolean reload_conf_for_reset: (Optional, defaults to **False**) Usefull only when the target status is NOT_STARTED. + If no users_to_notify is provided, all users that are configured to give feedback or approval will be notified. + :param boolean reload_conf_for_reset: (Optional, defaults to **False**) Useful only when the target status is NOT_STARTED. If True the current sign-off configuration will be overwritten by the one coming from the blueprint version, all delegated users will be reset. If False the current sign-off configuration will remain the same, allowing all delegated users to be retained but any changes to the sign-off configuration in the blueprint version will not be reflected. :type users_to_notify: list of dict :return: None """ - if users_to_notify is None: - users_to_notify = [] self.client._perform_json("POST", "/artifact/%s/workflow/step/%s/signoff/update-status" % (self.artifact_id, self.step_id), body={"targetStatus": signoff_status, "usersToSendEmailTo": users_to_notify, "reloadConfForReset": reload_conf_for_reset}) diff --git a/dataikuapi/govern_client.py b/dataikuapi/govern_client.py index a9955f51..46d21db9 100644 --- a/dataikuapi/govern_client.py +++ b/dataikuapi/govern_client.py @@ -673,9 +673,6 @@ def get_licensing_status(self): """ Returns a dictionary with information about licensing status of this Dataiku Govern instance - Note: - The API is not available on Cloud. Use the Launchpad or Launchpad API. - :rtype: dict """ return self._perform_json("GET", "/admin/licensing/status") @@ -684,9 +681,6 @@ def set_license(self, license): """ Sets a new licence for Dataiku Govern - Note: - The API is not available on Cloud. Use the Launchpad or Launchpad API. - :param license: license (content of license file) :return: None """ From 269f0e2a48b9d94094828386490e07d1e5ca51a2 Mon Sep 17 00:00:00 2001 From: Kevin REMY Date: Thu, 11 Jun 2026 22:41:30 +0200 Subject: [PATCH 2/2] Release metadata --- HISTORY.txt | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.txt b/HISTORY.txt index ee400983..6f245d38 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -2,6 +2,11 @@ Changelog ========== +14.6.2 (2026-06-12) +------------------- + +* Initial release for DSS 14.6.2 + 14.6.1 (2026-06-05) ------------------- diff --git a/setup.py b/setup.py index 1ba6224e..5a89359c 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup -VERSION = "14.6.1" +VERSION = "14.6.2" long_description = (open('README').read() + '\n\n' + open('HISTORY.txt').read())