Skip to content

Commit

Permalink
Merge branch 'dev' into jk-dev-kb
Browse files Browse the repository at this point in the history
  • Loading branch information
JustKuzya authored Feb 13, 2025
2 parents 89a6c77 + fca6f4b commit 4134003
Show file tree
Hide file tree
Showing 58 changed files with 1,272 additions and 444 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Dioptra is a software test platform for assessing the trustworthy characteristics of artificial intelligence (AI).
Trustworthy AI is: valid and reliable, safe, secure and resilient, accountable and transparent, explainable and interpretable, privacy-enhanced, and fair - with harmful bias managed[^1].
Dioptra supports the Measure function of the [NIST AI Risk Management Framework](https://nist.gov/itl/ai-risk-management-framework/) by providing functionality to assess, analyze, and track identified AI risks.
Dioptra supports the Measure function of the [NIST AI Risk Management Framework](https://nist.gov/itl/ai-risk-management-framework/) by providing functionality to assess, analyze, and track identified AI potential benefits and negative consequences.

Dioptra provides a REST API, which can be controlled via an intuitive web interface, a Python client, or any REST client library of the user's choice for designing, managing, executing, and tracking experiments.
Details are available in the project documentation available at <https://pages.nist.gov/dioptra/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ server {

location / {
root /frontend;
try_files $uri $uri/ /index.html;
}

location /api/ {
Expand Down
44 changes: 37 additions & 7 deletions docs/source/getting-started/running-dioptra.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ This will generate a setup that is appropriate for testing Dioptra on your perso
source venv-deploy/bin/activate
python -m pip install --upgrade pip cruft jinja2
# Run cruft and set the template's variables
cruft create https://github.com/usnistgov/dioptra --checkout $DIOPTRA_BRANCH \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment
Next, run cruft to begin the deployment process. The following command will run cruft and use all of the default template values except for the `datasets_directory`. If you wish to configure the deployment in a different manner, see the :ref:`Applying the template <getting-started-running-dioptra-applying-the-template>` section for detailed description of the template values and how to configure them.

We recommend identifying a location to store datasets you will want to use with Dioptra at this point and setting the `datasets_directory` variable accordingly. See the :ref:`Downloading the datasets <getting-started-acquiring-datasets>` section for more details.

.. code:: sh
Cruft will now run and prompt you to configure the deployment. See the :ref:`Applying the template <getting-started-running-dioptra-applying-the-template>` section for detailed description of each prompt.
cruft create https://github.com/usnistgov/dioptra --checkout main \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment --no-input \
--extra-context '{"datasets_directory": "/datasets"}'
We recommend identifying a location to store datasets you will want to use with Dioptra at this point and setting the ``datasets_directory`` variable accordingly. See the :ref:`Downloading the datasets <getting-started-acquiring-datasets>` section for more details.
Once you have configured your deployment, continue following the instructions for initializing and starting your deployment below.

Expand Down Expand Up @@ -96,14 +99,41 @@ Applying the template
---------------------

Create the folder where you plan to keep the deployment folder and change into it so that it becomes your working directory.
Next, run cruft and apply the Dioptra Deployment template.
Next, run cruft and apply the Dioptra Deployment template. There are four different methods for configuring the deployment.

1) Have cruft ask for each variable:

.. code:: sh
# To use the template published on the dev branch, use '--checkout dev' instead
cruft create https://github.com/usnistgov/dioptra --checkout main \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment
2) Have cruft automatically apply the default template values:

.. code:: sh
cruft create https://github.com/usnistgov/dioptra --checkout main \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment --no-input
3) Have cruft use default values except for those specified:

.. code:: sh
cruft create https://github.com/usnistgov/dioptra --checkout main \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment --no-input \
--extra-context '{"datasets_directory": "~/datasets"}'
4) Have cruft use default values except for those given in a file:

