Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ Changelog
==========


14.6.2 (2026-06-12)
-------------------

* Initial release for DSS 14.6.2

14.6.1 (2026-06-05)
-------------------

Expand Down
11 changes: 10 additions & 1 deletion dataikuapi/dss/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -36,6 +36,8 @@ def name(self):

:rtype: string
"""
if "name" in self:
return self["name"]
return self["id"]

@property
Expand All @@ -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):
Expand Down Expand Up @@ -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")

Expand Down
1 change: 1 addition & 0 deletions dataikuapi/dss/llm_tracing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions dataikuapi/dss/messaging_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
16 changes: 8 additions & 8 deletions dataikuapi/dss/ml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
64 changes: 59 additions & 5 deletions dataikuapi/dss/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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": "<column name>", "vector": "<column name>", "content": "<column name>", "metadata": "<column name>"}``
(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": "<column name>", "type": "<column 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"
Expand Down
17 changes: 9 additions & 8 deletions dataikuapi/dssclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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
"""
Expand Down
10 changes: 5 additions & 5 deletions dataikuapi/govern/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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})
Expand Down
6 changes: 0 additions & 6 deletions dataikuapi/govern_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
"""
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down