.. code:: sh
cruft create https://github.com/usnistgov/dioptra --checkout main \
--directory cookiecutter-templates/cookiecutter-dioptra-deployment --no-input \
--extra-context-file overrides.json
If you selected 1) above, you will now be asked to set the variables needed to customize the generated configuration files.

.. margin::

.. note::
Expand All @@ -113,7 +143,6 @@ Next, run cruft and apply the Dioptra Deployment template.
If it has, remove it.
To start over, re-run the ``cruft`` command.

You will now be asked to set the variables needed to customize the generated configuration files.
In most cases you can just use the default value, but there are a few that you may need to customize.
Below is a full list of the variables, their default values, and explanations of what they mean.

Expand Down Expand Up @@ -173,6 +202,7 @@ Below is a full list of the variables, their default values, and explanations of
If the provided path is not valid, the `init-deployment.sh` script will fail.
More advanced configurations can be achieved by modifying the `docker-compose.override.yml` file.
(default: ``""``)
See the :ref:`Downloading the datasets <getting-started-acquiring-datasets>` section for more details.

Example
~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion docs/source/overview/executive-summary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
.. https://creativecommons.org/licenses/by/4.0/legalcode
Dioptra is a software test platform for assessing the trustworthy characteristics of artificial intelligence (AI). Trustworthy AI is: valid and reliable, safe, secure and resilient, accountable and transparent, explainable and interpretable, privacy-enhanced, and fair - with harmful bias managed. Dioptra supports the Measure function of the NIST AI Risk Management Framework by providing functionality to assess, analyze, and track identified AI risks.
Dioptra is a software test platform for assessing the trustworthy characteristics of artificial intelligence (AI). Trustworthy AI is: valid and reliable, safe, secure and resilient, accountable and transparent, explainable and interpretable, privacy-enhanced, and fair - with harmful bias managed\ [#f1]_\ . Dioptra supports the Measure function of the NIST AI Risk Management Framework by providing functionality to assess, analyze, and track identified AI potential benefits and negative consequences.

Use Cases
---------
Expand Down
4 changes: 4 additions & 0 deletions src/dioptra/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class DioptraClientError(Exception):
"""Base class for client errors"""


class FieldNameCollisionError(DioptraClientError):
"""Raised when two field names will collide after conversion to camel case."""


class FieldsValidationError(DioptraClientError):
"""Raised when one or more fields are invalid."""

Expand Down
78 changes: 72 additions & 6 deletions src/dioptra/client/drafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
CollectionClient,
DioptraClientError,
DioptraSession,
FieldNameCollisionError,
FieldsValidationError,
SubCollectionClient,
SubCollectionUrlError,
Expand All @@ -33,11 +34,61 @@ class DraftFieldsValidationError(DioptraClientError):
"""Raised when one or more draft fields are invalid."""


class ConvertFieldNamesToCamelCaseProtocol(Protocol):
def __call__(self, json_: dict[str, Any]) -> dict[str, Any]:
... # fmt: skip


class ValidateDraftFieldsProtocol(Protocol):
def __call__(self, json_: dict[str, Any]) -> dict[str, Any]:
... # fmt: skip


def make_field_names_to_camel_case_converter(
name_mapping: dict[str, str],
) -> ConvertFieldNamesToCamelCaseProtocol:
"""
Create a function that uses a dictionary to convert field names passed to the client
to camel case.
Args:
name_mapping: A dictionary that maps the draft field names to their camel case
equivalents.
Returns:
A function for converting the draft fields names to camel case.
"""

def convert_field_names(json_: dict[str, Any]) -> dict[str, Any]:
"""Convert the names of the provided draft fields to camel case.
Args:
json_: The draft fields to convert.
Returns:
The draft fields with names converted to camel case.
"""
if not name_mapping:
return json_

converted_json_: dict[str, Any] = {}
conversions_seen: dict[str, str] = {}

for key, value in json_.items():
if (converted_key := name_mapping.get(key, key)) in conversions_seen:
raise FieldNameCollisionError(
f"Camel case conversion failed (reason: duplicates "
f"{conversions_seen[converted_key]} after conversion): {key}"
)

converted_json_[converted_key] = value
conversions_seen[converted_key] = key

return converted_json_

return convert_field_names


def make_draft_fields_validator(
draft_fields: set[str], resource_name: str
) -> ValidateDraftFieldsProtocol:
Expand Down Expand Up @@ -102,6 +153,7 @@ def __init__(
validate_fields_fn: ValidateDraftFieldsProtocol,
root_collection: CollectionClient[T],
parent_sub_collections: list[SubCollectionClient[T]] | None = None,
convert_field_names_fn: ConvertFieldNamesToCamelCaseProtocol | None = None,
) -> None:
"""Initialize the NewResourceDraftsSubCollectionClient instance.
Expand All @@ -122,6 +174,9 @@ def __init__(
self._parent_sub_collections: list[SubCollectionClient[T]] = (
parent_sub_collections or []
)
self._convert_field_names = (
convert_field_names_fn or make_field_names_to_camel_case_converter({})
)

def get(
self,
Expand Down Expand Up @@ -198,8 +253,7 @@ def create(
DraftFieldsValidationError: If one or more draft fields are invalid or
missing.
"""

if "group" in kwargs:
if "group" in (kwargs := self._convert_field_names(kwargs)):
raise FieldsValidationError(
"Invalid argument (reason: keyword is reserved): group"
)
Expand Down Expand Up @@ -228,7 +282,7 @@ def modify(self, *resource_ids: str | int, draft_id: int, **kwargs) -> T:
DraftFieldsValidationError: If one or more draft fields are invalid or
missing.
"""
if "draftId" in kwargs:
if "draftId" in (kwargs := self._convert_field_names(kwargs)):
raise FieldsValidationError(
"Invalid argument (reason: keyword is reserved): draftId"
)
Expand Down Expand Up @@ -322,6 +376,7 @@ def __init__(
validate_fields_fn: ValidateDraftFieldsProtocol,
root_collection: CollectionClient[T],
parent_sub_collections: list[SubCollectionClient[T]] | None = None,
convert_field_names_fn: ConvertFieldNamesToCamelCaseProtocol | None = None,
) -> None:
"""Initialize the ModifyResourceDraftsSubCollectionClient instance.
Expand All @@ -342,6 +397,9 @@ def __init__(
parent_sub_collections=parent_sub_collections,
)
self._validate_fields = validate_fields_fn
self._convert_field_names = (
convert_field_names_fn or make_field_names_to_camel_case_converter({})
)

def get_by_id(self, *resource_ids: str | int) -> T:
"""Get a resource modification draft.
Expand Down Expand Up @@ -370,14 +428,17 @@ def create(self, *resource_ids: str | int, **kwargs) -> T:
"""
return self._session.post(
self.build_sub_collection_url(*resource_ids),
json_=self._validate_fields(kwargs),
json_=self._validate_fields(self._convert_field_names(kwargs)),
)

def modify(self, *resource_ids: str | int, **kwargs) -> T:
def modify(
self, *resource_ids: str | int, resource_snapshot_id: str | int, **kwargs
) -> T:
"""Modify a resource modification draft.
Args:
*resource_ids: The parent resource ids that own the sub-collection.
resource_snapshot_id: The resource snapshot id the draft is based on.
**kwargs: The draft fields to modify.
Returns:
Expand All @@ -389,7 +450,12 @@ def modify(self, *resource_ids: str | int, **kwargs) -> T:
"""
return self._session.put(
self.build_sub_collection_url(*resource_ids),
json_=self._validate_fields(kwargs),
json_={
"resourceSnapshot": int(resource_snapshot_id),
"resourceData": self._validate_fields(
self._convert_field_names(kwargs)
),
},
)

def delete(self, *resource_ids: str | int) -> T:
Expand Down
15 changes: 14 additions & 1 deletion src/dioptra/client/entrypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
ModifyResourceDraftsSubCollectionClient,
NewResourceDraftsSubCollectionClient,
make_draft_fields_validator,
make_field_names_to_camel_case_converter,
)
from .snapshots import SnapshotsSubCollectionClient
from .tags import TagsSubCollectionClient
Expand All @@ -38,6 +39,9 @@
"queues",
"plugins",
}
FIELD_NAMES_TO_CAMEL_CASE: Final[dict[str, str]] = {
"task_graph": "taskGraph",
}

T = TypeVar("T")

Expand Down Expand Up @@ -280,6 +284,9 @@ def __init__(self, session: DioptraSession[T]) -> None:
resource_name=self.name,
),
root_collection=self,
convert_field_names_fn=make_field_names_to_camel_case_converter(
name_mapping=FIELD_NAMES_TO_CAMEL_CASE
),
)
self._modify_resource_drafts = ModifyResourceDraftsSubCollectionClient[T](
session=session,
Expand All @@ -288,6 +295,9 @@ def __init__(self, session: DioptraSession[T]) -> None:
resource_name=self.name,
),
root_collection=self,
convert_field_names_fn=make_field_names_to_camel_case_converter(
name_mapping=FIELD_NAMES_TO_CAMEL_CASE
),
)
self._snapshots = SnapshotsSubCollectionClient[T](
session=session, root_collection=self
Expand Down Expand Up @@ -350,7 +360,10 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/entrypoints/1/draft
client.entrypoints.modify_resource_drafts.modify(
1, name="new-name", description="new-description"
1,
resource_snapshot_id=1,
name="new-name",
description="new-description"
)
# POST /api/v1/entrypoints/1/draft
Expand Down
5 changes: 4 additions & 1 deletion src/dioptra/client/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,10 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/experiments/1/draft
client.experiments.modify_resource_drafts.modify(
1, name="new-name", description="new-description"
1,
resource_snapshot_id=1,
name="new-name",
description="new-description"
)
# POST /api/v1/experiments/1/draft
Expand Down
5 changes: 4 additions & 1 deletion src/dioptra/client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,10 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/models/1/draft
client.models.modify_resource_drafts.modify(
1, name="new-name", description="new-description"
1,
resource_snapshot_id=1,
name="new-name",
description="new-description"
)
# POST /api/v1/models/1/draft
Expand Down
6 changes: 5 additions & 1 deletion src/dioptra/client/plugin_parameter_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/pluginParameterTypes/1/draft
client.plugin_parameter_types.modify_resource_drafts.modify(
1, name="new-name", description="new-description", structure=None
1,
resource_snapshot_id=1,
name="new-name",
description="new-description",
structure=None
)
# POST /api/v1/pluginParameterTypes/1/draft
Expand Down
6 changes: 5 additions & 1 deletion src/dioptra/client/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
client.plugins.files.modify_resource_drafts.modify(
1,
2,
resource_snapshot_id=1,
filename="new_name.py",
contents="",
tasks=[],
Expand Down Expand Up @@ -465,7 +466,10 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/plugins/1/draft
client.plugins.modify_resource_drafts.modify(
1, name="new-name", description="new-description"
1,
resource_snapshot_id=1,
name="new-name",
description="new-description"
)
# POST /api/v1/plugins/1/draft
Expand Down
5 changes: 4 additions & 1 deletion src/dioptra/client/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ def modify_resource_drafts(self) -> ModifyResourceDraftsSubCollectionClient[T]:
# PUT /api/v1/queues/1/draft
client.queues.modify_resource_drafts.modify(
1, name="new-name", description="new-description"
1,
resource_snapshot_id=1,
name="new-name",
description="new-description"
)
# POST /api/v1/queues/1/draft
Expand Down
Loading

0 comments on commit 4134003

Please sign in to comment.