Repository: kubeflow/model-registry

Issue:
[Proposal] Introduce Tilt as a dev environment
**Is your feature request related to a problem? Please describe.**
Working with microservices can be quite cumbersome, especially when developers need to manage multiple services simultaneously. I am often frustrated by the inefficiency of manually rebuilding and redeploying services to see updates. This slows down development and increases context switching.

**Describe the solution you'd like**
Integrating Tilt as a development environment could solve this problem. Tilt provides an intuitive interface for managing multiple services, enabling live updates with minimal effort. This would allow developers to streamline their workflow, debug issues faster, and focus more on coding rather than manual build processes.

**Describe alternatives you've considered**
I've looked at using Kubernetes directly to manage the development workflow. While they work well, neither provides the real-time feedback and easy-to-use interface that Tilt provides.

**Additional context**

I've opened a [PR](https://github.com/kubeflow/model-registry/pull/850) so that it's possible to locally test and provide feedback on this proposal!

NOTE: I have tested it on my linux system, I do not have a machine to test it on arm, so volunteers are welcome 🙏🏼 


Hints from issue comments:
@syntaxsdev: +1, I agree. This is a good enhancement as it will allow faster development and abstract away the actual local deployment. Nice work on the PR for this!@Crazyglue: +1, i played with the pr locally and it seemed to spin up the whole stack quite nicely. Made testing a breeze. I'm also glad that the local clients are built in the wheel so even for python client testing the tilt stack works.


Solution patch:
diff --git a/.github/workflows/build-image-ui-pr.yml b/.github/workflows/build-image-ui-pr.yml
new file mode 100644
index 00000000..08fa3984
--- /dev/null
+++ b/.github/workflows/build-image-ui-pr.yml
@@ -0,0 +1,25 @@
+name: Test UI container image build and deployment
+on:
+  workflow_dispatch:
+  pull_request:
+    paths:
+      - "clients/ui/**"
+      - "!LICENSE*"
+      - "!DOCKERFILE*"
+      - "!**.gitignore"
+      - "!**.md"
+      - "!**.txt"
+env:
+  IMG_ORG: kubeflow
+  IMG_REPO: model-registry-ui
+  PUSH_IMAGE: false
+  BRANCH: ${{ github.base_ref }}
+jobs:
+  build-image:
+    runs-on: ubuntu-latest
+    steps:
+      # checkout branch
+      - uses: actions/checkout@v4
+      - name: Build UI Image
+        shell: bash
+        run: ./scripts/build_deploy.sh
diff --git a/api/openapi/model-registry.yaml b/api/openapi/model-registry.yaml
index b75466cb..aca8ce03 100644
--- a/api/openapi/model-registry.yaml
+++ b/api/openapi/model-registry.yaml
@@ -1550,6 +1550,35 @@ components:
             serviceAccountName:
               description: Name of the service account with storage secret.
               type: string
+            modelSourceKind:
+              type: string
+              description: |-
+                A string identifier describing the source kind. It differentiates various sources of model artifacts.
+                This identifier should be agreed upon by producers and consumers of source model metadata.
+                It is not an enumeration to keep the source of model metadata open ended. 
+                E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.
+            modelSourceClass:
+              type: string
+              description: |-
+                A subgroup within the source kind. It is a specific sub-component or instance within the source kind.
+                E.g. `pipelinerun` for a Kubeflow pipeline run.
+            modelSourceGroup:
+              type: string
+              description: |-
+                Unique identifier for a source group for models from source class. 
+                It maps to a physical group of source models. 
+                E.g. a Kubernetes namespace where the pipeline run was executed.
+            modelSourceId:
+              type: string
+              description: |-
+                A unique identifier for a source model within kind, class, and group.
+                It should be a url friendly string if source supports using URLs to locate source models.
+                E.g. a pipeline run ID.
+            modelSourceName:
+              type: string
+              description: |-
+                A human-readable name for the source model. 
+                E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.
     ModelArtifactCreate:
       description: An ML model artifact.
       properties:
@@ -1658,9 +1687,16 @@ components:
         - $ref: "#/components/schemas/BaseResourceUpdate"
     ServingEnvironmentCreate:
       description: A Model Serving environment for serving `RegisteredModels`.
+      required:
+      - name
       allOf:
         - $ref: "#/components/schemas/BaseResourceCreate"
         - $ref: "#/components/schemas/ServingEnvironmentUpdate"
+        - type: object  
+          properties:
+            name:
+              description: The name of the ServingEnvironment.
+              type: string  
     InferenceService:
       description: >-
         An `InferenceService` entity in a `ServingEnvironment` represents a deployed `ModelVersion` from a `RegisteredModel` created by Model Serving.
diff --git a/clients/python/README.md b/clients/python/README.md
index 09ff76b0..a7c5c066 100644
--- a/clients/python/README.md
+++ b/clients/python/README.md
@@ -178,11 +178,117 @@ By default, all queries will be `ascending`, but this method is also available f
 > Advanced usage note: You can also set the `page_size()` that you want the Pager to use when invoking the Model Registry backend.
 > When using it as an iterator, it will automatically manage pages for you.
 
+### Uploading local models to external storage and registering them
+
+To both upload and register a model, use the convenience method `upload_artifact_and_register_model`.
+
+This method supports both s3-based storage (via [boto3](https://github.com/boto/boto3)) as well as OCI-based image registries (via [olot](https://github.com/containers/olot), using either of the CLI tools [skopeo](https://github.com/containers/skopeo) or [oras](https://github.com/oras-project/oras))
+
+In order to utilize this method you must instantiate an `upload_params` object which contains the necessary locations and credentials needed to perform the upload to that storage provider.
+
+#### S3 based external storage
+
+Common S3 env vars will be automatically read, such ass the access_key_id, etc. It can also be provided explicitly in the `S3Params` object if desired.
+
+```python
+s3_upload_params = S3Params(
+    bucket="my-bucket",
+    s3_prefix="models/my_fraud_model",
+)
+
+registered_model = client.upload_artifact_and_register_model(
+    name="hello_world_model",
+    model_fiels_path="/home/user-01/models/model_training_01"
+    # If the model consists of a single file, such as a .onnx file, you can specify that as well
+    # model_fiels_path="/home/user-01/models/model_training_01.onnx"
+    author="Mr. Trainer",
+    version="0.0.1",
+    upload_params=s3_upload_params
+)
+```
+
+#### OCI-registry based storage
+
+```python
+oci_upload_params = OCIParams(
+    base_image="busybox",
+    oci_ref="registry.example.com/acme_org/hello_world_model:0.0.1"
+)
+
+registered_model = client.upload_artifact_and_register_model(
+    name="hello_world_model",
+    model_fiels_path="/home/user-01/models/model_training_01"
+    # If the model consists of a single file, such as a .onnx file, you can specify that as well
+    # model_fiels_path="/home/user-01/models/model_training_01.onnx"
+    author="Mr. Trainer",
+    version="0.0.1",
+    upload_params=oci_upload_params
+)
+```
+
+Additionally, OCI-based storage supports multiple CLI clients to perform the upload. However, one of these clients must be available in the hosts PATH. **Ensure your host has either [skopeo](https://github.com/containers/skopeo) or [oras](https://github.com/oras-project/oras) installed and available.** 
+
+By default, `skopeo` is used to perform the OCI image download/upload.
+
+If you prefer to use `oras` instead, you can specify it like so:
+
+```python
+oci_upload_params = OCIParams(
+    base_image="busybox",
+    oci_ref="registry.example.com/acme_org/hello_world_model:0.0.1",
+    backend="oras"
+)
+```
+
+Additionally, if neither of these CLI clients are sufficient for you, you can provide a `custom_oci_backend` in the `OCIParams` and specify the appropriate methods
+
+```python
+def is_available():
+    pass
+def pull():
+    pass
+def push():
+    pass
+
+custom_oci_backend = {
+    "is_available": is_available,
+    "pull": pull,
+    "push": push,
+}
+
+oci_upload_params = OCIParams(
+    base_image="busybox",
+    oci_ref="registry.example.com/acme_org/hello_world_model:0.0.1",
+    custom_oci_backend=custom_oci_backend,
+)
+```
+
 #### Implementation notes
 
 The pager will manage pages for you in order to prevent infinite looping.
 Currently, the Model Registry backend treats model lists as a circular buffer, and **will not end iteration** for you.
 
+
+### Running ModelRegistry on Ray or Uvloop
+When running `ModelRegistry` on a platform that sets a custom event loop that cannot be nested, an error will occur.
+
+To solve this, you can specify a custom `async_runner` when initializing the client, one that is compatible with your environment.
+
+`async_runner` is a function or a method that takes in a coroutine.
+
+
+Example of an async runner compatible with Ray or Uvloop can be found [here](tests/extras/async_task_runner.py) in `tests/extras`.
+
+Example usage:
+```py
+atr = AsyncTaskRunner()
+registry = ModelRegistry("http://server-address", 8080, author="Ada Lovelace", async_runner=atr.run)
+```
+
+See also the [test case](tests/test_client.py#L854) in `test_custom_async_runner_with_ray`.
+
+Please keep in mind, the `AsyncTaskRunner` used here for testing does not ship within the library so you will need to copy it into your code directly or import from elsewhere.
+
 ## Development
 
 ### Using the Makefile
diff --git a/clients/python/noxfile.py b/clients/python/noxfile.py
index 3a27a8b7..66aac2fd 100644
--- a/clients/python/noxfile.py
+++ b/clients/python/noxfile.py
@@ -58,6 +58,7 @@ def tests(session: Session) -> None:
         "requests",
         "pytest",
         "pytest-asyncio",
+        "uvloop",
         "olot",
     )
     session.run(
@@ -72,6 +73,7 @@ def e2e_tests(session: Session) -> None:
     """Run the test suite."""
     session.install(
         ".",
+        "ray",
         "requests",
         "pytest",
         "pytest-asyncio",
@@ -80,6 +82,7 @@ def e2e_tests(session: Session) -> None:
         "huggingface-hub",
         "boto3",
         "olot",
+        "uvloop",
     )
     try:
         session.run(
diff --git a/clients/python/poetry.lock b/clients/python/poetry.lock
index 7954ada4..78c8a58b 100644
--- a/clients/python/poetry.lock
+++ b/clients/python/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
 
 [[package]]
 name = "aiohappyeyeballs"
@@ -6,6 +6,7 @@ version = "2.3.4"
 description = "Happy Eyeballs for asyncio"
 optional = false
 python-versions = "<4.0,>=3.8"
+groups = ["main"]
 files = [
     {file = "aiohappyeyeballs-2.3.4-py3-none-any.whl", hash = "sha256:40a16ceffcf1fc9e142fd488123b2e218abc4188cf12ac20c67200e1579baa42"},
     {file = "aiohappyeyeballs-2.3.4.tar.gz", hash = "sha256:7e1ae8399c320a8adec76f6c919ed5ceae6edd4c3672f4d9eae2b27e37c80ff6"},
@@ -13,92 +14,93 @@ files = [
 
 [[package]]
 name = "aiohttp"
-version = "3.11.12"
+version = "3.11.14"
 description = "Async http client/server framework (asyncio)"
 optional = false
 python-versions = ">=3.9"
-files = [
-    {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"},
-    {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"},
-    {file = "aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957"},
-    {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42"},
-    {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55"},
-    {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb"},
-    {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae"},
-    {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf"},
-    {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff"},
-    {file = "aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d"},
-    {file = "aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5"},
-    {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb"},
-    {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9"},
-    {file = "aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933"},
-    {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1"},
-    {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94"},
-    {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6"},
-    {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5"},
-    {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804"},
-    {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b"},
-    {file = "aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16"},
-    {file = "aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6"},
-    {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250"},
-    {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1"},
-    {file = "aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c"},
-    {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df"},
-    {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259"},
-    {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d"},
-    {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e"},
-    {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef"},
-    {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9"},
-    {file = "aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a"},
-    {file = "aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802"},
-    {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9"},
-    {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c"},
-    {file = "aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0"},
-    {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2"},
-    {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1"},
-    {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7"},
-    {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e"},
-    {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a"},
-    {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce"},
-    {file = "aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f"},
-    {file = "aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"},
-    {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b"},
-    {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78"},
-    {file = "aiohttp-3.11.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73"},
-    {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460"},
-    {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a"},
-    {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6"},
-    {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5"},
-    {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4"},
-    {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8"},
-    {file = "aiohttp-3.11.12-cp39-cp39-win32.whl", hash = "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462"},
-    {file = "aiohttp-3.11.12-cp39-cp39-win_amd64.whl", hash = "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798"},
-    {file = "aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0"},
+groups = ["main"]
+files = [
+    {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d"},
+    {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa"},
+    {file = "aiohttp-3.11.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f"},
+    {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0"},
+    {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc"},
+    {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7"},
+    {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628"},
+    {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3"},
+    {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b"},
+    {file = "aiohttp-3.11.14-cp310-cp310-win32.whl", hash = "sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990"},
+    {file = "aiohttp-3.11.14-cp310-cp310-win_amd64.whl", hash = "sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186"},
+    {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f296d637a50bb15fb6a229fbb0eb053080e703b53dbfe55b1e4bb1c5ed25d325"},
+    {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6cd1954ca2bbf0970f531a628da1b1338f594bf5da7e361e19ba163ecc4f3b"},
+    {file = "aiohttp-3.11.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:572def4aad0a4775af66d5a2b5923c7de0820ecaeeb7987dcbccda2a735a993f"},
+    {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c68e41c4d576cd6aa6c6d2eddfb32b2acfb07ebfbb4f9da991da26633a3db1a"},
+    {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b8bbfc8111826aa8363442c0fc1f5751456b008737ff053570f06a151650b3"},
+    {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b0a200e85da5c966277a402736a96457b882360aa15416bf104ca81e6f5807b"},
+    {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d173c0ac508a2175f7c9a115a50db5fd3e35190d96fdd1a17f9cb10a6ab09aa1"},
+    {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:413fe39fd929329f697f41ad67936f379cba06fcd4c462b62e5b0f8061ee4a77"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65c75b14ee74e8eeff2886321e76188cbe938d18c85cff349d948430179ad02c"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:321238a42ed463848f06e291c4bbfb3d15ba5a79221a82c502da3e23d7525d06"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59a05cdc636431f7ce843c7c2f04772437dd816a5289f16440b19441be6511f1"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:daf20d9c3b12ae0fdf15ed92235e190f8284945563c4b8ad95b2d7a31f331cd3"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:05582cb2d156ac7506e68b5eac83179faedad74522ed88f88e5861b78740dc0e"},
+    {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12c5869e7ddf6b4b1f2109702b3cd7515667b437da90a5a4a50ba1354fe41881"},
+    {file = "aiohttp-3.11.14-cp311-cp311-win32.whl", hash = "sha256:92868f6512714efd4a6d6cb2bfc4903b997b36b97baea85f744229f18d12755e"},
+    {file = "aiohttp-3.11.14-cp311-cp311-win_amd64.whl", hash = "sha256:bccd2cb7aa5a3bfada72681bdb91637094d81639e116eac368f8b3874620a654"},
+    {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:70ab0f61c1a73d3e0342cedd9a7321425c27a7067bebeeacd509f96695b875fc"},
+    {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:602d4db80daf4497de93cb1ce00b8fc79969c0a7cf5b67bec96fa939268d806a"},
+    {file = "aiohttp-3.11.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a8a0d127c10b8d89e69bbd3430da0f73946d839e65fec00ae48ca7916a31948"},
+    {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9f835cdfedcb3f5947304e85b8ca3ace31eef6346d8027a97f4de5fb687534"},
+    {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aa5c68e1e68fff7cd3142288101deb4316b51f03d50c92de6ea5ce646e6c71f"},
+    {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b512f1de1c688f88dbe1b8bb1283f7fbeb7a2b2b26e743bb2193cbadfa6f307"},
+    {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc9253069158d57e27d47a8453d8a2c5a370dc461374111b5184cf2f147a3cc3"},
+    {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b2501f1b981e70932b4a552fc9b3c942991c7ae429ea117e8fba57718cdeed0"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:28a3d083819741592685762d51d789e6155411277050d08066537c5edc4066e6"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0df3788187559c262922846087e36228b75987f3ae31dd0a1e5ee1034090d42f"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e73fa341d8b308bb799cf0ab6f55fc0461d27a9fa3e4582755a3d81a6af8c09"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:51ba80d473eb780a329d73ac8afa44aa71dfb521693ccea1dea8b9b5c4df45ce"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8d1dd75aa4d855c7debaf1ef830ff2dfcc33f893c7db0af2423ee761ebffd22b"},
+    {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41cf0cefd9e7b5c646c2ef529c8335e7eafd326f444cc1cdb0c47b6bc836f9be"},
+    {file = "aiohttp-3.11.14-cp312-cp312-win32.whl", hash = "sha256:948abc8952aff63de7b2c83bfe3f211c727da3a33c3a5866a0e2cf1ee1aa950f"},
+    {file = "aiohttp-3.11.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b420d076a46f41ea48e5fcccb996f517af0d406267e31e6716f480a3d50d65c"},
+    {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d14e274828561db91e4178f0057a915f3af1757b94c2ca283cb34cbb6e00b50"},
+    {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f30fc72daf85486cdcdfc3f5e0aea9255493ef499e31582b34abadbfaafb0965"},
+    {file = "aiohttp-3.11.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4edcbe34e6dba0136e4cabf7568f5a434d89cc9de5d5155371acda275353d228"},
+    {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7169ded15505f55a87f8f0812c94c9412623c744227b9e51083a72a48b68a5"},
+    {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad1f2fb9fe9b585ea4b436d6e998e71b50d2b087b694ab277b30e060c434e5db"},
+    {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20412c7cc3720e47a47e63c0005f78c0c2370020f9f4770d7fc0075f397a9fb0"},
+    {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dd9766da617855f7e85f27d2bf9a565ace04ba7c387323cd3e651ac4329db91"},
+    {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:599b66582f7276ebefbaa38adf37585e636b6a7a73382eb412f7bc0fc55fb73d"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b41693b7388324b80f9acfabd479bd1c84f0bc7e8f17bab4ecd9675e9ff9c734"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:86135c32d06927339c8c5e64f96e4eee8825d928374b9b71a3c42379d7437058"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04eb541ce1e03edc1e3be1917a0f45ac703e913c21a940111df73a2c2db11d73"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dc311634f6f28661a76cbc1c28ecf3b3a70a8edd67b69288ab7ca91058eb5a33"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69bb252bfdca385ccabfd55f4cd740d421dd8c8ad438ded9637d81c228d0da49"},
+    {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b86efe23684b58a88e530c4ab5b20145f102916bbb2d82942cafec7bd36a647"},
+    {file = "aiohttp-3.11.14-cp313-cp313-win32.whl", hash = "sha256:b9c60d1de973ca94af02053d9b5111c4fbf97158e139b14f1be68337be267be6"},
+    {file = "aiohttp-3.11.14-cp313-cp313-win_amd64.whl", hash = "sha256:0a29be28e60e5610d2437b5b2fed61d6f3dcde898b57fb048aa5079271e7f6f3"},
+    {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14fc03508359334edc76d35b2821832f092c8f092e4b356e74e38419dfe7b6de"},
+    {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92007c89a8cb7be35befa2732b0b32bf3a394c1b22ef2dff0ef12537d98a7bda"},
+    {file = "aiohttp-3.11.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6d3986112e34eaa36e280dc8286b9dd4cc1a5bcf328a7f147453e188f6fe148f"},
+    {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:749f1eb10e51dbbcdba9df2ef457ec060554842eea4d23874a3e26495f9e87b1"},
+    {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:781c8bd423dcc4641298c8c5a2a125c8b1c31e11f828e8d35c1d3a722af4c15a"},
+    {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:997b57e38aa7dc6caab843c5e042ab557bc83a2f91b7bd302e3c3aebbb9042a1"},
+    {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a8b0321e40a833e381d127be993b7349d1564b756910b28b5f6588a159afef3"},
+    {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8778620396e554b758b59773ab29c03b55047841d8894c5e335f12bfc45ebd28"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e906da0f2bcbf9b26cc2b144929e88cb3bf943dd1942b4e5af066056875c7618"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:87f0e003fb4dd5810c7fbf47a1239eaa34cd929ef160e0a54c570883125c4831"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7f2dadece8b85596ac3ab1ec04b00694bdd62abc31e5618f524648d18d9dd7fa"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:fe846f0a98aa9913c2852b630cd39b4098f296e0907dd05f6c7b30d911afa4c3"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ced66c5c6ad5bcaf9be54560398654779ec1c3695f1a9cf0ae5e3606694a000a"},
+    {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a40087b82f83bd671cbeb5f582c233d196e9653220404a798798bfc0ee189fff"},
+    {file = "aiohttp-3.11.14-cp39-cp39-win32.whl", hash = "sha256:95d7787f2bcbf7cb46823036a8d64ccfbc2ffc7d52016b4044d901abceeba3db"},
+    {file = "aiohttp-3.11.14-cp39-cp39-win_amd64.whl", hash = "sha256:22a8107896877212130c58f74e64b77f7007cb03cea8698be317272643602d45"},
+    {file = "aiohttp-3.11.14.tar.gz", hash = "sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c"},
 ]
 
 [package.dependencies]
@@ -112,7 +114,7 @@ propcache = ">=0.2.0"
 yarl = ">=1.17.0,<2.0"
 
 [package.extras]
-speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
+speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
 
 [[package]]
 name = "aiohttp-retry"
@@ -120,6 +122,7 @@ version = "2.9.1"
 description = "Simple retry client for aiohttp"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
 files = [
     {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"},
     {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"},
@@ -134,6 +137,7 @@ version = "1.3.1"
 description = "aiosignal: a list of registered asynchronous callbacks"
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "dev"]
 files = [
     {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
     {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
@@ -148,6 +152,7 @@ version = "0.7.16"
 description = "A light, configurable Sphinx theme"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"},
     {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"},
@@ -159,6 +164,7 @@ version = "0.7.0"
 description = "Reusable constraint types to use with typing.Annotated"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
     {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -170,6 +176,7 @@ version = "4.3.0"
 description = "High level compatibility layer for multiple asynchronous event loop implementations"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
     {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
@@ -183,7 +190,7 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
 
 [package.extras]
 doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
-test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""]
 trio = ["trio (>=0.23)"]
 
 [[package]]
@@ -192,6 +199,8 @@ version = "4.0.3"
 description = "Timeout context manager for asyncio programs"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version < \"3.11\""
 files = [
     {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
     {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
@@ -203,16 +212,17 @@ version = "21.4.0"
 description = "Classes Without Boilerplate"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+groups = ["main", "dev"]
 files = [
     {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
     {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
 ]
 
 [package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"]
+dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"]
 docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
-tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
+tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
+tests-no-zope = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
 
 [[package]]
 name = "babel"
@@ -220,6 +230,7 @@ version = "2.13.1"
 description = "Internationalization utilities"
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"},
     {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"},
@@ -237,6 +248,7 @@ version = "4.12.2"
 description = "Screen-scraping library"
 optional = false
 python-versions = ">=3.6.0"
+groups = ["docs"]
 files = [
     {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
     {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
@@ -255,6 +267,7 @@ version = "25.1.0"
 description = "The uncompromising code formatter."
 optional = false
 python-versions = ">=3.9"
+groups = ["dev"]
 files = [
     {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"},
     {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"},
@@ -301,6 +314,7 @@ version = "2024.7.4"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
     {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
@@ -312,6 +326,7 @@ version = "3.3.2"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 optional = false
 python-versions = ">=3.7.0"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
     {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
@@ -411,10 +426,12 @@ version = "8.1.7"
 description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
     {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
 ]
+markers = {main = "extra == \"olot\""}
 
 [package.dependencies]
 colorama = {version = "*", markers = "platform_system == \"Windows\""}
@@ -425,88 +442,91 @@ version = "0.4.6"
 description = "Cross-platform colored terminal text."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 ]
+markers = {main = "(extra == \"hf\" or extra == \"olot\") and platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""}
 
 [[package]]
 name = "coverage"
-version = "7.6.12"
+version = "7.7.0"
 description = "Code coverage measurement for Python"
 optional = false
 python-versions = ">=3.9"
-files = [
-    {file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
-    {file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
-    {file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"},
-    {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"},
-    {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"},
-    {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"},
-    {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"},
-    {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"},
-    {file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"},
-    {file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"},
-    {file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"},
-    {file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"},
-    {file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"},
-    {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"},
-    {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"},
-    {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"},
-    {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"},
-    {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"},
-    {file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"},
-    {file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"},
-    {file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"},
-    {file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"},
-    {file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"},
-    {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"},
-    {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"},
-    {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"},
-    {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"},
-    {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"},
-    {file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"},
-    {file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"},
-    {file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"},
-    {file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"},
-    {file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"},
-    {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"},
-    {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"},
-    {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"},
-    {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"},
-    {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"},
-    {file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"},
-    {file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"},
-    {file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"},
-    {file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"},
-    {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"},
-    {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"},
-    {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"},
-    {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"},
-    {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"},
-    {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"},
-    {file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"},
-    {file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"},
-    {file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"},
-    {file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"},
-    {file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"},
-    {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"},
-    {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"},
-    {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"},
-    {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"},
-    {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"},
-    {file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"},
-    {file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"},
-    {file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"},
-    {file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"},
-    {file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"},
+groups = ["dev"]
+files = [
+    {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"},
+    {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"},
+    {file = "coverage-7.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6c96d69928a3a6767fab8dc1ce8a02cf0156836ccb1e820c7f45a423570d98"},
+    {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f18d47641282664276977c604b5a261e51fefc2980f5271d547d706b06a837f"},
+    {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a1e18a85bd066c7c556d85277a7adf4651f259b2579113844835ba1a74aafd"},
+    {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70f0925c4e2bfc965369f417e7cc72538fd1ba91639cf1e4ef4b1a6b50439b3b"},
+    {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b0fac2088ec4aaeb5468b814bd3ff5e5978364bfbce5e567c44c9e2854469f6c"},
+    {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3e212a894d8ae07fde2ca8b43d666a6d49bbbddb10da0f6a74ca7bd31f20054"},
+    {file = "coverage-7.7.0-cp310-cp310-win32.whl", hash = "sha256:f32b165bf6dfea0846a9c9c38b7e1d68f313956d60a15cde5d1709fddcaf3bee"},
+    {file = "coverage-7.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:a2454b12a3f12cc4698f3508912e6225ec63682e2ca5a96f80a2b93cef9e63f3"},
+    {file = "coverage-7.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a0a207c87a9f743c8072d059b4711f8d13c456eb42dac778a7d2e5d4f3c253a7"},
+    {file = "coverage-7.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d673e3add00048215c2cc507f1228a7523fd8bf34f279ac98334c9b07bd2656"},
+    {file = "coverage-7.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f81fe93dc1b8e5673f33443c0786c14b77e36f1025973b85e07c70353e46882b"},
+    {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8c7524779003d59948c51b4fcbf1ca4e27c26a7d75984f63488f3625c328b9b"},
+    {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c124025430249118d018dcedc8b7426f39373527c845093132196f2a483b6dd"},
+    {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7f559c36d5cdc448ee13e7e56ed7b6b5d44a40a511d584d388a0f5d940977ba"},
+    {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:37cbc7b0d93dfd133e33c7ec01123fbb90401dce174c3b6661d8d36fb1e30608"},
+    {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d2a65876274acf544703e943c010b60bd79404e3623a1e5d52b64a6e2728de5"},
+    {file = "coverage-7.7.0-cp311-cp311-win32.whl", hash = "sha256:f5a2f71d6a91238e7628f23538c26aa464d390cbdedf12ee2a7a0fb92a24482a"},
+    {file = "coverage-7.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae8006772c6b0fa53c33747913473e064985dac4d65f77fd2fdc6474e7cd54e4"},
+    {file = "coverage-7.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:056d3017ed67e7ddf266e6f57378ece543755a4c9231e997789ab3bd11392c94"},
+    {file = "coverage-7.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33c1394d8407e2771547583b66a85d07ed441ff8fae5a4adb4237ad39ece60db"},
+    {file = "coverage-7.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbb7a0c3c21908520149d7751cf5b74eb9b38b54d62997b1e9b3ac19a8ee2fe"},
+    {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb356e7ae7c2da13f404bf8f75be90f743c6df8d4607022e759f5d7d89fe83f8"},
+    {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce730d484038e97f27ea2dbe5d392ec5c2261f28c319a3bb266f6b213650135"},
+    {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa4dff57fc21a575672176d5ab0ef15a927199e775c5e8a3d75162ab2b0c7705"},
+    {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b667b91f4f714b17af2a18e220015c941d1cf8b07c17f2160033dbe1e64149f0"},
+    {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:693d921621a0c8043bfdc61f7d4df5ea6d22165fe8b807cac21eb80dd94e4bbd"},
+    {file = "coverage-7.7.0-cp312-cp312-win32.whl", hash = "sha256:52fc89602cde411a4196c8c6894afb384f2125f34c031774f82a4f2608c59d7d"},
+    {file = "coverage-7.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ce8cf59e09d31a4915ff4c3b94c6514af4c84b22c4cc8ad7c3c546a86150a92"},
+    {file = "coverage-7.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4545485fef7a8a2d8f30e6f79ce719eb154aab7e44217eb444c1d38239af2072"},
+    {file = "coverage-7.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1393e5aa9441dafb0162c36c8506c648b89aea9565b31f6bfa351e66c11bcd82"},
+    {file = "coverage-7.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316f29cc3392fa3912493ee4c83afa4a0e2db04ff69600711f8c03997c39baaa"},
+    {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ffde1d6bc2a92f9c9207d1ad808550873748ac2d4d923c815b866baa343b3f"},
+    {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416e2a8845eaff288f97eaf76ab40367deafb9073ffc47bf2a583f26b05e5265"},
+    {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5efdeff5f353ed3352c04e6b318ab05c6ce9249c25ed3c2090c6e9cadda1e3b2"},
+    {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:57f3bd0d29bf2bd9325c0ff9cc532a175110c4bf8f412c05b2405fd35745266d"},
+    {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ab7090f04b12dc6469882ce81244572779d3a4b67eea1c96fb9ecc8c607ef39"},
+    {file = "coverage-7.7.0-cp313-cp313-win32.whl", hash = "sha256:180e3fc68ee4dc5af8b33b6ca4e3bb8aa1abe25eedcb958ba5cff7123071af68"},
+    {file = "coverage-7.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:55143aa13c49491f5606f05b49ed88663446dce3a4d3c5d77baa4e36a16d3573"},
+    {file = "coverage-7.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc41374d2f27d81d6558f8a24e5c114580ffefc197fd43eabd7058182f743322"},
+    {file = "coverage-7.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:89078312f06237417adda7c021c33f80f7a6d2db8572a5f6c330d89b080061ce"},
+    {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b2f144444879363ea8834cd7b6869d79ac796cb8f864b0cfdde50296cd95816"},
+    {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e6347d1ed882b1159ffea172cb8466ee46c665af4ca397edbf10ff53e9ffaf"},
+    {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb203c0afffaf1a8f5b9659a013f8f16a1b2cad3a80a8733ceedc968c0cf4c57"},
+    {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ad0edaa97cb983d9f2ff48cadddc3e1fb09f24aa558abeb4dc9a0dbacd12cbb4"},
+    {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c5f8a5364fc37b2f172c26a038bc7ec4885f429de4a05fc10fdcb53fb5834c5c"},
+    {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4e09534037933bf6eb31d804e72c52ec23219b32c1730f9152feabbd7499463"},
+    {file = "coverage-7.7.0-cp313-cp313t-win32.whl", hash = "sha256:1b336d06af14f8da5b1f391e8dec03634daf54dfcb4d1c4fb6d04c09d83cef90"},
+    {file = "coverage-7.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b54a1ee4c6f1905a436cbaa04b26626d27925a41cbc3a337e2d3ff7038187f07"},
+    {file = "coverage-7.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c8fbce80b2b8bf135d105aa8f5b36eae0c57d702a1cc3ebdea2a6f03f6cdde5"},
+    {file = "coverage-7.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9710521f07f526de30ccdead67e6b236fe996d214e1a7fba8b36e2ba2cd8261"},
+    {file = "coverage-7.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7789e700f33f2b133adae582c9f437523cd5db8de845774988a58c360fc88253"},
+    {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c36093aca722db73633cf2359026ed7782a239eb1c6db2abcff876012dc4cf"},
+    {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c075d167a6ec99b798c1fdf6e391a1d5a2d054caffe9593ba0f97e3df2c04f0e"},
+    {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d013c07061751ae81861cae6ec3a4fe04e84781b11fd4b6b4201590234b25c7b"},
+    {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:104bf640f408f4e115b85110047c7f27377e1a8b7ba86f7db4fa47aa49dc9a8e"},
+    {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:39abcacd1ed54e2c33c54bdc488b310e8ef6705833f7148b6eb9a547199d375d"},
+    {file = "coverage-7.7.0-cp39-cp39-win32.whl", hash = "sha256:8e336b56301774ace6be0017ff85c3566c556d938359b61b840796a0202f805c"},
+    {file = "coverage-7.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c938c6ae59be67ac19a7204e079efc94b38222cd7d0269f96e45e18cddeaa59"},
+    {file = "coverage-7.7.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:3b0e6e54591ae0d7427def8a4d40fca99df6b899d10354bab73cd5609807261c"},
+    {file = "coverage-7.7.0-py3-none-any.whl", hash = "sha256:708f0a1105ef2b11c79ed54ed31f17e6325ac936501fc373f24be3e6a578146a"},
+    {file = "coverage-7.7.0.tar.gz", hash = "sha256:cd879d4646055a573775a1cec863d00c9ff8c55860f8b17f6d8eee9140c06166"},
 ]
 
 [package.dependencies]
 tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
 
 [package.extras]
-toml = ["tomli"]
+toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
 
 [[package]]
 name = "docutils"
@@ -514,6 +534,7 @@ version = "0.20.1"
 description = "Docutils -- Python Documentation Utilities"
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"},
     {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
@@ -525,6 +546,7 @@ version = "0.2.2"
 description = "Like `typing._eval_type`, but lets older Python versions use newer typing features."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"},
     {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"},
@@ -539,6 +561,8 @@ version = "1.2.0"
 description = "Backport of PEP 654 (exception groups)"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev", "docs"]
+markers = "python_version < \"3.11\""
 files = [
     {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
     {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
@@ -551,17 +575,19 @@ test = ["pytest (>=6)"]
 name = "filelock"
 version = "3.13.1"
 description = "A platform independent file lock."
-optional = true
+optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
 files = [
     {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
     {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
 ]
+markers = {main = "extra == \"hf\""}
 
 [package.extras]
 docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
-typing = ["typing-extensions (>=4.8)"]
+typing = ["typing-extensions (>=4.8) ; python_version < \"3.11\""]
 
 [[package]]
 name = "frozenlist"
@@ -569,6 +595,7 @@ version = "1.4.1"
 description = "A list-like structure which implements collections.abc.MutableSequence"
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
 files = [
     {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
     {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
@@ -655,6 +682,8 @@ version = "2023.12.2"
 description = "File-system specification"
 optional = true
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "extra == \"hf\""
 files = [
     {file = "fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960"},
     {file = "fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb"},
@@ -690,6 +719,7 @@ version = "2024.8.6"
 description = "A clean customisable Sphinx documentation theme."
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"},
     {file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"},
@@ -707,6 +737,7 @@ version = "0.14.0"
 description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
     {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -714,13 +745,15 @@ files = [
 
 [[package]]
 name = "huggingface-hub"
-version = "0.29.1"
+version = "0.29.2"
 description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
 optional = true
 python-versions = ">=3.8.0"
+groups = ["main"]
+markers = "extra == \"hf\""
 files = [
-    {file = "huggingface_hub-0.29.1-py3-none-any.whl", hash = "sha256:352f69caf16566c7b6de84b54a822f6238e17ddd8ae3da4f8f2272aea5b198d5"},
-    {file = "huggingface_hub-0.29.1.tar.gz", hash = "sha256:9524eae42077b8ff4fc459ceb7a514eca1c1232b775276b009709fe2a084f250"},
+    {file = "huggingface_hub-0.29.2-py3-none-any.whl", hash = "sha256:c56f20fca09ef19da84dcde2b76379ecdaddf390b083f59f166715584953307d"},
+    {file = "huggingface_hub-0.29.2.tar.gz", hash = "sha256:590b29c0dcbd0ee4b7b023714dc1ad8563fe4a68a91463438b74e980d28afaf3"},
 ]
 
 [package.dependencies]
@@ -752,6 +785,7 @@ version = "3.7"
 description = "Internationalized Domain Names in Applications (IDNA)"
 optional = false
 python-versions = ">=3.5"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
     {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
@@ -763,6 +797,7 @@ version = "1.4.1"
 description = "Getting image size from png/jpeg/jpeg2000/gif file"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["docs"]
 files = [
     {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
     {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
@@ -774,6 +809,8 @@ version = "7.0.0"
 description = "Read metadata from Python packages"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
+markers = "python_version < \"3.10\""
 files = [
     {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"},
     {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"},
@@ -785,7 +822,7 @@ zipp = ">=0.5"
 [package.extras]
 docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
 perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"]
 
 [[package]]
 name = "iniconfig"
@@ -793,6 +830,7 @@ version = "2.0.0"
 description = "brain-dead simple config-ini parsing"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev"]
 files = [
     {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -800,13 +838,14 @@ files = [
 
 [[package]]
 name = "jinja2"
-version = "3.1.5"
+version = "3.1.6"
 description = "A very fast and expressive template engine."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
-    {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
-    {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
+    {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
+    {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
 ]
 
 [package.dependencies]
@@ -815,12 +854,33 @@ MarkupSafe = ">=2.0"
 [package.extras]
 i18n = ["Babel (>=2.7)"]
 
+[[package]]
+name = "jsonschema"
+version = "4.17.3"
+description = "An implementation of JSON Schema validation for Python"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+files = [
+    {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
+    {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
+]
+
+[package.dependencies]
+attrs = ">=17.4.0"
+pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
+
 [[package]]
 name = "linkify-it-py"
 version = "2.0.2"
 description = "Links recognition library with FULL unicode support."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "linkify-it-py-2.0.2.tar.gz", hash = "sha256:19f3060727842c254c808e99d465c80c49d2c7306788140987a1a7a29b0d6ad2"},
     {file = "linkify_it_py-2.0.2-py3-none-any.whl", hash = "sha256:a3a24428f6c96f27370d7fe61d2ac0be09017be5190d68d8658233171f1b6541"},
@@ -841,6 +901,7 @@ version = "3.0.0"
 description = "Python port of markdown-it. Markdown parsing, done right!"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
     {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
@@ -865,6 +926,7 @@ version = "2.1.3"
 description = "Safely add untrusted strings to HTML/XML markup."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
     {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
@@ -934,6 +996,7 @@ version = "0.4.0"
 description = "Collection of plugins for markdown-it-py"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"},
     {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"},
@@ -953,17 +1016,93 @@ version = "0.1.2"
 description = "Markdown URL utilities"
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
     {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
 ]
 
+[[package]]
+name = "msgpack"
+version = "1.1.0"
+description = "MessagePack serializer"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+    {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"},
+    {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"},
+    {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"},
+    {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"},
+    {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"},
+    {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"},
+    {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"},
+    {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"},
+    {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"},
+    {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"},
+    {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"},
+    {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"},
+    {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"},
+    {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"},
+    {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"},
+    {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"},
+    {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"},
+    {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"},
+    {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"},
+    {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"},
+    {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"},
+    {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"},
+    {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"},
+    {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"},
+    {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"},
+    {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"},
+    {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"},
+    {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"},
+    {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"},
+    {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"},
+    {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"},
+    {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"},
+    {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"},
+    {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"},
+    {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"},
+    {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"},
+    {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"},
+    {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"},
+    {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"},
+    {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"},
+    {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"},
+    {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"},
+    {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"},
+    {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"},
+    {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"},
+    {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"},
+    {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"},
+    {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"},
+    {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"},
+    {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"},
+    {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"},
+    {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"},
+    {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"},
+    {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"},
+    {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"},
+    {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"},
+    {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"},
+    {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"},
+    {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"},
+    {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"},
+    {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"},
+    {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"},
+    {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"},
+    {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"},
+]
+
 [[package]]
 name = "multidict"
 version = "6.0.5"
 description = "multidict implementation"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
 files = [
     {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
     {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
@@ -1063,6 +1202,7 @@ version = "1.15.0"
 description = "Optional static typing for Python"
 optional = false
 python-versions = ">=3.9"
+groups = ["dev"]
 files = [
     {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
     {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
@@ -1116,6 +1256,7 @@ version = "1.0.0"
 description = "Type system extensions for programs checked with the mypy type checker."
 optional = false
 python-versions = ">=3.5"
+groups = ["dev"]
 files = [
     {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
     {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
@@ -1127,6 +1268,7 @@ version = "3.0.1"
 description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"},
     {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"},
@@ -1154,6 +1296,7 @@ version = "1.6.0"
 description = "Patch asyncio to allow nested event loops"
 optional = false
 python-versions = ">=3.5"
+groups = ["main"]
 files = [
     {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"},
     {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
@@ -1161,13 +1304,15 @@ files = [
 
 [[package]]
 name = "olot"
-version = "0.1.2"
+version = "0.1.4"
 description = "oci layers on top"
 optional = true
 python-versions = "<4.0,>=3.9"
+groups = ["main"]
+markers = "extra == \"olot\""
 files = [
-    {file = "olot-0.1.2-py3-none-any.whl", hash = "sha256:e8b02c8a2e74a1b2d642c48bc0ded4236fadce6636faa63c6a736a14ef208893"},
-    {file = "olot-0.1.2.tar.gz", hash = "sha256:cdd1a1da751ef29dce002e94a681411fdeb956c32878eba4803230ca1550f653"},
+    {file = "olot-0.1.4-py3-none-any.whl", hash = "sha256:2e62a806935efea89d9d20a02245a7ca4f0b690aacf48d1f2c9adf926d42545b"},
+    {file = "olot-0.1.4.tar.gz", hash = "sha256:650910f1cbbdb18833a1acb63ecc6b22ca2cc7fe78a0ea0eeb15ca23b59ad39e"},
 ]
 
 [package.dependencies]
@@ -1180,10 +1325,12 @@ version = "23.2"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
     {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
 ]
+markers = {main = "extra == \"hf\""}
 
 [[package]]
 name = "pathspec"
@@ -1191,6 +1338,7 @@ version = "0.12.1"
 description = "Utility library for gitignore style pattern matching of file paths."
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
     {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
     {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -1202,6 +1350,7 @@ version = "4.2.2"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
     {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
     {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
@@ -1218,6 +1367,7 @@ version = "1.5.0"
 description = "plugin and hook calling mechanisms for python"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
     {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
     {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@@ -1233,6 +1383,7 @@ version = "0.2.0"
 description = "Accelerated property cache"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"},
     {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"},
@@ -1334,12 +1485,32 @@ files = [
     {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"},
 ]
 
+[[package]]
+name = "protobuf"
+version = "6.30.0"
+description = ""
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+    {file = "protobuf-6.30.0-cp310-abi3-win32.whl", hash = "sha256:7337d76d8efe65ee09ee566b47b5914c517190196f414e5418fa236dfd1aed3e"},
+    {file = "protobuf-6.30.0-cp310-abi3-win_amd64.whl", hash = "sha256:9b33d51cc95a7ec4f407004c8b744330b6911a37a782e2629c67e1e8ac41318f"},
+    {file = "protobuf-6.30.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:52d4bb6fe76005860e1d0b8bfa126f5c97c19cc82704961f60718f50be16942d"},
+    {file = "protobuf-6.30.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:7940ab4dfd60d514b2e1d3161549ea7aed5be37d53bafde16001ac470a3e202b"},
+    {file = "protobuf-6.30.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:d79bf6a202a536b192b7e8d295d7eece0c86fbd9b583d147faf8cfeff46bf598"},
+    {file = "protobuf-6.30.0-cp39-cp39-win32.whl", hash = "sha256:bb35ad251d222f03d6c4652c072dfee156be0ef9578373929c1a7ead2bd5492c"},
+    {file = "protobuf-6.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:501810e0eba1d327e783fde47cc767a563b0f1c292f1a3546d4f2b8c3612d4d0"},
+    {file = "protobuf-6.30.0-py3-none-any.whl", hash = "sha256:e5ef216ea061b262b8994cb6b7d6637a4fb27b3fb4d8e216a6040c0b93bd10d7"},
+    {file = "protobuf-6.30.0.tar.gz", hash = "sha256:852b675d276a7d028f660da075af1841c768618f76b90af771a8e2c29e6f5965"},
+]
+
 [[package]]
 name = "pydantic"
 version = "2.10.6"
 description = "Data validation using Python type hints"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
     {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
@@ -1352,7 +1523,7 @@ typing-extensions = ">=4.12.2"
 
 [package.extras]
 email = ["email-validator (>=2.0.0)"]
-timezone = ["tzdata"]
+timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
 
 [[package]]
 name = "pydantic-core"
@@ -1360,6 +1531,7 @@ version = "2.27.2"
 description = "Core functionality for Pydantic validation and serialization"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
     {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
@@ -1472,21 +1644,65 @@ version = "2.17.2"
 description = "Pygments is a syntax highlighting package written in Python."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
     {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
 ]
 
 [package.extras]
-plugins = ["importlib-metadata"]
+plugins = ["importlib-metadata ; python_version < \"3.8\""]
 windows-terminal = ["colorama (>=0.4.6)"]
 
+[[package]]
+name = "pyrsistent"
+version = "0.20.0"
+description = "Persistent/Functional/Immutable data structures"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+    {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"},
+    {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"},
+    {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"},
+    {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"},
+    {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"},
+    {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"},
+    {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"},
+    {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"},
+    {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"},
+    {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"},
+    {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"},
+    {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"},
+]
+
 [[package]]
 name = "pytest"
 version = "8.3.5"
 description = "pytest: simple powerful testing with Python"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
     {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"},
     {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"},
@@ -1509,6 +1725,7 @@ version = "0.25.3"
 description = "Pytest support for asyncio"
 optional = false
 python-versions = ">=3.9"
+groups = ["dev"]
 files = [
     {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"},
     {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"},
@@ -1527,6 +1744,7 @@ version = "6.0.0"
 description = "Pytest plugin for measuring coverage."
 optional = false
 python-versions = ">=3.9"
+groups = ["dev"]
 files = [
     {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
     {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
@@ -1545,6 +1763,7 @@ version = "2.9.0.post0"
 description = "Extensions to the standard Python datetime module"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["main"]
 files = [
     {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
     {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -1559,6 +1778,7 @@ version = "6.0.1"
 description = "YAML parser and emitter for Python"
 optional = false
 python-versions = ">=3.6"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
     {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
@@ -1612,6 +1832,67 @@ files = [
     {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
     {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
 ]
+markers = {main = "extra == \"hf\""}
+
+[[package]]
+name = "ray"
+version = "2.43.0"
+description = "Ray provides a simple, universal API for building distributed applications."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+    {file = "ray-2.43.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:18626fff368451a37a76b33d52a70f05b20a211eb0867d860721c8e86cb6955a"},
+    {file = "ray-2.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7c4fdec59a14d6b2939d91fee6efc84b614a6722c3be0b27fa371e3f563255f"},
+    {file = "ray-2.43.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:57381c54f200e6c0203d5f70ac6f882b13cc1a80faf336518787a39a6d6f65d0"},
+    {file = "ray-2.43.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:1872983a285a85b776bf311c809d559f8482909a2d39ead5f2ac69cfe3aa8544"},
+    {file = "ray-2.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:f61b9a644197f7049cb8688e2ea9db49d486f0cbef433f7a7b7349bfbf8dec19"},
+    {file = "ray-2.43.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:fde8a81280f07af983bc3769c9941db5db273ce10e92abb3348e41bed023d735"},
+    {file = "ray-2.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e93c32ad0cb67f1f7da76fac409d87d5cd5ea3eb03b836830e9ef5cc810bc2c0"},
+    {file = "ray-2.43.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:476ec3e1fa2464ddd5f049c0f2758ff9dfecc21fb8df4266f1df01b2780c6653"},
+    {file = "ray-2.43.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:78c3bdbf182b4d019fa9a8aabd55c39bf705bb630aea064f768f305fc472d1eb"},
+    {file = "ray-2.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2b0fa0272ade67bad2e83d7de996795bfb4f10f4b895476b95fbfda6d3c3ed6"},
+    {file = "ray-2.43.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:5121fdf4bcbcb0fda3b9b71164dd6c8fcc79a2e258022a2a3957e401018913fb"},
+    {file = "ray-2.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f26f7b72da04c3c4422269c31b067abd15cb38424b7012d812ddfb2c77462ea"},
+    {file = "ray-2.43.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:73770d4c8a989730985ff2b4292129249e28c1e29e84589470c9ba1ae91ca832"},
+    {file = "ray-2.43.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:b45f478d29ce5df3fc19861df64fef9ed5c25f1e83fa10028d33fadefdeca095"},
+    {file = "ray-2.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:1c3a9880112a8d561280a34e8ef9471070f81ca467e08b669e5e77a85e173c9c"},
+    {file = "ray-2.43.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d0e26f9db91a5b3343f0858eb256255b35c7e97fc6bf97065f5744ad7e8cc297"},
+    {file = "ray-2.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8cfdec2bf61c48690f6cb4325f1673e0d1d00dea925b59b631196e67f426a0e"},
+    {file = "ray-2.43.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:87e0cac85189dc519af1b6ef2abd654804c488c932caadd658b90caaa65f8dae"},
+    {file = "ray-2.43.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:5edf3da18041e0b6170c62e08480ab050f0766361ff3f6e11257cc04ab46e592"},
+    {file = "ray-2.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:889964d09ae048f5bf56a54d2477779f44d780b35dfadaf927da20e4703a6f67"},
+]
+
+[package.dependencies]
+aiosignal = "*"
+click = ">=7.0"
+filelock = "*"
+frozenlist = "*"
+jsonschema = "*"
+msgpack = ">=1.0.0,<2.0.0"
+packaging = "*"
+protobuf = ">=3.15.3,<3.19.5 || >3.19.5"
+pyyaml = "*"
+requests = "*"
+
+[package.extras]
+adag = ["cupy-cuda12x ; sys_platform != \"darwin\""]
+air = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "fsspec", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "numpy (>=1.20)", "opencensus", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"]
+all = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x ; sys_platform != \"darwin\"", "dm-tree", "fastapi", "fsspec", "grpcio", "grpcio (!=1.56.0) ; sys_platform == \"darwin\"", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "gymnasium (==1.0.0)", "lz4", "memray ; sys_platform != \"win32\"", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "ormsgpack (==1.7.0)", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pyOpenSSL", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "requests", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"]
+all-cpp = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x ; sys_platform != \"darwin\"", "dm-tree", "fastapi", "fsspec", "grpcio", "grpcio (!=1.56.0) ; sys_platform == \"darwin\"", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "gymnasium (==1.0.0)", "lz4", "memray ; sys_platform != \"win32\"", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "ormsgpack (==1.7.0)", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pyOpenSSL", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "ray-cpp (==2.43.0)", "requests", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"]
+cgraph = ["cupy-cuda12x ; sys_platform != \"darwin\""]
+client = ["grpcio", "grpcio (!=1.56.0) ; sys_platform == \"darwin\""]
+cpp = ["ray-cpp (==2.43.0)"]
+data = ["fsspec", "numpy (>=1.20)", "pandas (>=1.3)", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)"]
+default = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "virtualenv (>=20.0.24,!=20.21.1)"]
+llm = ["aiohttp (>=3.7)", "aiohttp-cors", "async-timeout", "asyncache (>=0.3.1)", "boto3", "colorful", "fastapi", "fsspec", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "jsonref (>=1.1.0)", "numpy (>=1.20)", "opencensus", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "vllm (>=0.7.2)", "watchfiles"]
+observability = ["memray ; sys_platform != \"win32\"", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"]
+rllib = ["dm-tree", "fsspec", "gymnasium (==1.0.0)", "lz4", "ormsgpack (==1.7.0)", "pandas", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pyyaml", "requests", "scipy", "tensorboardX (>=1.9)"]
+serve = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"]
+serve-grpc = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0) ; python_version < \"3.10\"", "grpcio (>=1.42.0) ; python_version >= \"3.10\"", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0) ; python_version < \"3.12\"", "py-spy (>=0.4.0) ; python_version >= \"3.12\"", "pyOpenSSL", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"]
+train = ["fsspec", "pandas", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "tensorboardX (>=1.9)"]
+tune = ["fsspec", "pandas", "pyarrow (<18) ; sys_platform == \"darwin\" and platform_machine == \"x86_64\"", "pyarrow (>=9.0.0)", "requests", "tensorboardX (>=1.9)"]
 
 [[package]]
 name = "requests"
@@ -1619,6 +1900,7 @@ version = "2.32.3"
 description = "Python HTTP for Humans."
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
     {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
@@ -1636,29 +1918,30 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 
 [[package]]
 name = "ruff"
-version = "0.9.9"
+version = "0.11.0"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
-files = [
-    {file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"},
-    {file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"},
-    {file = "ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb"},
-    {file = "ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0"},
-    {file = "ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17"},
-    {file = "ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1"},
-    {file = "ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57"},
-    {file = "ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e"},
-    {file = "ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1"},
-    {file = "ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1"},
-    {file = "ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf"},
-    {file = "ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933"},
+groups = ["dev"]
+files = [
+    {file = "ruff-0.11.0-py3-none-linux_armv6l.whl", hash = "sha256:dc67e32bc3b29557513eb7eeabb23efdb25753684b913bebb8a0c62495095acb"},
+    {file = "ruff-0.11.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38c23fd9bdec4eb437b4c1e3595905a0a8edfccd63a790f818b28c78fe345639"},
+    {file = "ruff-0.11.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7c8661b0be91a38bd56db593e9331beaf9064a79028adee2d5f392674bbc5e88"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6c0e8d3d2db7e9f6efd884f44b8dc542d5b6b590fc4bb334fdbc624d93a29a2"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c3156d3f4b42e57247275a0a7e15a851c165a4fc89c5e8fa30ea6da4f7407b8"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:490b1e147c1260545f6d041c4092483e3f6d8eba81dc2875eaebcf9140b53905"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1bc09a7419e09662983b1312f6fa5dab829d6ab5d11f18c3760be7ca521c9329"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcfa478daf61ac8002214eb2ca5f3e9365048506a9d52b11bea3ecea822bb844"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fbb2aed66fe742a6a3a0075ed467a459b7cedc5ae01008340075909d819df1e"},
+    {file = "ruff-0.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92c0c1ff014351c0b0cdfdb1e35fa83b780f1e065667167bb9502d47ca41e6db"},
+    {file = "ruff-0.11.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e4fd5ff5de5f83e0458a138e8a869c7c5e907541aec32b707f57cf9a5e124445"},
+    {file = "ruff-0.11.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:96bc89a5c5fd21a04939773f9e0e276308be0935de06845110f43fd5c2e4ead7"},
+    {file = "ruff-0.11.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a9352b9d767889ec5df1483f94870564e8102d4d7e99da52ebf564b882cdc2c7"},
+    {file = "ruff-0.11.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:049a191969a10897fe052ef9cc7491b3ef6de79acd7790af7d7897b7a9bfbcb6"},
+    {file = "ruff-0.11.0-py3-none-win32.whl", hash = "sha256:3191e9116b6b5bbe187447656f0c8526f0d36b6fd89ad78ccaad6bdc2fad7df2"},
+    {file = "ruff-0.11.0-py3-none-win_amd64.whl", hash = "sha256:c58bfa00e740ca0a6c43d41fb004cd22d165302f360aaa56f7126d544db31a21"},
+    {file = "ruff-0.11.0-py3-none-win_arm64.whl", hash = "sha256:868364fc23f5aa122b00c6f794211e85f7e78f5dffdf7c590ab90b8c4e69b657"},
+    {file = "ruff-0.11.0.tar.gz", hash = "sha256:e55c620690a4a7ee6f1cccb256ec2157dc597d109400ae75bbf944fc9d6462e2"},
 ]
 
 [[package]]
@@ -1667,6 +1950,8 @@ version = "70.2.0"
 description = "Easily download, build, install, upgrade, and uninstall Python packages"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
+markers = "python_version >= \"3.12\""
 files = [
     {file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
     {file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
@@ -1674,7 +1959,7 @@ files = [
 
 [package.extras]
 doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
 
 [[package]]
 name = "six"
@@ -1682,6 +1967,7 @@ version = "1.16.0"
 description = "Python 2 and 3 compatibility utilities"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+groups = ["main"]
 files = [
     {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -1693,6 +1979,7 @@ version = "1.3.1"
 description = "Sniff out which async library your code is running under"
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
     {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
@@ -1704,6 +1991,7 @@ version = "2.2.0"
 description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
 optional = false
 python-versions = "*"
+groups = ["docs"]
 files = [
     {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
     {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
@@ -1715,6 +2003,7 @@ version = "2.5"
 description = "A modern CSS selector implementation for Beautiful Soup."
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
     {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
@@ -1726,6 +2015,7 @@ version = "7.4.7"
 description = "Python documentation generator"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"},
     {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"},
@@ -1762,6 +2052,7 @@ version = "2024.10.3"
 description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser."
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa"},
     {file = "sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1"},
@@ -1784,6 +2075,7 @@ version = "1.0.0b2"
 description = "A modern skeleton for Sphinx themes."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"},
     {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"},
@@ -1801,6 +2093,7 @@ version = "1.0.7"
 description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"},
     {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"},
@@ -1819,6 +2112,7 @@ version = "1.0.5"
 description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"},
     {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"},
@@ -1837,6 +2131,7 @@ version = "2.0.4"
 description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"},
     {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"},
@@ -1855,6 +2150,7 @@ version = "1.0.1"
 description = "A sphinx extension which renders display math in HTML via JavaScript"
 optional = false
 python-versions = ">=3.5"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
     {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
@@ -1869,6 +2165,7 @@ version = "1.0.6"
 description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"},
     {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"},
@@ -1887,6 +2184,7 @@ version = "1.1.9"
 description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
 optional = false
 python-versions = ">=3.9"
+groups = ["docs"]
 files = [
     {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"},
     {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"},
@@ -1905,6 +2203,7 @@ version = "0.40.0"
 description = "The little ASGI library that shines."
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"},
     {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"},
@@ -1923,10 +2222,12 @@ version = "2.0.1"
 description = "A lil' TOML parser"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev", "docs"]
 files = [
     {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
     {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
 ]
+markers = {dev = "python_full_version <= \"3.11.0a6\"", docs = "python_version < \"3.11\""}
 
 [[package]]
 name = "tqdm"
@@ -1934,6 +2235,8 @@ version = "4.66.3"
 description = "Fast, Extensible Progress Meter"
 optional = true
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "extra == \"hf\""
 files = [
     {file = "tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53"},
     {file = "tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5"},
@@ -1954,6 +2257,7 @@ version = "2.9.0.20241206"
 description = "Typing stubs for python-dateutil"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
     {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"},
     {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"},
@@ -1965,6 +2269,7 @@ version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@@ -1976,6 +2281,7 @@ version = "1.0.2"
 description = "Micro subset of unicode data files for linkify-it-py projects."
 optional = false
 python-versions = ">=3.7"
+groups = ["docs"]
 files = [
     {file = "uc-micro-py-1.0.2.tar.gz", hash = "sha256:30ae2ac9c49f39ac6dce743bd187fcd2b574b16ca095fa74cd9396795c954c54"},
     {file = "uc_micro_py-1.0.2-py3-none-any.whl", hash = "sha256:8c9110c309db9d9e87302e2f4ad2c3152770930d88ab385cd544e7a7e75f3de0"},
@@ -1990,13 +2296,14 @@ version = "2.2.2"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev", "docs"]
 files = [
     {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
     {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
 ]
 
 [package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
 h2 = ["h2 (>=4,<5)"]
 socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 zstd = ["zstandard (>=0.18.0)"]
@@ -2007,6 +2314,7 @@ version = "0.29.0"
 description = "The lightning-fast ASGI server."
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
     {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
@@ -2018,7 +2326,59 @@ h11 = ">=0.8"
 typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
 
 [package.extras]
-standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
+standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"]
+
+[[package]]
+name = "uvloop"
+version = "0.21.0"
+description = "Fast implementation of asyncio event loop on top of libuv"
+optional = false
+python-versions = ">=3.8.0"
+groups = ["dev"]
+files = [
+    {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"},
+    {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"},
+    {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"},
+    {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"},
+    {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"},
+    {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"},
+    {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"},
+    {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"},
+    {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"},
+    {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"},
+    {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"},
+    {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"},
+    {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"},
+    {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"},
+    {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"},
+    {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"},
+    {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"},
+    {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"},
+    {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"},
+    {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"},
+    {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"},
+    {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"},
+    {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"},
+    {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"},
+    {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"},
+    {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"},
+    {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"},
+    {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"},
+    {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"},
+    {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"},
+    {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"},
+    {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"},
+    {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"},
+    {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"},
+    {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"},
+    {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"},
+    {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"},
+]
+
+[package.extras]
+dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"]
 
 [[package]]
 name = "watchfiles"
@@ -2026,6 +2386,7 @@ version = "0.21.0"
 description = "Simple, modern and high performance file watching and code reload in python."
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"},
     {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"},
@@ -2113,6 +2474,7 @@ version = "12.0"
 description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
 files = [
     {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"},
     {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"},
@@ -2194,6 +2556,7 @@ version = "1.17.2"
 description = "Yet another URL library"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
 files = [
     {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:93771146ef048b34201bfa382c2bf74c524980870bb278e6df515efaf93699ff"},
     {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8281db240a1616af2f9c5f71d355057e73a1409c4648c8949901396dc0a3c151"},
@@ -2290,6 +2653,8 @@ version = "3.19.1"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.8"
+groups = ["docs"]
+markers = "python_version < \"3.10\""
 files = [
     {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"},
     {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"},
@@ -2305,6 +2670,6 @@ hf = ["huggingface-hub"]
 olot = ["olot"]
 
 [metadata]
-lock-version = "2.0"
+lock-version = "2.1"
 python-versions = ">= 3.9, < 4.0"
-content-hash = "9409597dfcae8b06a78e88036c74d02f87cccea5d0f8bb69042535912f5c08dc"
+content-hash = "b47673235facdda56a0f6cc2477ca48d09f4a8927a83bd470fb6dad0f3117473"
diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml
index 63414765..684e47bf 100644
--- a/clients/python/pyproject.toml
+++ b/clients/python/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "model-registry"
-version = "0.2.15"
+version = "0.2.16"
 description = "Client for Kubeflow Model Registry"
 authors = ["Isabella Basso do Amaral <idoamara@redhat.com>"]
 license = "Apache-2.0"
@@ -27,7 +27,7 @@ nest-asyncio = "^1.6.0"
 eval-type-backport = "^0.2.0"
 
 huggingface-hub = { version = ">=0.20.1,<0.30.0", optional = true }
-olot = { version = "^0.1.2", optional = true }
+olot = { version = "^0.1.4", optional = true }
 
 [tool.poetry.extras]
 hf = ["huggingface-hub"]
@@ -47,8 +47,10 @@ sphinx-autobuild = ">=2021.3.14,<2025.0.0"
 pytest = ">=7.4.2,<9.0.0"
 coverage = { extras = ["toml"], version = "^7.3.2" }
 pytest-cov = ">=4.1,<7.0"
-ruff = ">=0.5.2,<0.10.0"
+ruff = ">=0.5.2,<0.12.0"
 mypy = "^1.7.0"
+ray = "^2.43.0"
+uvloop = "^0.21.0"
 pytest-asyncio = ">=0.23.7,<0.26.0"
 requests = "^2.32.2"
 black = ">=24.4.2,<26.0.0"
diff --git a/clients/python/src/model_registry/__init__.py b/clients/python/src/model_registry/__init__.py
index 034c1367..31315dea 100644
--- a/clients/python/src/model_registry/__init__.py
+++ b/clients/python/src/model_registry/__init__.py
@@ -1,6 +1,6 @@
 """Main package for the Kubeflow model registry."""
 
-__version__ = "0.2.15"
+__version__ = "0.2.16"
 
 from ._client import ModelRegistry
 
diff --git a/clients/python/src/model_registry/_client.py b/clients/python/src/model_registry/_client.py
index a43f935e..71a5a8fd 100644
--- a/clients/python/src/model_registry/_client.py
+++ b/clients/python/src/model_registry/_client.py
@@ -2,11 +2,13 @@
 
 from __future__ import annotations
 
+import inspect
 import logging
 import os
-from collections.abc import Mapping
+from collections.abc import Coroutine, Mapping
+from dataclasses import asdict
 from pathlib import Path
-from typing import TYPE_CHECKING, Any, TypeVar, Union, get_args
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, get_args
 from warnings import warn
 
 from .core import ModelRegistryAPIClient
@@ -19,7 +21,13 @@ from .types import (
     RegisteredModel,
     SupportedTypes,
 )
-from .utils import s3_uri_from
+from .utils import (
+    OCIParams,
+    S3Params,
+    get_files_from_path,
+    s3_uri_from,
+    save_to_oci_registry,
+)
 
 ModelTypes = Union[RegisteredModel, ModelVersion, ModelArtifact]
 TModel = TypeVar("TModel", bound=ModelTypes)
@@ -56,7 +64,7 @@ if TYPE_CHECKING:
 class ModelRegistry:
     """Model registry client."""
 
-    def __init__(
+    def __init__(  # noqa: C901
         self,
         server_address: str,
         port: int = 443,
@@ -68,6 +76,7 @@ class ModelRegistry:
         custom_ca: str | None = None,
         custom_ca_envvar: str | None = None,
         log_level: int = logging.WARNING,
+        async_runner: Callable[[Coroutine[Any, Any, Any]], Any] = None,
     ):
         """Constructor.
 
@@ -83,16 +92,23 @@ class ModelRegistry:
             custom_ca: Path to the PEM-encoded root certificates as a string.
             custom_ca_envvar: Environment variable to read the custom CA from if it's not passed as an arg.
             log_level: Log level. Defaults to logging.WARNING.
+            async_runner: A modular async scheduler Callable (either a method or function) that takes in a coroutine for scheduling.
         """
         logger.setLevel(log_level)
 
-        import nest_asyncio
-
-        logger.debug("Setting up reentrant async event loop")
-        nest_asyncio.apply()
-
         # TODO: get remaining args from env
         self._author = author
+        # Set the user's defined async runner
+        if async_runner:
+            if not (inspect.ismethod(async_runner) or inspect.isfunction(async_runner)):
+                msg = "`async_runner` must be a bound method or a function that takes in a coroutine to run."
+                raise ValueError(msg)
+            self._user_async_runner = async_runner
+        else:
+            import nest_asyncio
+
+            logger.debug("Setting up reentrant async event loop")
+            nest_asyncio.apply()
 
         if not user_token and user_token_envvar:
             logger.info("Reading user token from %s", user_token_envvar)
@@ -133,6 +149,9 @@ class ModelRegistry:
         self.get_registered_models().page_size(1)._next_page()
 
     def async_runner(self, coro: Any) -> Any:
+        if hasattr(self, "_user_async_runner"):
+            return self._user_async_runner(coro)
+
         import asyncio
 
         try:
@@ -170,6 +189,77 @@ class ModelRegistry:
             ModelArtifact(name=name, uri=uri, **kwargs), mv.id
         )
 
+    def upload_artifact_and_register_model(
+        self,
+        name: str,
+        model_files_path: str,
+        *,
+        # Upload/client Params
+        upload_params: OCIParams | S3Params,
+        # Artifact/Model Params
+        version: str,
+        model_format_name: str,
+        model_format_version: str,
+        storage_path: str | None = None,
+        storage_key: str | None = None,
+        service_account_name: str | None = None,
+        author: str | None = None,
+        owner: str | None = None,
+        description: str | None = None,
+        metadata: Mapping[str, SupportedTypes] | None = None,
+    ) -> RegisteredModel:
+        """Convenience method to perform 2 operations; uploading an artifact to a storage location, and registers the model in model registry.
+
+        Args:
+            name: Name of the model.
+            model_files_path: The path where the model files are located. If a directory, uploads the entire directory.
+
+        Keyword Args:
+            upload_params: Parameters to configure which storage client to use as well as that client's configuration when uploading the model.
+            version: Version of the model. Has to be unique.
+            model_format_name: Name of the model format.
+            model_format_version: Version of the model format.
+            description: Description of the model.
+            author: Author of the model. Defaults to the client author.
+            owner: Owner of the model. Defaults to the client author.
+            storage_key: Storage key.
+            storage_path: Storage path.
+            service_account_name: Service account name.
+            metadata: Additional version metadata. Defaults to values returned by `default_metadata()`.
+
+        Raises:
+            ValueError: When the provided `upload_params` is missing or invalid
+
+        Returns:
+            Registered model. See: :meth:`~ModelRegistry.register_model`
+        """
+        if isinstance(upload_params, S3Params):
+            destination_uri = self.save_to_s3(
+                **asdict(upload_params), path=model_files_path
+            )
+        elif isinstance(upload_params, OCIParams):
+            destination_uri = save_to_oci_registry(
+                **asdict(upload_params), model_files_path=model_files_path
+            )
+        else:
+            msg = 'Param "upload_params" is required to perform an upload. Please ensure the value provided is valid'
+            raise ValueError(msg)
+
+        return self.register_model(
+            name,
+            destination_uri,
+            model_format_name=model_format_name,
+            model_format_version=model_format_version,
+            version=version,
+            storage_key=storage_key,
+            storage_path=storage_path,
+            service_account_name=service_account_name,
+            author=author,
+            owner=owner,
+            description=description,
+            metadata=metadata,
+        )
+
     def register_model(
         self,
         name: str,
@@ -181,6 +271,11 @@ class ModelRegistry:
         storage_key: str | None = None,
         storage_path: str | None = None,
         service_account_name: str | None = None,
+        model_source_kind: str | None = None,
+        model_source_class: str | None = None,
+        model_source_group: str | None = None,
+        model_source_id: str | None = None,
+        model_source_name: str | None = None,
         author: str | None = None,
         owner: str | None = None,
         description: str | None = None,
@@ -210,6 +305,11 @@ class ModelRegistry:
             storage_key: Storage key.
             storage_path: Storage path.
             service_account_name: Service account name.
+            model_source_kind: A string identifier describing the source kind.
+            model_source_class: A subgroup within the source kind.
+            model_source_group: This identifies a source group for models from source class.
+            model_source_id: A unique identifier for a source model within kind, class, and group.
+            model_source_name: A human-readable name for the source model.
             metadata: Additional version metadata. Defaults to values returned by `default_metadata()`.
 
         Returns:
@@ -235,6 +335,11 @@ class ModelRegistry:
                 storage_key=storage_key,
                 storage_path=storage_path,
                 service_account_name=service_account_name,
+                model_source_kind=model_source_kind,
+                model_source_class=model_source_class,
+                model_source_group=model_source_group,
+                model_source_id=model_source_id,
+                model_source_name=model_source_name,
             )
         )
 
@@ -294,8 +399,8 @@ class ModelRegistry:
             from huggingface_hub import HfApi, hf_hub_url, utils
         except ImportError as e:
             msg = """package `huggingface-hub` is not installed.
-            To import models from Hugging Face Hub, start by installing the `huggingface-hub` package, either directly or as an
-            extra (available as `model-registry[hf]`), e.g.:
+            To import models from Hugging Face Hub, start by installing the `huggingface-hub` package,
+            either directly or as an extra (available as `model-registry[hf]`), e.g.:
             ```sh
             !pip install --pre model-registry[hf]
             ```
@@ -437,7 +542,8 @@ class ModelRegistry:
             raise StoreError(msg)
 
         def rm_versions(options: ListOptions) -> list[ModelVersion]:
-            # type checkers can't restrict the type inside a nested function: https://mypy.readthedocs.io/en/stable/common_issues.html#narrowing-and-inner-functions
+            # type checkers can't restrict the type inside a nested function:
+            # https://mypy.readthedocs.io/en/stable/common_issues.html#narrowing-and-inner-functions
             assert rm.id
             return self.async_runner(self._api.get_model_versions(rm.id, options))
 
@@ -524,16 +630,10 @@ class ModelRegistry:
             StoreError if `path` does not exist.
             StoreError if `path_prefix` is not set.
         """
-        is_file = os.path.isfile(path)
-
         if not path_prefix:
             msg = "`path_prefix` must be set."
             raise StoreError(msg)
 
-        if not os.path.exists(path):
-            msg = f"Path '{path}' does not exist. Please ensure path is correct."
-            raise StoreError(msg)
-
         if path_prefix.endswith("/"):
             path_prefix = path_prefix[:-1]
 
@@ -544,23 +644,10 @@ class ModelRegistry:
             region=region,
         )
 
-        if is_file:
-            filename = os.path.basename(path)
-            if not filename:
-                msg = "An error occured determining if the supplied path was file."
-                raise StoreError(msg)
-
-            s3_key = os.path.join(path_prefix, filename)
-            s3.upload_file(path, bucket, s3_key)
-
-            return uri
-
-        for root, _, files in os.walk(path):
-            for filename in files:
-                file_path = os.path.join(root, filename)
-                relative_path = os.path.relpath(file_path, path)
-                s3_key = os.path.join(path_prefix, relative_path)
-                s3.upload_file(file_path, bucket, s3_key)
+        files = get_files_from_path(path)
+        for absolute_path_filename, relative_path_filename in files:
+            s3_key = os.path.join(path_prefix, relative_path_filename)
+            s3.upload_file(absolute_path_filename, bucket, s3_key)
 
         return uri
 
diff --git a/clients/python/src/model_registry/types/artifacts.py b/clients/python/src/model_registry/types/artifacts.py
index a8cfd793..5c67a114 100644
--- a/clients/python/src/model_registry/types/artifacts.py
+++ b/clients/python/src/model_registry/types/artifacts.py
@@ -135,6 +135,11 @@ class ModelArtifact(Artifact):
         storage_key: Storage secret name.
         storage_path: Storage path of the model.
         service_account_name: Name of the service account with storage secret.
+        model_source_kind: A string identifier describing the source kind.
+        model_source_class: A subgroup within the source kind.
+        model_source_group: This identifies a source group for models from source class.
+        model_source_id: A unique identifier for a source model within kind, class, and group.
+        model_source_name: A human-readable name for the source model.
     """
 
     # TODO: this could be an enum of valid formats
@@ -143,6 +148,11 @@ class ModelArtifact(Artifact):
     storage_key: str | None = None
     storage_path: str | None = None
     service_account_name: str | None = None
+    model_source_kind: str | None = None
+    model_source_class: str | None = None
+    model_source_group: str | None = None
+    model_source_id: str | None = None
+    model_source_name: str | None = None
 
     _model_version_id: str | None = None
 
@@ -194,6 +204,11 @@ class ModelArtifact(Artifact):
             storage_key=source.storage_key,
             storage_path=source.storage_path,
             service_account_name=source.service_account_name,
+            model_source_kind=source.model_source_kind,
+            model_source_class=source.model_source_class,
+            model_source_group=source.model_source_group,
+            model_source_id=source.model_source_id,
+            model_source_name=source.model_source_name,
             state=source.state,
             custom_properties=cls._unmap_custom_properties(source.custom_properties)
             if source.custom_properties
diff --git a/clients/python/src/model_registry/utils.py b/clients/python/src/model_registry/utils.py
index dbb3e244..4d67ea57 100644
--- a/clients/python/src/model_registry/utils.py
+++ b/clients/python/src/model_registry/utils.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import os
 import tempfile
+from dataclasses import dataclass
 from pathlib import Path
 from typing import Callable, TypedDict
 
@@ -78,9 +79,7 @@ def s3_uri_from(
             raise MissingMetadata(msg)
         bucket = default_bucket
     elif (not default_bucket or default_bucket != bucket) and not endpoint:
-        msg = (
-            "endpoint must be provided for non-default bucket"
-        )
+        msg = "endpoint must be provided for non-default bucket"
         raise MissingMetadata(msg)
 
     endpoint = endpoint or os.getenv("AWS_S3_ENDPOINT")
@@ -92,7 +91,7 @@ def s3_uri_from(
     # https://alexwlchan.net/2020/s3-keys-are-not-file-paths/ nor do they resolve to valid URls
     # FIXME: is this safe?
     if not region:
-         return f"s3://{bucket}/{path}?endpoint={endpoint}"
+        return f"s3://{bucket}/{path}?endpoint={endpoint}"
     return f"s3://{bucket}/{path}?endpoint={endpoint}&defaultRegion={region}"
 
 
@@ -116,11 +115,8 @@ def _get_skopeo_backend() -> BackendDefinition:
         msg = "Could not import 'olot.backend.skopeo'. Ensure that 'olot' is installed if you want to use the 'skopeo' backend."
         raise ImportError(msg) from e
 
-    return {
-        "is_available": is_skopeo,
-        "pull": skopeo_pull,
-        "push": skopeo_push
-    }
+    return {"is_available": is_skopeo, "pull": skopeo_pull, "push": skopeo_push}
+
 
 def _get_oras_backend() -> BackendDefinition:
     try:
@@ -135,6 +131,37 @@ def _get_oras_backend() -> BackendDefinition:
         "push": oras_push,
     }
 
+
+@dataclass
+class OCIParams:
+    """Parameters for the OCI client to perform the upload.
+
+    Allows for some customization of how to perform the upload step when uploading via OCI
+    """
+
+    base_image: str
+    oci_ref: str
+    dest_dir: str | os.PathLike = None
+    backend: str = "skopeo"
+    modelcard: os.PathLike | None = None
+    custom_oci_backend: BackendDefinition = None
+
+
+@dataclass
+class S3Params:
+    """Parameters for the S3 Client (boto3) to perform the upload.
+
+    Allows for some amount of customization when performing an upload, such as providing a custom endpoint url, access keys, etc.
+    """
+
+    bucket_name: str
+    s3_prefix: str
+    endpoint_url: str | None = None
+    access_key_id: str | None = None
+    secret_access_key: str | None = None
+    region: str | None = None
+
+
 # A dict mapping backend names to their definitions
 BackendDict = dict[str, Callable[[], BackendDefinition]]
 
@@ -143,14 +170,15 @@ DEFAULT_BACKENDS: BackendDict = {
     "oras": _get_oras_backend,
 }
 
+
 def save_to_oci_registry(
-        base_image: str,
-        oci_ref: str,
-        model_files: list[os.PathLike],
-        dest_dir: str | os.PathLike = None,
-        backend: str = "skopeo",
-        modelcard: os.PathLike | None = None,
-        backend_registry: BackendDict | None = DEFAULT_BACKENDS,
+    base_image: str,
+    oci_ref: str,
+    model_files_path: str | os.PathLike,
+    dest_dir: str | os.PathLike = None,
+    backend: str = "skopeo",
+    modelcard: os.PathLike | None = None,
+    custom_oci_backend: BackendDefinition | None = None,
 ) -> str:
     """Appends a list of files to an OCI-based image.
 
@@ -158,10 +186,10 @@ def save_to_oci_registry(
         base_image: The image to append model files to. This image will be downloaded to the location at `dest_dir`
         dest_dir: The location to save the downloaded and extracted base image to.
         oci_ref: Destination of where to push the newly layered image to. eg, "quay.io/my-org/my-registry:1.0.0"
-        model_files: List of files to add to the base_image as layers
+        model_files_path: Path to the files to add to the base_image as layers
         backend: The CLI tool to use to perform the oci image pull/push. One of: "skopeo", "oras"
         modelcard: Optional, path to the modelcard to additionally include as a layer
-        backend_registry: Optional, a dict of backends available to be used to perform the OCI image download/upload
+        custom_oci_backend: Optional, if you would like to use your own OCI Backend layer, you can provide it here
     Raises:
         ValueError: If the chosen backend is not installed on the host
         ValueError: If the chosen backend is an invalid option
@@ -185,14 +213,16 @@ or
         """
         raise StoreError(msg) from e
 
-
-    if backend not in backend_registry:
-        msg = f"'{backend}' is not an available backend to use. Available backends: {backend_registry.keys()}"
+    # If a custom backend is provided, use it, else fetch the backend out of the registry
+    if custom_oci_backend:
+        backend_def = custom_oci_backend
+    elif backend in DEFAULT_BACKENDS:
+        # Fetching the backend definition can throw an error, but it should bubble up as it has the appropriate messaging
+        backend_def = DEFAULT_BACKENDS[backend]()
+    else:
+        msg = f"'{backend}' is not an available backend to use. Available backends: {DEFAULT_BACKENDS.keys()}"
         raise ValueError(msg)
 
-    # Fetching the backend definition can throw an error, but it should bubble up as it has the appropriate messaging
-    backend_def = backend_registry[backend]()
-
     if not backend_def["is_available"]():
         msg = f"Backend '{backend}' is selected, but not available on the system. Ensure the dependencies for '{backend}' are installed in your environment."
         raise ValueError(msg)
@@ -201,9 +231,50 @@ or
         dest_dir = tempfile.mkdtemp()
     local_image_path = Path(dest_dir)
     backend_def["pull"](base_image, local_image_path)
-    oci_layers_on_top(local_image_path, model_files, modelcard)
+    # Extract the absolute path from the files found in the path
+    files = [file[0] for file in get_files_from_path(model_files_path)]
+    oci_layers_on_top(local_image_path, files, modelcard)
     backend_def["push"](local_image_path, oci_ref)
 
     # Return the OCI URI
 
     return f"oci://{oci_ref}"
+
+
+def get_files_from_path(path: str) -> list[tuple[str, str]]:
+    """Given a path, get the list of files.
+
+    If the path points to a single file, that file's absolute_path and filename will be the only entry returned
+
+    If the path points to a directory, the directory will be walked to fetch all the absolute and relative filepaths for each file
+
+    Args:
+        path: Location (directory or file) to extract filenames from
+
+    Returns:
+        A list of 2-entry tuples containing (absolute_path, relative_path) from the path provided
+
+    Raises:
+        ValueError: If the path provided does not already exist.
+    """
+    if not os.path.exists(path):
+        msg = f"Path '{path}' does not exist. Please ensure path is correct."
+        raise ValueError(msg)
+
+    files = []
+
+    is_file = os.path.isfile(path)
+    if is_file:
+        # When just a single file, return it
+        filename = os.path.basename(path)
+        file = (path, filename)
+        files.append(file)
+        return files
+
+    for root, _, filenames in os.walk(path):
+        for filename in filenames:
+            absolute_path = os.path.join(root, filename)
+            relative_path = os.path.relpath(absolute_path, path)
+            files.append((absolute_path, relative_path))
+
+    return files
diff --git a/clients/python/src/mr_openapi/models/model_artifact.py b/clients/python/src/mr_openapi/models/model_artifact.py
index 66b5fc22..aea9ce2e 100644
--- a/clients/python/src/mr_openapi/models/model_artifact.py
+++ b/clients/python/src/mr_openapi/models/model_artifact.py
@@ -70,6 +70,31 @@ class ModelArtifact(BaseModel):
     service_account_name: StrictStr | None = Field(
         default=None, description="Name of the service account with storage secret.", alias="serviceAccountName"
     )
+    model_source_kind: StrictStr | None = Field(
+        default=None,
+        description="A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.",
+        alias="modelSourceKind",
+    )
+    model_source_class: StrictStr | None = Field(
+        default=None,
+        description="A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.",
+        alias="modelSourceClass",
+    )
+    model_source_group: StrictStr | None = Field(
+        default=None,
+        description="Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.",
+        alias="modelSourceGroup",
+    )
+    model_source_id: StrictStr | None = Field(
+        default=None,
+        description="A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.",
+        alias="modelSourceId",
+    )
+    model_source_name: StrictStr | None = Field(
+        default=None,
+        description="A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.",
+        alias="modelSourceName",
+    )
     __properties: ClassVar[list[str]] = [
         "customProperties",
         "description",
@@ -86,6 +111,11 @@ class ModelArtifact(BaseModel):
         "storagePath",
         "modelFormatVersion",
         "serviceAccountName",
+        "modelSourceKind",
+        "modelSourceClass",
+        "modelSourceGroup",
+        "modelSourceId",
+        "modelSourceName",
     ]
 
     model_config = ConfigDict(
@@ -170,5 +200,10 @@ class ModelArtifact(BaseModel):
                 "storagePath": obj.get("storagePath"),
                 "modelFormatVersion": obj.get("modelFormatVersion"),
                 "serviceAccountName": obj.get("serviceAccountName"),
+                "modelSourceKind": obj.get("modelSourceKind"),
+                "modelSourceClass": obj.get("modelSourceClass"),
+                "modelSourceGroup": obj.get("modelSourceGroup"),
+                "modelSourceId": obj.get("modelSourceId"),
+                "modelSourceName": obj.get("modelSourceName"),
             }
         )
diff --git a/clients/python/src/mr_openapi/models/model_artifact_create.py b/clients/python/src/mr_openapi/models/model_artifact_create.py
index 074cf613..8cc50559 100644
--- a/clients/python/src/mr_openapi/models/model_artifact_create.py
+++ b/clients/python/src/mr_openapi/models/model_artifact_create.py
@@ -59,6 +59,31 @@ class ModelArtifactCreate(BaseModel):
     service_account_name: StrictStr | None = Field(
         default=None, description="Name of the service account with storage secret.", alias="serviceAccountName"
     )
+    model_source_kind: StrictStr | None = Field(
+        default=None,
+        description="A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.",
+        alias="modelSourceKind",
+    )
+    model_source_class: StrictStr | None = Field(
+        default=None,
+        description="A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.",
+        alias="modelSourceClass",
+    )
+    model_source_group: StrictStr | None = Field(
+        default=None,
+        description="Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.",
+        alias="modelSourceGroup",
+    )
+    model_source_id: StrictStr | None = Field(
+        default=None,
+        description="A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.",
+        alias="modelSourceId",
+    )
+    model_source_name: StrictStr | None = Field(
+        default=None,
+        description="A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.",
+        alias="modelSourceName",
+    )
     __properties: ClassVar[list[str]] = [
         "customProperties",
         "description",
@@ -72,6 +97,11 @@ class ModelArtifactCreate(BaseModel):
         "storagePath",
         "modelFormatVersion",
         "serviceAccountName",
+        "modelSourceKind",
+        "modelSourceClass",
+        "modelSourceGroup",
+        "modelSourceId",
+        "modelSourceName",
     ]
 
     model_config = ConfigDict(
@@ -147,5 +177,10 @@ class ModelArtifactCreate(BaseModel):
                 "storagePath": obj.get("storagePath"),
                 "modelFormatVersion": obj.get("modelFormatVersion"),
                 "serviceAccountName": obj.get("serviceAccountName"),
+                "modelSourceKind": obj.get("modelSourceKind"),
+                "modelSourceClass": obj.get("modelSourceClass"),
+                "modelSourceGroup": obj.get("modelSourceGroup"),
+                "modelSourceId": obj.get("modelSourceId"),
+                "modelSourceName": obj.get("modelSourceName"),
             }
         )
diff --git a/clients/python/src/mr_openapi/models/model_artifact_update.py b/clients/python/src/mr_openapi/models/model_artifact_update.py
index c9825d5f..da6ce71d 100644
--- a/clients/python/src/mr_openapi/models/model_artifact_update.py
+++ b/clients/python/src/mr_openapi/models/model_artifact_update.py
@@ -55,6 +55,31 @@ class ModelArtifactUpdate(BaseModel):
     service_account_name: StrictStr | None = Field(
         default=None, description="Name of the service account with storage secret.", alias="serviceAccountName"
     )
+    model_source_kind: StrictStr | None = Field(
+        default=None,
+        description="A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.",
+        alias="modelSourceKind",
+    )
+    model_source_class: StrictStr | None = Field(
+        default=None,
+        description="A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.",
+        alias="modelSourceClass",
+    )
+    model_source_group: StrictStr | None = Field(
+        default=None,
+        description="Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.",
+        alias="modelSourceGroup",
+    )
+    model_source_id: StrictStr | None = Field(
+        default=None,
+        description="A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.",
+        alias="modelSourceId",
+    )
+    model_source_name: StrictStr | None = Field(
+        default=None,
+        description="A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.",
+        alias="modelSourceName",
+    )
     __properties: ClassVar[list[str]] = [
         "customProperties",
         "description",
@@ -67,6 +92,11 @@ class ModelArtifactUpdate(BaseModel):
         "storagePath",
         "modelFormatVersion",
         "serviceAccountName",
+        "modelSourceKind",
+        "modelSourceClass",
+        "modelSourceGroup",
+        "modelSourceId",
+        "modelSourceName",
     ]
 
     model_config = ConfigDict(
@@ -141,5 +171,10 @@ class ModelArtifactUpdate(BaseModel):
                 "storagePath": obj.get("storagePath"),
                 "modelFormatVersion": obj.get("modelFormatVersion"),
                 "serviceAccountName": obj.get("serviceAccountName"),
+                "modelSourceKind": obj.get("modelSourceKind"),
+                "modelSourceClass": obj.get("modelSourceClass"),
+                "modelSourceGroup": obj.get("modelSourceGroup"),
+                "modelSourceId": obj.get("modelSourceId"),
+                "modelSourceName": obj.get("modelSourceName"),
             }
         )
diff --git a/clients/python/src/mr_openapi/models/serving_environment.py b/clients/python/src/mr_openapi/models/serving_environment.py
index 42819bdd..2d1d7096 100644
--- a/clients/python/src/mr_openapi/models/serving_environment.py
+++ b/clients/python/src/mr_openapi/models/serving_environment.py
@@ -35,10 +35,7 @@ class ServingEnvironment(BaseModel):
         description="The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.",
         alias="externalId",
     )
-    name: StrictStr | None = Field(
-        default=None,
-        description="The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.",
-    )
+    name: StrictStr = Field(description="The name of the ServingEnvironment.")
     id: StrictStr | None = Field(default=None, description="The unique server generated id of the resource.")
     create_time_since_epoch: StrictStr | None = Field(
         default=None,
diff --git a/clients/python/src/mr_openapi/models/serving_environment_create.py b/clients/python/src/mr_openapi/models/serving_environment_create.py
index f935a8e7..4c449caf 100644
--- a/clients/python/src/mr_openapi/models/serving_environment_create.py
+++ b/clients/python/src/mr_openapi/models/serving_environment_create.py
@@ -35,10 +35,7 @@ class ServingEnvironmentCreate(BaseModel):
         description="The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.",
         alias="externalId",
     )
-    name: StrictStr | None = Field(
-        default=None,
-        description="The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.",
-    )
+    name: StrictStr = Field(description="The name of the ServingEnvironment.")
     __properties: ClassVar[list[str]] = ["customProperties", "description", "externalId", "name"]
 
     model_config = ConfigDict(
diff --git a/clients/ui/bff/go.mod b/clients/ui/bff/go.mod
index 0e06a815..1f4e66b4 100644
--- a/clients/ui/bff/go.mod
+++ b/clients/ui/bff/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/rs/cors v1.11.1
 	github.com/stretchr/testify v1.10.0
 	k8s.io/api v0.31.2
-	k8s.io/apimachinery v0.32.2
+	k8s.io/apimachinery v0.32.3
 	k8s.io/client-go v0.31.2
 	sigs.k8s.io/controller-runtime v0.19.1
 )
diff --git a/clients/ui/bff/go.sum b/clients/ui/bff/go.sum
index c5149fc7..627a9e3d 100644
--- a/clients/ui/bff/go.sum
+++ b/clients/ui/bff/go.sum
@@ -182,8 +182,8 @@ k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
 k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
 k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
 k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
-k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
-k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
 k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
 k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
diff --git a/clients/ui/bff/internal/api/app.go b/clients/ui/bff/internal/api/app.go
index 98bfee87..0a3ecd61 100644
--- a/clients/ui/bff/internal/api/app.go
+++ b/clients/ui/bff/internal/api/app.go
@@ -3,11 +3,12 @@ package api
 import (
 	"context"
 	"fmt"
-	helper "github.com/kubeflow/model-registry/ui/bff/internal/helpers"
 	"log/slog"
 	"net/http"
 	"path"
 
+	helper "github.com/kubeflow/model-registry/ui/bff/internal/helpers"
+
 	"github.com/kubeflow/model-registry/ui/bff/internal/config"
 	"github.com/kubeflow/model-registry/ui/bff/internal/integrations"
 	"github.com/kubeflow/model-registry/ui/bff/internal/repositories"
@@ -19,26 +20,29 @@ import (
 const (
 	Version = "1.0.0"
 
-	PathPrefix                   = "/model-registry"
-	ApiPathPrefix                = "/api/v1"
-	ModelRegistryId              = "model_registry_id"
-	RegisteredModelId            = "registered_model_id"
-	ModelVersionId               = "model_version_id"
-	ModelArtifactId              = "model_artifact_id"
-	ArtifactId                   = "artifact_id"
-	HealthCheckPath              = ApiPathPrefix + "/healthcheck"
-	UserPath                     = ApiPathPrefix + "/user"
-	ModelRegistryListPath        = ApiPathPrefix + "/model_registry"
-	NamespaceListPath            = ApiPathPrefix + "/namespaces"
-	ModelRegistryPath            = ModelRegistryListPath + "/:" + ModelRegistryId
-	RegisteredModelListPath      = ModelRegistryPath + "/registered_models"
-	RegisteredModelPath          = RegisteredModelListPath + "/:" + RegisteredModelId
-	RegisteredModelVersionsPath  = RegisteredModelPath + "/versions"
-	ModelVersionListPath         = ModelRegistryPath + "/model_versions"
-	ModelVersionPath             = ModelVersionListPath + "/:" + ModelVersionId
-	ModelVersionArtifactListPath = ModelVersionPath + "/artifacts"
-	ModelArtifactListPath        = ModelRegistryPath + "/model_artifacts"
-	ModelArtifactPath            = ModelArtifactListPath + "/:" + ModelArtifactId
+	PathPrefix                    = "/model-registry"
+	ApiPathPrefix                 = "/api/v1"
+	ModelRegistryId               = "model_registry_id"
+	RegisteredModelId             = "registered_model_id"
+	ModelVersionId                = "model_version_id"
+	ModelArtifactId               = "model_artifact_id"
+	ArtifactId                    = "artifact_id"
+	HealthCheckPath               = ApiPathPrefix + "/healthcheck"
+	UserPath                      = ApiPathPrefix + "/user"
+	ModelRegistryListPath         = ApiPathPrefix + "/model_registry"
+	ModelRegistryPath             = ModelRegistryListPath + "/:" + ModelRegistryId
+	NamespaceListPath             = ApiPathPrefix + "/namespaces"
+	SettingsPath                  = ApiPathPrefix + "/settings"
+	ModelRegistrySettingsListPath = SettingsPath + "/model_registry"
+	ModelRegistrySettingsPath     = ModelRegistrySettingsListPath + "/:" + ModelRegistryId
+	RegisteredModelListPath       = ModelRegistryPath + "/registered_models"
+	RegisteredModelPath           = RegisteredModelListPath + "/:" + RegisteredModelId
+	RegisteredModelVersionsPath   = RegisteredModelPath + "/versions"
+	ModelVersionListPath          = ModelRegistryPath + "/model_versions"
+	ModelVersionPath              = ModelVersionListPath + "/:" + ModelVersionId
+	ModelVersionArtifactListPath  = ModelVersionPath + "/artifacts"
+	ModelArtifactListPath         = ModelRegistryPath + "/model_artifacts"
+	ModelArtifactPath             = ModelArtifactListPath + "/:" + ModelArtifactId
 
 	ArtifactListPath = ModelRegistryPath + "/artifacts"
 	ArtifactPath     = ArtifactListPath + "/:" + ArtifactId
@@ -119,14 +123,18 @@ func (app *App) Routes() http.Handler {
 	apiRouter.PATCH(ArtifactPath, app.AttachNamespace(app.PerformSARonSpecificService(app.AttachRESTClient(app.UpdateArtifactHandler))))
 	apiRouter.GET(ModelVersionArtifactListPath, app.AttachNamespace(app.PerformSARonSpecificService(app.AttachRESTClient(app.GetAllModelArtifactsByModelVersionHandler))))
 	apiRouter.POST(ModelVersionArtifactListPath, app.AttachNamespace(app.PerformSARonSpecificService(app.AttachRESTClient(app.CreateModelArtifactByModelVersionHandler))))
-	apiRouter.PATCH(ModelRegistryPath, app.AttachNamespace(app.PerformSARonSpecificService(app.AttachRESTClient(app.UpdateModelVersionHandler))))
 
 	// Kubernetes routes
 	apiRouter.GET(UserPath, app.UserHandler)
-	// Perform SAR to Get List Services by Namespace
-	apiRouter.GET(ModelRegistryListPath, app.AttachNamespace(app.PerformSARonGetListServicesByNamespace(app.ModelRegistryHandler)))
+	apiRouter.GET(ModelRegistryListPath, app.AttachNamespace(app.PerformSARonGetListServicesByNamespace(app.GetAllModelRegistriesHandler)))
 	if app.config.StandaloneMode {
 		apiRouter.GET(NamespaceListPath, app.GetNamespacesHandler)
+		//Those endpoints are not implement yet. This is a STUB API to unblock frontend development
+		apiRouter.GET(ModelRegistrySettingsListPath, app.AttachNamespace(app.GetAllModelRegistriesSettingsHandler))
+		apiRouter.POST(ModelRegistrySettingsListPath, app.AttachNamespace(app.CreateModelRegistrySettingsHandler))
+		apiRouter.GET(ModelRegistrySettingsPath, app.AttachNamespace(app.GetModelRegistrySettingsHandler))
+		apiRouter.PATCH(ModelRegistrySettingsPath, app.AttachNamespace(app.UpdateModelRegistrySettingsHandler))
+		apiRouter.DELETE(ModelRegistrySettingsPath, app.AttachNamespace(app.DeleteModelRegistrySettingsHandler))
 	}
 
 	// App Router
diff --git a/clients/ui/bff/internal/api/middleware.go b/clients/ui/bff/internal/api/middleware.go
index 3fcc6891..dd878c16 100644
--- a/clients/ui/bff/internal/api/middleware.go
+++ b/clients/ui/bff/internal/api/middleware.go
@@ -11,7 +11,6 @@ import (
 
 	"github.com/google/uuid"
 	"github.com/julienschmidt/httprouter"
-	"github.com/kubeflow/model-registry/ui/bff/internal/config"
 	"github.com/kubeflow/model-registry/ui/bff/internal/constants"
 	helper "github.com/kubeflow/model-registry/ui/bff/internal/helpers"
 	"github.com/kubeflow/model-registry/ui/bff/internal/integrations"
@@ -113,11 +112,18 @@ func (app *App) AttachRESTClient(next func(http.ResponseWriter, *http.Request, h
 			app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
 		}
 
-		modelRegistryBaseURL, err := resolveModelRegistryURL(r.Context(), namespace, modelRegistryID, app.kubernetesClient, app.config)
+		modelRegistry, err := app.repositories.ModelRegistry.GetModelRegistry(r.Context(), app.kubernetesClient, namespace, modelRegistryID)
 		if err != nil {
 			app.notFoundResponse(w, r)
 			return
 		}
+		modelRegistryBaseURL := modelRegistry.ServerAddress
+
+		// If we are in dev mode, we need to resolve the server address to the local host
+		// to allow the client to connect to the model registry via port forwarded from the cluster to the local machine.
+		if app.config.DevMode {
+			modelRegistryBaseURL = app.repositories.ModelRegistry.ResolveServerAddress("localhost", int32(app.config.DevModePort))
+		}
 
 		// Set up a child logger for the rest client that automatically adds the request id to all statements for
 		// tracing.
@@ -141,22 +147,6 @@ func (app *App) AttachRESTClient(next func(http.ResponseWriter, *http.Request, h
 	}
 }
 
-func resolveModelRegistryURL(sessionCtx context.Context, namespace string, serviceName string, client integrations.KubernetesClientInterface, config config.EnvConfig) (string, error) {
-
-	serviceDetails, err := client.GetServiceDetailsByName(sessionCtx, namespace, serviceName)
-	if err != nil {
-		return "", err
-	}
-
-	if config.DevMode {
-		serviceDetails.ClusterIP = "localhost"
-		serviceDetails.HTTPPort = int32(config.DevModePort)
-	}
-
-	url := fmt.Sprintf("http://%s:%d/api/model_registry/v1alpha3", serviceDetails.ClusterIP, serviceDetails.HTTPPort)
-	return url, nil
-}
-
 func (app *App) AttachNamespace(next func(http.ResponseWriter, *http.Request, httprouter.Params)) httprouter.Handle {
 	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 		namespace := r.URL.Query().Get(string(constants.NamespaceHeaderParameterKey))
diff --git a/clients/ui/bff/internal/api/model_registry_handler.go b/clients/ui/bff/internal/api/model_registry_handler.go
index d5ee69e0..4af755e9 100644
--- a/clients/ui/bff/internal/api/model_registry_handler.go
+++ b/clients/ui/bff/internal/api/model_registry_handler.go
@@ -2,15 +2,17 @@ package api
 
 import (
 	"fmt"
+	"net/http"
+
 	"github.com/julienschmidt/httprouter"
 	"github.com/kubeflow/model-registry/ui/bff/internal/constants"
 	"github.com/kubeflow/model-registry/ui/bff/internal/models"
-	"net/http"
 )
 
 type ModelRegistryListEnvelope Envelope[[]models.ModelRegistryModel, None]
+type ModelRegistryEnvelope Envelope[models.ModelRegistryModel, None]
 
-func (app *App) ModelRegistryHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+func (app *App) GetAllModelRegistriesHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 
 	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
 	if !ok || namespace == "" {
diff --git a/clients/ui/bff/internal/api/model_registry_settings_handler.go b/clients/ui/bff/internal/api/model_registry_settings_handler.go
new file mode 100644
index 00000000..c6eb857a
--- /dev/null
+++ b/clients/ui/bff/internal/api/model_registry_settings_handler.go
@@ -0,0 +1,180 @@
+package api
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/julienschmidt/httprouter"
+	"github.com/kubeflow/model-registry/ui/bff/internal/constants"
+	helper "github.com/kubeflow/model-registry/ui/bff/internal/helpers"
+	"github.com/kubeflow/model-registry/ui/bff/internal/models"
+)
+
+type ModelRegistrySettingsListEnvelope Envelope[[]models.ModelRegistryKind, None]
+type ModelRegistrySettingsEnvelope Envelope[models.ModelRegistryKind, None]
+
+func (app *App) GetAllModelRegistriesSettingsHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+	ctxLogger := helper.GetContextLoggerFromReq(r)
+	ctxLogger.Info("This functionality is not implement yet. This is a STUB API to unblock frontend development")
+
+	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
+	if !ok || namespace == "" {
+		app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
+	}
+
+	registries := []models.ModelRegistryKind{createSampleModelRegistry("model-registry", namespace),
+		createSampleModelRegistry("model-registry-dora", namespace),
+		createSampleModelRegistry("model-registry-bella", namespace)}
+
+	modelRegistryRes := ModelRegistrySettingsListEnvelope{
+		Data: registries,
+	}
+
+	err := app.WriteJSON(w, http.StatusOK, modelRegistryRes, nil)
+
+	if err != nil {
+		app.serverErrorResponse(w, r, err)
+	}
+
+}
+
+func (app *App) GetModelRegistrySettingsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	ctxLogger := helper.GetContextLoggerFromReq(r)
+	ctxLogger.Info("This functionality is not implement yet. This is a STUB API to unblock frontend development")
+
+	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
+	if !ok || namespace == "" {
+		app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
+	}
+
+	modelId := ps.ByName(ModelRegistryId)
+	registry := createSampleModelRegistry(modelId, namespace)
+
+	modelRegistryRes := ModelRegistrySettingsEnvelope{
+		Data: registry,
+	}
+
+	err := app.WriteJSON(w, http.StatusOK, modelRegistryRes, nil)
+
+	if err != nil {
+		app.serverErrorResponse(w, r, err)
+	}
+}
+
+func (app *App) CreateModelRegistrySettingsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	ctxLogger := helper.GetContextLoggerFromReq(r)
+	ctxLogger.Info("This functionality is not implement yet. This is a STUB API to unblock frontend development")
+
+	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
+	if !ok || namespace == "" {
+		app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
+	}
+
+	registry := createSampleModelRegistry("new-model-registry", namespace)
+
+	modelRegistryRes := ModelRegistrySettingsEnvelope{
+		Data: registry,
+	}
+
+	w.Header().Set("Location", r.URL.JoinPath(modelRegistryRes.Data.Metadata.Name).String())
+	err := app.WriteJSON(w, http.StatusCreated, modelRegistryRes, nil)
+	if err != nil {
+		app.serverErrorResponse(w, r, fmt.Errorf("error writing JSON"))
+		return
+	}
+
+}
+
+func (app *App) UpdateModelRegistrySettingsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+	ctxLogger := helper.GetContextLoggerFromReq(r)
+	ctxLogger.Info("This functionality is not implement yet. This is a STUB API to unblock frontend development")
+
+	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
+	if !ok || namespace == "" {
+		app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
+	}
+
+	modelId := ps.ByName(ModelRegistryId)
+	registry := createSampleModelRegistry(modelId, namespace)
+
+	modelRegistryRes := ModelRegistrySettingsEnvelope{
+		Data: registry,
+	}
+
+	err := app.WriteJSON(w, http.StatusOK, modelRegistryRes, nil)
+	if err != nil {
+		app.serverErrorResponse(w, r, fmt.Errorf("error writing JSON"))
+		return
+	}
+}
+
+func (app *App) DeleteModelRegistrySettingsHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+	ctxLogger := helper.GetContextLoggerFromReq(r)
+	ctxLogger.Info("This functionality is not implement yet. This is a STUB API to unblock frontend development")
+
+	namespace, ok := r.Context().Value(constants.NamespaceHeaderParameterKey).(string)
+	if !ok || namespace == "" {
+		app.badRequestResponse(w, r, fmt.Errorf("missing namespace in the context"))
+	}
+
+	w.WriteHeader(200)
+}
+
+// This function is a temporary function to create a sample model registry kind until we have a real implementation
+func createSampleModelRegistry(name string, namespace string) models.ModelRegistryKind {
+
+	creationTime, _ := time.Parse(time.RFC3339, "2024-03-14T08:01:42Z")
+	lastTransitionTime, _ := time.Parse(time.RFC3339, "2024-03-22T09:30:02Z")
+
+	return models.ModelRegistryKind{
+		APIVersion: "modelregistry.io/v1alpha1",
+		Kind:       "ModelRegistry",
+		Metadata: models.Metadata{
+			Name:              name,
+			Namespace:         namespace,
+			CreationTimestamp: creationTime,
+			Annotations:       map[string]string{},
+		},
+		Spec: models.ModelRegistrySpec{
+			GRPC: models.EmptyObject{},
+			REST: models.EmptyObject{},
+			Istio: models.IstioConfig{
+				Gateway: models.GatewayConfig{
+					GRPC: models.GRPCConfig{
+						TLS: models.EmptyObject{},
+					},
+					REST: models.RESTConfig{
+						TLS: models.EmptyObject{},
+					},
+				},
+			},
+			DatabaseConfig: models.DatabaseConfig{
+				DatabaseType: models.MySQL,
+				Database:     "model-registry",
+				Host:         "model-registry-db",
+				//intentionally not set
+				// PasswordSecret: models.PasswordSecret{
+				// 	Key:  "database-password",
+				// 	Name: "model-registry-db",
+				// },
+				Port:                        5432,
+				SkipDBCreation:              false,
+				Username:                    "mlmduser",
+				SSLRootCertificateConfigMap: "ssl-config-map",
+				SSLRootCertificateSecret:    "ssl-secret",
+			},
+		},
+		Status: models.Status{
+			Conditions: []models.Condition{
+				{
+					LastTransitionTime: lastTransitionTime,
+					Message:            "Deployment for custom resource " + name + " was successfully created",
+					Reason:             "CreatedDeployment",
+					Status:             "True",
+					Type:               "Progressing",
+				},
+			},
+		},
+	}
+}
diff --git a/clients/ui/bff/internal/mocks/k8s_mock.go b/clients/ui/bff/internal/mocks/k8s_mock.go
index 0cb01444..0b385d4e 100644
--- a/clients/ui/bff/internal/mocks/k8s_mock.go
+++ b/clients/ui/bff/internal/mocks/k8s_mock.go
@@ -192,6 +192,8 @@ func (m *KubernetesClientMock) GetServiceDetails(sessionCtx context.Context, nam
 		return nil, fmt.Errorf("failed to get service details: %w", err)
 	}
 
+	//in the mock we are changing the cluster ip to localhost
+	//to be able to connect with local model registry
 	for i := range originalServices {
 		originalServices[i].ClusterIP = "127.0.0.1"
 		originalServices[i].HTTPPort = 8080
@@ -205,7 +207,8 @@ func (m *KubernetesClientMock) GetServiceDetailsByName(sessionCtx context.Contex
 	if err != nil {
 		return k8s.ServiceDetails{}, fmt.Errorf("failed to get service details: %w", err)
 	}
-	//changing from cluster service ip to localhost
+	//in the mock we are changing the cluster ip to localhost
+	//to be able to connect with local model registry
 	originalService.ClusterIP = "127.0.0.1"
 	originalService.HTTPPort = 8080
 
diff --git a/clients/ui/bff/internal/models/model_registry.go b/clients/ui/bff/internal/models/model_registry.go
index 8e1438c3..81e9d1fd 100644
--- a/clients/ui/bff/internal/models/model_registry.go
+++ b/clients/ui/bff/internal/models/model_registry.go
@@ -1,7 +1,8 @@
 package models
 
 type ModelRegistryModel struct {
-	Name        string `json:"name"`
-	DisplayName string `json:"displayName"`
-	Description string `json:"description"`
+	Name          string `json:"name"`
+	DisplayName   string `json:"displayName"`
+	Description   string `json:"description"`
+	ServerAddress string `json:"serverAddress"`
 }
diff --git a/clients/ui/bff/internal/models/model_registry_kind.go b/clients/ui/bff/internal/models/model_registry_kind.go
new file mode 100644
index 00000000..7ca46dd2
--- /dev/null
+++ b/clients/ui/bff/internal/models/model_registry_kind.go
@@ -0,0 +1,83 @@
+package models
+
+import (
+	"time"
+)
+
+type ModelRegistryKind struct {
+	APIVersion string            `json:"apiVersion"`
+	Kind       string            `json:"kind"`
+	Metadata   Metadata          `json:"metadata"`
+	Spec       ModelRegistrySpec `json:"spec"`
+	Status     Status            `json:"status"`
+}
+
+type Metadata struct {
+	Name              string            `json:"name"`
+	Namespace         string            `json:"namespace"`
+	CreationTimestamp time.Time         `json:"creationTimestamp"`
+	Annotations       map[string]string `json:"annotations,omitempty"`
+}
+
+// EmptyObject represents an empty object at create time, properties here aren't used by the UI
+type EmptyObject struct{}
+
+type ModelRegistrySpec struct {
+	GRPC           EmptyObject    `json:"grpc"` // Empty object at create time, properties here aren't used by the UI
+	REST           EmptyObject    `json:"rest"` // Empty object at create time, properties here aren't used by the UI
+	Istio          IstioConfig    `json:"istio"`
+	DatabaseConfig DatabaseConfig `json:"databaseConfig"`
+}
+
+type IstioConfig struct {
+	Gateway GatewayConfig `json:"gateway"`
+}
+
+type GatewayConfig struct {
+	GRPC GRPCConfig `json:"grpc"`
+	REST RESTConfig `json:"rest"`
+}
+
+type GRPCConfig struct {
+	TLS EmptyObject `json:"tls"` // Empty object at create time, properties here aren't used by the UI
+}
+
+type RESTConfig struct {
+	TLS EmptyObject `json:"tls"` // Empty object at create time, properties here aren't used by the UI
+}
+
+type DatabaseType string
+
+const (
+	MySQL    DatabaseType = "mysql"
+	Postgres DatabaseType = "postgres"
+)
+
+type DatabaseConfig struct {
+	DatabaseType                DatabaseType   `json:"databaseType"`
+	Database                    string         `json:"database"`
+	Host                        string         `json:"host"`
+	PasswordSecret              PasswordSecret `json:"passwordSecret,omitempty"`
+	Port                        int            `json:"port"`
+	SkipDBCreation              bool           `json:"skipDBCreation"`
+	Username                    string         `json:"username"`
+	SSLRootCertificateConfigMap string         `json:"sslRootCertificateConfigMap"`
+	SSLRootCertificateSecret    string         `json:"sslRootCertificateSecret"`
+}
+
+type PasswordSecret struct {
+	Key  string `json:"key"`
+	Name string `json:"name"`
+}
+
+type Status struct {
+	Conditions []Condition `json:"conditions"`
+}
+
+type Condition struct {
+	LastTransitionTime time.Time `json:"lastTransitionTime"`
+	Message            string    `json:"message"`
+	Reason             string    `json:"reason"`
+	Status             string    `json:"status"`
+	Type               string    `json:"type"`
+}
diff --git a/clients/ui/bff/internal/repositories/model_registry.go b/clients/ui/bff/internal/repositories/model_registry.go
index 60ec9bbb..4e27943a 100644
--- a/clients/ui/bff/internal/repositories/model_registry.go
+++ b/clients/ui/bff/internal/repositories/model_registry.go
@@ -3,6 +3,7 @@ package repositories
 import (
 	"context"
 	"fmt"
+
 	k8s "github.com/kubeflow/model-registry/ui/bff/internal/integrations"
 	"github.com/kubeflow/model-registry/ui/bff/internal/models"
 )
@@ -22,14 +23,38 @@ func (m *ModelRegistryRepository) GetAllModelRegistries(sessionCtx context.Conte
 	}
 
 	var registries = []models.ModelRegistryModel{}
-	for _, item := range resources {
+	for _, s := range resources {
+		serverAddress := m.ResolveServerAddress(s.ClusterIP, s.HTTPPort)
 		registry := models.ModelRegistryModel{
-			Name:        item.Name,
-			Description: item.Description,
-			DisplayName: item.DisplayName,
+			Name:          s.Name,
+			Description:   s.Description,
+			DisplayName:   s.DisplayName,
+			ServerAddress: serverAddress,
 		}
 		registries = append(registries, registry)
 	}
 
 	return registries, nil
 }
+
+func (m *ModelRegistryRepository) GetModelRegistry(sessionCtx context.Context, client k8s.KubernetesClientInterface, namespace string, modelRegistryID string) (models.ModelRegistryModel, error) {
+
+	s, err := client.GetServiceDetailsByName(sessionCtx, namespace, modelRegistryID)
+	if err != nil {
+		return models.ModelRegistryModel{}, fmt.Errorf("error fetching model registry: %w", err)
+	}
+
+	modelRegistry := models.ModelRegistryModel{
+		Name:          s.Name,
+		Description:   s.Description,
+		DisplayName:   s.DisplayName,
+		ServerAddress: m.ResolveServerAddress(s.ClusterIP, s.HTTPPort),
+	}
+
+	return modelRegistry, nil
+}
+
+func (m *ModelRegistryRepository) ResolveServerAddress(clusterIP string, httpPort int32) string {
+	url := fmt.Sprintf("http://%s:%d/api/model_registry/v1alpha3", clusterIP, httpPort)
+	return url
+}
diff --git a/clients/ui/frontend/package-lock.json b/clients/ui/frontend/package-lock.json
index 4e59cae9..30e3d9e2 100644
--- a/clients/ui/frontend/package-lock.json
+++ b/clients/ui/frontend/package-lock.json
@@ -29,11 +29,11 @@
         "showdown": "^2.1.0"
       },
       "devDependencies": {
-        "@babel/preset-env": "^7.21.5",
+        "@babel/preset-env": "^7.26.9",
         "@babel/preset-react": "^7.18.6",
         "@babel/preset-typescript": "^7.21.5",
         "@cypress/code-coverage": "^3.13.12",
-        "@module-federation/enhanced": "^0.8.9",
+        "@module-federation/enhanced": "^0.11.1",
         "@mui/icons-material": "^6.4.1",
         "@mui/material": "^6.1.7",
         "@mui/types": "^7.2.20",
@@ -42,7 +42,7 @@
         "@testing-library/cypress": "^10.0.3",
         "@testing-library/dom": "^10.4.0",
         "@testing-library/jest-dom": "^6.6.2",
-        "@testing-library/react": "^16.1.0",
+        "@testing-library/react": "^16.2.0",
         "@testing-library/user-event": "14.6.1",
         "@types/classnames": "^2.3.1",
         "@types/dompurify": "^3.2.0",
@@ -86,11 +86,11 @@
         "svg-url-loader": "^8.0.0",
         "swc-loader": "^0.2.6",
         "terser-webpack-plugin": "^5.3.11",
-        "ts-jest": "^29.2.5",
+        "ts-jest": "^29.2.6",
         "ts-loader": "^9.5.1",
         "tsconfig-paths-webpack-plugin": "^4.1.0",
         "tslib": "^2.7.0",
-        "typescript": "^5.7.2",
+        "typescript": "^5.8.2",
         "url-loader": "^4.1.1",
         "webpack": "^5.97.1",
         "webpack-bundle-analyzer": "^4.10.2",
@@ -102,8 +102,8 @@
         "node": ">=20.0.0"
       },
       "optionalDependencies": {
-        "@typescript-eslint/eslint-plugin": "^8.24.0",
-        "@typescript-eslint/parser": "^8.24.0",
+        "@typescript-eslint/eslint-plugin": "^8.26.1",
+        "@typescript-eslint/parser": "^8.26.1",
         "eslint": "^8.57.0",
         "eslint-config-prettier": "^9.1.0",
         "eslint-import-resolver-node": "^0.3.7",
@@ -112,10 +112,10 @@
         "eslint-plugin-import": "^2.31.0",
         "eslint-plugin-jsx-a11y": "^6.10.2",
         "eslint-plugin-no-only-tests": "^3.1.0",
-        "eslint-plugin-no-relative-import-paths": "^1.5.2",
+        "eslint-plugin-no-relative-import-paths": "^1.6.1",
         "eslint-plugin-prettier": "^5.0.0",
         "eslint-plugin-react": "^7.37.2",
-        "eslint-plugin-react-hooks": "^5.1.0"
+        "eslint-plugin-react-hooks": "^5.2.0"
       }
     },
     "node_modules/@adobe/css-tools": {
@@ -154,9 +154,9 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.26.2",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
-      "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
+      "version": "7.26.8",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
+      "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -195,13 +195,13 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.26.2",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
-      "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
+      "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.26.2",
-        "@babel/types": "^7.26.0",
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9",
         "@jridgewell/gen-mapping": "^0.3.5",
         "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^3.0.2"
@@ -223,28 +223,14 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz",
-      "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/traverse": "^7.25.9",
-        "@babel/types": "^7.25.9"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
-      "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
+      "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/compat-data": "^7.25.9",
+        "@babel/compat-data": "^7.26.5",
         "@babel/helper-validator-option": "^7.25.9",
         "browserslist": "^4.24.0",
         "lru-cache": "^5.1.1",
@@ -295,9 +281,9 @@
       }
     },
     "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz",
-      "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==",
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
+      "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -370,9 +356,9 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
-      "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
+      "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -415,20 +401,6 @@
         "@babel/core": "^7.0.0"
       }
     },
-    "node_modules/@babel/helper-simple-access": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz",
-      "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/traverse": "^7.25.9",
-        "@babel/types": "^7.25.9"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
       "version": "7.25.9",
       "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
@@ -487,26 +459,26 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
-      "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
+      "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/template": "^7.25.9",
-        "@babel/types": "^7.26.0"
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.10"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.26.2",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
-      "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
+      "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
       "license": "MIT",
       "dependencies": {
-        "@babel/types": "^7.26.0"
+        "@babel/types": "^7.26.9"
       },
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -901,15 +873,15 @@
       }
     },
     "node_modules/@babel/plugin-transform-async-generator-functions": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
-      "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
+      "version": "7.26.8",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz",
+      "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9",
+        "@babel/helper-plugin-utils": "^7.26.5",
         "@babel/helper-remap-async-to-generator": "^7.25.9",
-        "@babel/traverse": "^7.25.9"
+        "@babel/traverse": "^7.26.8"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -937,13 +909,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
-      "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz",
+      "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9"
+        "@babel/helper-plugin-utils": "^7.26.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1123,13 +1095,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz",
-      "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==",
+      "version": "7.26.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
+      "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9",
         "@babel/helper-plugin-utils": "^7.25.9"
       },
       "engines": {
@@ -1156,13 +1127,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-for-of": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
-      "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz",
+      "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9",
+        "@babel/helper-plugin-utils": "^7.26.5",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
       },
       "engines": {
@@ -1272,15 +1243,14 @@
       }
     },
     "node_modules/@babel/plugin-transform-modules-commonjs": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz",
-      "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==",
+      "version": "7.26.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz",
+      "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-module-transforms": "^7.25.9",
-        "@babel/helper-plugin-utils": "^7.25.9",
-        "@babel/helper-simple-access": "^7.25.9"
+        "@babel/helper-module-transforms": "^7.26.0",
+        "@babel/helper-plugin-utils": "^7.25.9"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1359,13 +1329,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz",
-      "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==",
+      "version": "7.26.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz",
+      "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9"
+        "@babel/helper-plugin-utils": "^7.26.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1694,13 +1664,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-template-literals": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
-      "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
+      "version": "7.26.8",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz",
+      "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9"
+        "@babel/helper-plugin-utils": "^7.26.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1710,13 +1680,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-typeof-symbol": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
-      "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
+      "version": "7.26.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz",
+      "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9"
+        "@babel/helper-plugin-utils": "^7.26.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1813,15 +1783,15 @@
       }
     },
     "node_modules/@babel/preset-env": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
-      "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz",
+      "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/compat-data": "^7.26.0",
-        "@babel/helper-compilation-targets": "^7.25.9",
-        "@babel/helper-plugin-utils": "^7.25.9",
+        "@babel/compat-data": "^7.26.8",
+        "@babel/helper-compilation-targets": "^7.26.5",
+        "@babel/helper-plugin-utils": "^7.26.5",
         "@babel/helper-validator-option": "^7.25.9",
         "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9",
         "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9",
@@ -1833,9 +1803,9 @@
         "@babel/plugin-syntax-import-attributes": "^7.26.0",
         "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
         "@babel/plugin-transform-arrow-functions": "^7.25.9",
-        "@babel/plugin-transform-async-generator-functions": "^7.25.9",
+        "@babel/plugin-transform-async-generator-functions": "^7.26.8",
         "@babel/plugin-transform-async-to-generator": "^7.25.9",
-        "@babel/plugin-transform-block-scoped-functions": "^7.25.9",
+        "@babel/plugin-transform-block-scoped-functions": "^7.26.5",
         "@babel/plugin-transform-block-scoping": "^7.25.9",
         "@babel/plugin-transform-class-properties": "^7.25.9",
         "@babel/plugin-transform-class-static-block": "^7.26.0",
@@ -1846,21 +1816,21 @@
         "@babel/plugin-transform-duplicate-keys": "^7.25.9",
         "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9",
         "@babel/plugin-transform-dynamic-import": "^7.25.9",
-        "@babel/plugin-transform-exponentiation-operator": "^7.25.9",
+        "@babel/plugin-transform-exponentiation-operator": "^7.26.3",
         "@babel/plugin-transform-export-namespace-from": "^7.25.9",
-        "@babel/plugin-transform-for-of": "^7.25.9",
+        "@babel/plugin-transform-for-of": "^7.26.9",
         "@babel/plugin-transform-function-name": "^7.25.9",
         "@babel/plugin-transform-json-strings": "^7.25.9",
         "@babel/plugin-transform-literals": "^7.25.9",
         "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
         "@babel/plugin-transform-member-expression-literals": "^7.25.9",
         "@babel/plugin-transform-modules-amd": "^7.25.9",
-        "@babel/plugin-transform-modules-commonjs": "^7.25.9",
+        "@babel/plugin-transform-modules-commonjs": "^7.26.3",
         "@babel/plugin-transform-modules-systemjs": "^7.25.9",
         "@babel/plugin-transform-modules-umd": "^7.25.9",
         "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9",
         "@babel/plugin-transform-new-target": "^7.25.9",
-        "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
+        "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6",
         "@babel/plugin-transform-numeric-separator": "^7.25.9",
         "@babel/plugin-transform-object-rest-spread": "^7.25.9",
         "@babel/plugin-transform-object-super": "^7.25.9",
@@ -1876,17 +1846,17 @@
         "@babel/plugin-transform-shorthand-properties": "^7.25.9",
         "@babel/plugin-transform-spread": "^7.25.9",
         "@babel/plugin-transform-sticky-regex": "^7.25.9",
-        "@babel/plugin-transform-template-literals": "^7.25.9",
-        "@babel/plugin-transform-typeof-symbol": "^7.25.9",
+        "@babel/plugin-transform-template-literals": "^7.26.8",
+        "@babel/plugin-transform-typeof-symbol": "^7.26.7",
         "@babel/plugin-transform-unicode-escapes": "^7.25.9",
         "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
         "@babel/plugin-transform-unicode-regex": "^7.25.9",
         "@babel/plugin-transform-unicode-sets-regex": "^7.25.9",
         "@babel/preset-modules": "0.1.6-no-external-plugins",
         "babel-plugin-polyfill-corejs2": "^0.4.10",
-        "babel-plugin-polyfill-corejs3": "^0.10.6",
+        "babel-plugin-polyfill-corejs3": "^0.11.0",
         "babel-plugin-polyfill-regenerator": "^0.6.1",
-        "core-js-compat": "^3.38.1",
+        "core-js-compat": "^3.40.0",
         "semver": "^6.3.1"
       },
       "engines": {
@@ -1953,9 +1923,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
-      "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
+      "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
       "license": "MIT",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
@@ -1965,30 +1935,30 @@
       }
     },
     "node_modules/@babel/template": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
-      "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
+      "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.25.9",
-        "@babel/parser": "^7.25.9",
-        "@babel/types": "^7.25.9"
+        "@babel/code-frame": "^7.26.2",
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz",
-      "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
+      "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.25.9",
-        "@babel/generator": "^7.25.9",
-        "@babel/parser": "^7.25.9",
-        "@babel/template": "^7.25.9",
-        "@babel/types": "^7.25.9",
+        "@babel/code-frame": "^7.26.2",
+        "@babel/generator": "^7.26.9",
+        "@babel/parser": "^7.26.9",
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.9",
         "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
@@ -1997,9 +1967,9 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
-      "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
+      "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
       "license": "MIT",
       "dependencies": {
         "@babel/helper-string-parser": "^7.25.9",
@@ -3032,12 +3002,12 @@
       "license": "MIT"
     },
     "node_modules/@module-federation/bridge-react-webpack-plugin": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.8.9.tgz",
-      "integrity": "sha512-gM5fnWvFfTEfh5UjSmNod1H/np/pUUjvJnnN31ucKcZ21mL6wJo0A+Vfj8a5gv34S+1n5lWR1b6eVv9CofwgDQ==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.11.1.tgz",
+      "integrity": "sha512-SUoJE7dEQZVUwBGN+mcMxLyQcQT0RAOZfcjp6Jq3uS6dTtEmJiR4KKGAaNYBlaszBh/HxmiM1U+zE3G126O7tQ==",
       "dev": true,
       "dependencies": {
-        "@module-federation/sdk": "0.8.9",
+        "@module-federation/sdk": "0.11.1",
         "@types/semver": "7.5.8",
         "semver": "7.6.3"
       }
@@ -3055,13 +3025,13 @@
       }
     },
     "node_modules/@module-federation/data-prefetch": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.8.9.tgz",
-      "integrity": "sha512-doUzJ5uuLPrN5e/NMvKuyUE/lqiZFgGzFITwBF90/KKqOq7R2ZWh43R4RJgkUYfs/F119AYXZ9jxrSphIh+cjg==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.11.1.tgz",
+      "integrity": "sha512-94CiNMHqFdaP93RzzEkMyAQcjXMdlSrsMd9R0htzW23keSnerWnmTcRfkvuzKSAR5cqYszGlrYH9CLyMl1T56A==",
       "dev": true,
       "dependencies": {
-        "@module-federation/runtime": "0.8.9",
-        "@module-federation/sdk": "0.8.9",
+        "@module-federation/runtime": "0.11.1",
+        "@module-federation/sdk": "0.11.1",
         "fs-extra": "9.1.0"
       },
       "peerDependencies": {
@@ -3070,22 +3040,22 @@
       }
     },
     "node_modules/@module-federation/dts-plugin": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.8.9.tgz",
-      "integrity": "sha512-Q40yqdQvTV8QRoegjUquTejhLl2gZoPv623hc8MWmGuP2vXLBIJJ9iV849LkFLeE8xaUgtwmn2uCQeBh2aJNjg==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.11.1.tgz",
+      "integrity": "sha512-1fsnNb1dIYFoZ9Gk0gVXb/7+YDm0yWSBsVQvGLBOxMSaf1aF12mVi7MF9+l/ax4BgtgFvqVwekKssAbOXrjsig==",
       "dev": true,
       "dependencies": {
-        "@module-federation/error-codes": "0.8.9",
-        "@module-federation/managers": "0.8.9",
-        "@module-federation/sdk": "0.8.9",
-        "@module-federation/third-party-dts-extractor": "0.8.9",
+        "@module-federation/error-codes": "0.11.1",
+        "@module-federation/managers": "0.11.1",
+        "@module-federation/sdk": "0.11.1",
+        "@module-federation/third-party-dts-extractor": "0.11.1",
         "adm-zip": "^0.5.10",
         "ansi-colors": "^4.1.3",
-        "axios": "^1.7.4",
+        "axios": "^1.8.2",
         "chalk": "3.0.0",
         "fs-extra": "9.1.0",
         "isomorphic-ws": "5.0.0",
-        "koa": "2.15.3",
+        "koa": "2.15.4",
         "lodash.clonedeepwith": "4.5.0",
         "log4js": "6.9.1",
         "node-schedule": "2.1.1",
@@ -3116,21 +3086,21 @@
       }
     },
     "node_modules/@module-federation/enhanced": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.8.9.tgz",
-      "integrity": "sha512-feZUhznCyFkyivWNSFPN+FHMZmyrxlKBhWW8ldWsp/LlKIzWI75vMLimpiydBWnWTZgnOhQDBZGZVxSlfnfKHQ==",
-      "dev": true,
-      "dependencies": {
-        "@module-federation/bridge-react-webpack-plugin": "0.8.9",
-        "@module-federation/data-prefetch": "0.8.9",
-        "@module-federation/dts-plugin": "0.8.9",
-        "@module-federation/error-codes": "0.8.9",
-        "@module-federation/inject-external-runtime-core-plugin": "0.8.9",
-        "@module-federation/managers": "0.8.9",
-        "@module-federation/manifest": "0.8.9",
-        "@module-federation/rspack": "0.8.9",
-        "@module-federation/runtime-tools": "0.8.9",
-        "@module-federation/sdk": "0.8.9",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.11.1.tgz",
+      "integrity": "sha512-B+2kWuzkwEZvpLxwiah2gxHov9Xw2fmXZKvMqBwDSGCOobTtdipAjAeG0AXm7mdEO1Xw/SkZgEDLYQxW27exjA==",
+      "dev": true,
+      "dependencies": {
+        "@module-federation/bridge-react-webpack-plugin": "0.11.1",
+        "@module-federation/data-prefetch": "0.11.1",
+        "@module-federation/dts-plugin": "0.11.1",
+        "@module-federation/error-codes": "0.11.1",
+        "@module-federation/inject-external-runtime-core-plugin": "0.11.1",
+        "@module-federation/managers": "0.11.1",
+        "@module-federation/manifest": "0.11.1",
+        "@module-federation/rspack": "0.11.1",
+        "@module-federation/runtime-tools": "0.11.1",
+        "@module-federation/sdk": "0.11.1",
         "btoa": "^1.2.1",
         "upath": "2.0.1"
       },
@@ -3152,40 +3122,40 @@
       }
     },
     "node_modules/@module-federation/error-codes": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.8.9.tgz",
-      "integrity": "sha512-yUA3GZjOy8Ll6l193faXir2veexDaUiLdmptbzC9tIee/iSQiSwIlibdTafCfqaJ62cLZaytOUdmAFAKLv8QQw==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.11.1.tgz",
+      "integrity": "sha512-N1cs1qwrO8cU/OzfnBbr+3FaVbrJk6QEAsQ8H+YxGRrh/kHsR2BKpZCX79jTG27oDbz45FLjQ98YucMMXC24EA==",
       "dev": true
     },
     "node_modules/@module-federation/inject-external-runtime-core-plugin": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.8.9.tgz",
-      "integrity": "sha512-F3s/3Iny20c0Y0CaXgOZdgl7PzgXj6ftCiT5FO6QWmyhzfl2R0o3KFuv54BmwtoimSFky1ShuC/jPluz4EGG1g==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.11.1.tgz",
+      "integrity": "sha512-giayNxsx7XK86aIzydjtnn8UD8qYyBcVwmsFEEXXA02Jod36CGRFcd0eQ6TzgVEF7kE3iZkpHEKJBGontl32Qg==",
       "dev": true,
       "peerDependencies": {
-        "@module-federation/runtime-tools": "0.8.9"
+        "@module-federation/runtime-tools": "0.11.1"
       }
     },
     "node_modules/@module-federation/managers": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.8.9.tgz",
-      "integrity": "sha512-Fb28sa2iAqwSSSgiPBoL5Kp0RB9ZKJvdwLAEwZBV2c2LyZUqJCdTjmYuB5Q2sWB9wsudd/ijV6WWNK/nVoJxdA==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.11.1.tgz",
+      "integrity": "sha512-qEgJE4qFfIaPVpGncsCvn5eqC6VJ8wT2jwPF2+27cpU5IqZwvfIBZlIjUkGEDPjUtbS10JdGKBfLFrcI+p9i5A==",
       "dev": true,
       "dependencies": {
-        "@module-federation/sdk": "0.8.9",
+        "@module-federation/sdk": "0.11.1",
         "find-pkg": "2.0.0",
         "fs-extra": "9.1.0"
       }
     },
     "node_modules/@module-federation/manifest": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.8.9.tgz",
-      "integrity": "sha512-48jIv50l0tTx+CVzs2JKMlTO0mcUvFRDxqO79nJaL1DLfpRQoCRQjCA8sXUp5PPIyJ8MHQqkHK/dnaal4NlR/A==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.11.1.tgz",
+      "integrity": "sha512-y4zoS4QJMn6OVtY3Wkq3khtjVCU3J8Pnb1CbjM5xoJ7jy/qZhTnDJd194czoQgzx4MnP+BR5jshEQha9GCFPzg==",
       "dev": true,
       "dependencies": {
-        "@module-federation/dts-plugin": "0.8.9",
-        "@module-federation/managers": "0.8.9",
-        "@module-federation/sdk": "0.8.9",
+        "@module-federation/dts-plugin": "0.11.1",
+        "@module-federation/managers": "0.11.1",
+        "@module-federation/sdk": "0.11.1",
         "chalk": "3.0.0",
         "find-pkg": "2.0.0"
       }
@@ -3204,18 +3174,19 @@
       }
     },
     "node_modules/@module-federation/rspack": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.8.9.tgz",
-      "integrity": "sha512-wuHRx+aUYhsrAQzU5yKQksaOJ8pwhuQJDl8I86xmzkEsnxcWsZcn49xIR4t7v1HcYAqjBXLNXSoQwkIMH9zu2w==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.11.1.tgz",
+      "integrity": "sha512-hhikMBb7qSWCKtoCPO1QLoJwu4BsZPAKs9nychLXakMPQEGByrnh/upVb1C2wep2C4Ax9sjuFT7zD/3CdHOmGg==",
       "dev": true,
       "dependencies": {
-        "@module-federation/bridge-react-webpack-plugin": "0.8.9",
-        "@module-federation/dts-plugin": "0.8.9",
-        "@module-federation/inject-external-runtime-core-plugin": "0.8.9",
-        "@module-federation/managers": "0.8.9",
-        "@module-federation/manifest": "0.8.9",
-        "@module-federation/runtime-tools": "0.8.9",
-        "@module-federation/sdk": "0.8.9"
+        "@module-federation/bridge-react-webpack-plugin": "0.11.1",
+        "@module-federation/dts-plugin": "0.11.1",
+        "@module-federation/inject-external-runtime-core-plugin": "0.11.1",
+        "@module-federation/managers": "0.11.1",
+        "@module-federation/manifest": "0.11.1",
+        "@module-federation/runtime-tools": "0.11.1",
+        "@module-federation/sdk": "0.11.1",
+        "btoa": "1.2.1"
       },
       "peerDependencies": {
         "@rspack/core": ">=0.7",
@@ -3232,49 +3203,46 @@
       }
     },
     "node_modules/@module-federation/runtime": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.8.9.tgz",
-      "integrity": "sha512-i+a+/hoT/c+EE52mT+gJrbA6DhL86PY9cd/dIv/oKpLz9i+yYBlG+RA+puc7YsUEO4irbFLvnIMq6AGDUKVzYA==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.11.1.tgz",
+      "integrity": "sha512-yxxa/TRXaNggb34N+oL82J7r9+GZ3gYTCDyGibYqtsC5j7+9oB4tmc0UyhjrGMhg+fF8TAWFZjNKo7ZnyN9LcQ==",
       "dev": true,
       "dependencies": {
-        "@module-federation/error-codes": "0.8.9",
-        "@module-federation/runtime-core": "0.6.17",
-        "@module-federation/sdk": "0.8.9"
+        "@module-federation/error-codes": "0.11.1",
+        "@module-federation/runtime-core": "0.11.1",
+        "@module-federation/sdk": "0.11.1"
       }
     },
     "node_modules/@module-federation/runtime-core": {
-      "version": "0.6.17",
-      "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.6.17.tgz",
-      "integrity": "sha512-PXFN/TT9f64Un6NQYqH1Z0QLhpytW15jkZvTEOV8W7Ed319BECFI0Rv4xAsAGa8zJGFoaM/c7QOQfdFXtKj5Og==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.11.1.tgz",
+      "integrity": "sha512-6KxLfkCl05Ey69Xg/dsjf7fPit9qGXZ0lpwaG2agiCqC3JCDxYjT7tgGvnWhTXCcztb/ThpT+bHrRD4Kw8SMhA==",
       "dev": true,
       "dependencies": {
-        "@module-federation/error-codes": "0.8.9",
-        "@module-federation/sdk": "0.8.9"
+        "@module-federation/error-codes": "0.11.1",
+        "@module-federation/sdk": "0.11.1"
       }
     },
     "node_modules/@module-federation/runtime-tools": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.8.9.tgz",
-      "integrity": "sha512-xBUGx1oOZNuxXjPGdTMrLtAIDrbrN6jE2Mgb9w1qr2mQ4AW9b5TOlxbARBoX4q98xt9oFCGU6Q0eW5XJpsl8AQ==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.11.1.tgz",
+      "integrity": "sha512-8UqMbHJSdkEvKlnlXpR/OjMA77bUbhtmv0I4UO+PA1zBga4y3/St6NOjD66NTINKeWEgsCt1aepXHspduXp33w==",
       "dev": true,
       "dependencies": {
-        "@module-federation/runtime": "0.8.9",
-        "@module-federation/webpack-bundler-runtime": "0.8.9"
+        "@module-federation/runtime": "0.11.1",
+        "@module-federation/webpack-bundler-runtime": "0.11.1"
       }
     },
     "node_modules/@module-federation/sdk": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.8.9.tgz",
-      "integrity": "sha512-QJ60itWC/SPjqduT7wDiF8UGwVU/yJ/Sz+QbnoxB9b7gNLzvI//swAXTo9eOtKsCy/V2BMwjt0F3eOcfnaqllA==",
-      "dev": true,
-      "dependencies": {
-        "isomorphic-rslog": "0.0.7"
-      }
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.11.1.tgz",
+      "integrity": "sha512-QS6zevdQYLCGF6NFf0LysMGARh+dZxMeoRKKDUW5PYi3XOk+tjJ7QsDKybfcBZBNgBJfIuwxh4Oei6WOFJEfRg==",
+      "dev": true
     },
     "node_modules/@module-federation/third-party-dts-extractor": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.8.9.tgz",
-      "integrity": "sha512-53v6B5zfhGlAPpH0SrlJDp9B6kcOcZaUPi6J4L/7ie5F0YVe0vq/mJGOOzAStN9ggJjSrjBZLFT3gFmBYM3Z9A==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.11.1.tgz",
+      "integrity": "sha512-oyyelSLGFObM699p192Cuk/LxEDdzSOywDUXrzPa/qSKNFii5WartjQRc4QPMLnsQaGD7fQVTTiBkvVBcWUdyQ==",
       "dev": true,
       "dependencies": {
         "find-pkg": "2.0.0",
@@ -3283,13 +3251,13 @@
       }
     },
     "node_modules/@module-federation/webpack-bundler-runtime": {
-      "version": "0.8.9",
-      "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.8.9.tgz",
-      "integrity": "sha512-DYLvVi4b2MUYu/B4g5wIC5SHxiODboKHkYGHYapOhCcqOchca/N16gtiAI8eSNjJPc+fgUXUGIyGiB18IlFEeQ==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.11.1.tgz",
+      "integrity": "sha512-XlVegGyCBBLId8Jr6USjPOFYViQ0CCtoYjHpC8y1FOGtuXLGrvnEdFcl4XHlFlp3MY3Rxhr8QigrdZhYe5bRWg==",
       "dev": true,
       "dependencies": {
-        "@module-federation/runtime": "0.8.9",
-        "@module-federation/sdk": "0.8.9"
+        "@module-federation/runtime": "0.11.1",
+        "@module-federation/sdk": "0.11.1"
       }
     },
     "node_modules/@mui/core-downloads-tracker": {
@@ -4753,10 +4721,11 @@
       "license": "MIT"
     },
     "node_modules/@testing-library/react": {
-      "version": "16.1.0",
-      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz",
-      "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==",
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz",
+      "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@babel/runtime": "^7.12.5"
       },
@@ -5373,17 +5342,17 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz",
-      "integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
+      "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.24.0",
-        "@typescript-eslint/type-utils": "8.24.0",
-        "@typescript-eslint/utils": "8.24.0",
-        "@typescript-eslint/visitor-keys": "8.24.0",
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/type-utils": "8.26.1",
+        "@typescript-eslint/utils": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
@@ -5399,20 +5368,20 @@
       "peerDependencies": {
         "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
         "eslint": "^8.57.0 || ^9.0.0",
-        "typescript": ">=4.8.4 <5.8.0"
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz",
-      "integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
+      "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.24.0",
-        "@typescript-eslint/types": "8.24.0",
-        "@typescript-eslint/typescript-estree": "8.24.0",
-        "@typescript-eslint/visitor-keys": "8.24.0",
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/typescript-estree": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -5424,18 +5393,18 @@
       },
       "peerDependencies": {
         "eslint": "^8.57.0 || ^9.0.0",
-        "typescript": ">=4.8.4 <5.8.0"
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz",
-      "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
+      "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.24.0",
-        "@typescript-eslint/visitor-keys": "8.24.0"
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -5446,14 +5415,14 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz",
-      "integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
+      "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.24.0",
-        "@typescript-eslint/utils": "8.24.0",
+        "@typescript-eslint/typescript-estree": "8.26.1",
+        "@typescript-eslint/utils": "8.26.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^2.0.1"
       },
@@ -5466,13 +5435,13 @@
       },
       "peerDependencies": {
         "eslint": "^8.57.0 || ^9.0.0",
-        "typescript": ">=4.8.4 <5.8.0"
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz",
-      "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
+      "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
       "license": "MIT",
       "optional": true,
       "engines": {
@@ -5484,14 +5453,14 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz",
-      "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
+      "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.24.0",
-        "@typescript-eslint/visitor-keys": "8.24.0",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -5507,7 +5476,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "typescript": ">=4.8.4 <5.8.0"
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
@@ -5524,16 +5493,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz",
-      "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
+      "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.24.0",
-        "@typescript-eslint/types": "8.24.0",
-        "@typescript-eslint/typescript-estree": "8.24.0"
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/typescript-estree": "8.26.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -5544,17 +5513,17 @@
       },
       "peerDependencies": {
         "eslint": "^8.57.0 || ^9.0.0",
-        "typescript": ">=4.8.4 <5.8.0"
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.24.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz",
-      "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
+      "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
       "license": "MIT",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.24.0",
+        "@typescript-eslint/types": "8.26.1",
         "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
@@ -6423,9 +6392,9 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.7.9",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
-      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
+      "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
       "dev": true,
       "dependencies": {
         "follow-redirects": "^1.15.6",
@@ -6571,14 +6540,14 @@
       }
     },
     "node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.10.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
-      "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
+      "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.2",
-        "core-js-compat": "^3.38.0"
+        "@babel/helper-define-polyfill-provider": "^0.6.3",
+        "core-js-compat": "^3.40.0"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -6960,9 +6929,9 @@
       "peer": true
     },
     "node_modules/browserslist": {
-      "version": "4.24.2",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz",
-      "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
+      "version": "4.24.4",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+      "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
       "dev": true,
       "funding": [
         {
@@ -6980,9 +6949,9 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "caniuse-lite": "^1.0.30001669",
-        "electron-to-chromium": "^1.5.41",
-        "node-releases": "^2.0.18",
+        "caniuse-lite": "^1.0.30001688",
+        "electron-to-chromium": "^1.5.73",
+        "node-releases": "^2.0.19",
         "update-browserslist-db": "^1.1.1"
       },
       "bin": {
@@ -7226,9 +7195,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001677",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz",
-      "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==",
+      "version": "1.0.30001703",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz",
+      "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==",
       "dev": true,
       "funding": [
         {
@@ -7926,13 +7895,13 @@
       }
     },
     "node_modules/core-js-compat": {
-      "version": "3.39.0",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
-      "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
+      "version": "3.41.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz",
+      "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.24.2"
+        "browserslist": "^4.24.4"
       },
       "funding": {
         "type": "opencollective",
@@ -9204,9 +9173,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.52",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz",
-      "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==",
+      "version": "1.5.114",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz",
+      "integrity": "sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==",
       "dev": true,
       "license": "ISC"
     },
@@ -9935,9 +9904,9 @@
       }
     },
     "node_modules/eslint-plugin-no-relative-import-paths": {
-      "version": "1.5.5",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-no-relative-import-paths/-/eslint-plugin-no-relative-import-paths-1.5.5.tgz",
-      "integrity": "sha512-UjudFFdBbv93v0CsVdEKcMLbBzRIjeK2PubTctX57tgnHxZcMj1Jm8lDBWoETnPxk0S5g5QLSltEM+511yL4+w==",
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-no-relative-import-paths/-/eslint-plugin-no-relative-import-paths-1.6.1.tgz",
+      "integrity": "sha512-YZNeOnsOrJcwhFw0X29MXjIzu2P/f5X2BZDPWw1R3VUYBRFxNIh77lyoL/XrMU9ewZNQPcEvAgL/cBOT1P330A==",
       "license": "ISC",
       "optional": true
     },
@@ -10006,9 +9975,9 @@
       }
     },
     "node_modules/eslint-plugin-react-hooks": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz",
-      "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+      "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
       "license": "MIT",
       "optional": true,
       "engines": {
@@ -13115,15 +13084,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/isomorphic-rslog": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/isomorphic-rslog/-/isomorphic-rslog-0.0.7.tgz",
-      "integrity": "sha512-n6/XnKnZ5eLEj6VllG4XmamXG7/F69nls8dcynHyhcTpsPUYgcgx4ifEaCo4lQJ2uzwfmIT+F0KBGwBcMKmt5g==",
-      "dev": true,
-      "engines": {
-        "node": ">=14.17.6"
-      }
-    },
     "node_modules/isomorphic-ws": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
@@ -14610,9 +14570,9 @@
       }
     },
     "node_modules/koa": {
-      "version": "2.15.3",
-      "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz",
-      "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==",
+      "version": "2.15.4",
+      "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.4.tgz",
+      "integrity": "sha512-7fNBIdrU2PEgLljXoPWoyY4r1e+ToWCmzS/wwMPbUNs7X+5MMET1ObhJBlUkF5uZG9B6QhM2zS1TsH6adegkiQ==",
       "dev": true,
       "dependencies": {
         "accepts": "^1.3.5",
@@ -15882,9 +15842,9 @@
       }
     },
     "node_modules/node-releases": {
-      "version": "2.0.18",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
-      "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+      "version": "2.0.19",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+      "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
       "dev": true,
       "license": "MIT"
     },
@@ -20708,9 +20668,9 @@
       }
     },
     "node_modules/ts-jest": {
-      "version": "29.2.5",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
-      "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==",
+      "version": "29.2.6",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz",
+      "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -20721,7 +20681,7 @@
         "json5": "^2.2.3",
         "lodash.memoize": "^4.1.2",
         "make-error": "^1.3.6",
-        "semver": "^7.6.3",
+        "semver": "^7.7.1",
         "yargs-parser": "^21.1.1"
       },
       "bin": {
@@ -20757,9 +20717,9 @@
       }
     },
     "node_modules/ts-jest/node_modules/semver": {
-      "version": "7.6.3",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
-      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -21073,9 +21033,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
-      "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
+      "version": "5.8.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+      "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
       "devOptional": true,
       "license": "Apache-2.0",
       "bin": {
diff --git a/clients/ui/frontend/package.json b/clients/ui/frontend/package.json
index 3fbb45d3..059e7bed 100644
--- a/clients/ui/frontend/package.json
+++ b/clients/ui/frontend/package.json
@@ -31,20 +31,20 @@
     "cypress:server": "serve ./public-cypress -p 9001 -s -L"
   },
   "devDependencies": {
-    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
-    "@swc/core": "^1.9.1",
-    "@babel/preset-env": "^7.21.5",
+    "@babel/preset-env": "^7.26.9",
     "@babel/preset-react": "^7.18.6",
     "@babel/preset-typescript": "^7.21.5",
     "@cypress/code-coverage": "^3.13.12",
-    "@module-federation/enhanced": "^0.8.9",
-    "@mui/material": "^6.1.7",
+    "@module-federation/enhanced": "^0.11.1",
     "@mui/icons-material": "^6.4.1",
+    "@mui/material": "^6.1.7",
     "@mui/types": "^7.2.20",
+    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
+    "@swc/core": "^1.9.1",
     "@testing-library/cypress": "^10.0.3",
     "@testing-library/dom": "^10.4.0",
     "@testing-library/jest-dom": "^6.6.2",
-    "@testing-library/react": "^16.1.0",
+    "@testing-library/react": "^16.2.0",
     "@testing-library/user-event": "14.6.1",
     "@types/classnames": "^2.3.1",
     "@types/dompurify": "^3.2.0",
@@ -88,11 +88,11 @@
     "svg-url-loader": "^8.0.0",
     "swc-loader": "^0.2.6",
     "terser-webpack-plugin": "^5.3.11",
-    "ts-jest": "^29.2.5",
+    "ts-jest": "^29.2.6",
     "ts-loader": "^9.5.1",
     "tsconfig-paths-webpack-plugin": "^4.1.0",
     "tslib": "^2.7.0",
-    "typescript": "^5.7.2",
+    "typescript": "^5.8.2",
     "url-loader": "^4.1.1",
     "webpack": "^5.97.1",
     "webpack-bundle-analyzer": "^4.10.2",
@@ -109,6 +109,8 @@
     "@patternfly/react-styles": "6.0.0",
     "@patternfly/react-table": "6.1.0",
     "@patternfly/react-templates": "6.1.0",
+    "classnames": "^2.2.6",
+    "dompurify": "^3.2.4",
     "lodash-es": "^4.17.15",
     "npm-run-all": "^4.1.5",
     "react": "^18",
@@ -116,13 +118,11 @@
     "react-router": "^7.1.5",
     "react-router-dom": "^7.1.5",
     "sass": "^1.83.0",
-    "dompurify": "^3.2.4",
-    "showdown": "^2.1.0",
-    "classnames": "^2.2.6"
+    "showdown": "^2.1.0"
   },
   "optionalDependencies": {
-    "@typescript-eslint/eslint-plugin": "^8.24.0",
-    "@typescript-eslint/parser": "^8.24.0",
+    "@typescript-eslint/eslint-plugin": "^8.26.1",
+    "@typescript-eslint/parser": "^8.26.1",
     "eslint": "^8.57.0",
     "eslint-config-prettier": "^9.1.0",
     "eslint-import-resolver-node": "^0.3.7",
@@ -131,10 +131,10 @@
     "eslint-plugin-import": "^2.31.0",
     "eslint-plugin-jsx-a11y": "^6.10.2",
     "eslint-plugin-no-only-tests": "^3.1.0",
-    "eslint-plugin-no-relative-import-paths": "^1.5.2",
+    "eslint-plugin-no-relative-import-paths": "^1.6.1",
     "eslint-plugin-prettier": "^5.0.0",
     "eslint-plugin-react": "^7.37.2",
-    "eslint-plugin-react-hooks": "^5.1.0"
+    "eslint-plugin-react-hooks": "^5.2.0"
   },
   "overrides": {
     "serve": {
diff --git a/clients/ui/frontend/src/app/pages/settings/DeleteModelRegistryModal.tsx b/clients/ui/frontend/src/app/pages/settings/DeleteModelRegistryModal.tsx
new file mode 100644
index 00000000..da3bbc85
--- /dev/null
+++ b/clients/ui/frontend/src/app/pages/settings/DeleteModelRegistryModal.tsx
@@ -0,0 +1,99 @@
+import React from 'react';
+import { Content, TextInput, Stack, StackItem } from '@patternfly/react-core';
+import { Modal } from '@patternfly/react-core/deprecated';
+import { ModelRegistry } from '~/app/types';
+import DashboardModalFooter from '~/shared/components/DashboardModalFooter';
+
+type DeleteModelRegistryModalProps = {
+  modelRegistry: ModelRegistry;
+  onClose: () => void;
+  refresh: () => void;
+};
+
+const DeleteModelRegistryModal: React.FC<DeleteModelRegistryModalProps> = ({
+  modelRegistry: mr,
+  onClose,
+  refresh,
+}) => {
+  const [isSubmitting, setIsSubmitting] = React.useState(false);
+  const [error, setError] = React.useState<Error>();
+  const [confirmInputValue, setConfirmInputValue] = React.useState('');
+  const isDisabled = confirmInputValue.trim() !== mr.name || isSubmitting;
+
+  const onBeforeClose = () => {
+    setConfirmInputValue('');
+    setIsSubmitting(false);
+    setError(undefined);
+    onClose();
+  };
+
+  const onConfirm = async () => {
+    setIsSubmitting(true);
+    setError(undefined);
+    try {
+      // TODO: implement when CRD endpoint is ready
+      // await deleteModelRegistryBackend(mr.name);
+      await refresh();
+      onBeforeClose();
+    } catch (e) {
+      if (e instanceof Error) {
+        setError(e);
+      }
+      setIsSubmitting(false);
+    }
+  };
+
+  return (
+    <Modal
+      data-testid="delete-mr-modal"
+      titleIconVariant="warning"
+      title="Delete model registry?"
+      isOpen
+      onClose={onClose}
+      variant="medium"
+      footer={
+        <DashboardModalFooter
+          submitLabel="Delete model registry"
+          submitButtonVariant="danger"
+          onSubmit={onConfirm}
+          onCancel={onBeforeClose}
+          isSubmitLoading={isSubmitting}
+          isSubmitDisabled={isDisabled}
+          error={error}
+          alertTitle="Error deleting model registry"
+        />
+      }
+    >
+      <Stack hasGutter>
+        <StackItem>
+          <Content>
+            <Content component="p">
+              The <strong>{mr.name}</strong> model registry, its default group, and any permissions
+              associated with it will be deleted. Data located in the database connected to the
+              registry will be unaffected.
+            </Content>
+            <Content component="p">
+              Type <strong>{mr.name}</strong> to confirm deletion:
+            </Content>
+          </Content>
+        </StackItem>
+        <StackItem>
+          <TextInput
+            id="confirm-delete-input"
+            data-testid="confirm-delete-input"
+            aria-label="Confirm delete input"
+            value={confirmInputValue}
+            onChange={(_e, newValue) => setConfirmInputValue(newValue)}
+            onKeyDown={(event) => {
+              if (event.key === 'Enter' && !isDisabled) {
+                onConfirm();
+              }
+            }}
+          />
+        </StackItem>
+      </Stack>
+    </Modal>
+  );
+};
+
+export default DeleteModelRegistryModal;
diff --git a/clients/ui/frontend/src/app/pages/settings/ModelRegistriesTable.tsx b/clients/ui/frontend/src/app/pages/settings/ModelRegistriesTable.tsx
index e9096cb3..f2c98084 100644
--- a/clients/ui/frontend/src/app/pages/settings/ModelRegistriesTable.tsx
+++ b/clients/ui/frontend/src/app/pages/settings/ModelRegistriesTable.tsx
@@ -1,31 +1,75 @@
 import React from 'react';
+import { Button, Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core';
 import { ModelRegistry } from '~/app/types';
 import { Table } from '~/shared/components/table';
 import { modelRegistryColumns } from './columns';
 import ModelRegistriesTableRow from './ModelRegistriesTableRow';
+import DeleteModelRegistryModal from './DeleteModelRegistryModal';
 
 type ModelRegistriesTableProps = {
   modelRegistries: ModelRegistry[];
+  onCreateModelRegistryClick: () => void;
+  refresh: () => void;
 };
 
-const ModelRegistriesTable: React.FC<ModelRegistriesTableProps> = ({ modelRegistries }) => (
+const ModelRegistriesTable: React.FC<ModelRegistriesTableProps> = ({
+  modelRegistries,
+  onCreateModelRegistryClick,
+  refresh,
+}) => {
   // TODO: [Midstream] Complete once we have permissions
-  <Table
-    data-testid="model-registries-table"
-    data={modelRegistries}
-    columns={modelRegistryColumns}
-    rowRenderer={(mr) => (
-      <ModelRegistriesTableRow
-        key={mr.name}
-        modelRegistry={mr}
-        // eslint-disable-next-line @typescript-eslint/no-empty-function
-        onDeleteRegistry={() => {}}
-        // eslint-disable-next-line @typescript-eslint/no-empty-function
-        onEditRegistry={() => {}}
+
+  const [deleteRegistry, setDeleteRegistry] = React.useState<ModelRegistry>();
+
+  return (
+    <>
+      <Table
+        data-testid="model-registries-table"
+        data={modelRegistries}
+        columns={modelRegistryColumns}
+        toolbarContent={
+          <Toolbar>
+            <ToolbarContent>
+              <ToolbarItem>
+                <Button
+                  data-testid="create-model-registry-button"
+                  variant="primary"
+                  onClick={onCreateModelRegistryClick}
+                >
+                  Create model registry
+                </Button>
+              </ToolbarItem>
+            </ToolbarContent>
+          </Toolbar>
+        }
+        rowRenderer={(mr) => (
+          <ModelRegistriesTableRow
+            key={mr.name}
+            modelRegistry={mr}
+            onDeleteRegistry={(i) => setDeleteRegistry(i)}
+            // eslint-disable-next-line @typescript-eslint/no-empty-function
+            onEditRegistry={() => {}}
+          />
+        )}
+        variant="compact"
       />
-    )}
-    variant="compact"
-  />
-);
+      {/* TODO: implement when CRD endpoint is ready */}
+      {/* {editRegistry ? (
+        <CreateModal
+          modelRegistry={editRegistry}
+          onClose={() => setEditRegistry(undefined)}
+          refresh={refresh}
+        />
+      ) : null} */}
+      {deleteRegistry ? (
+        <DeleteModelRegistryModal
+          modelRegistry={deleteRegistry}
+          onClose={() => setDeleteRegistry(undefined)}
+          refresh={refresh}
+        />
+      ) : null}
+    </>
+  );
+};
 
 export default ModelRegistriesTable;
diff --git a/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModal.tsx b/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModal.tsx
new file mode 100644
index 00000000..c0257fbd
--- /dev/null
+++ b/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModal.tsx
@@ -0,0 +1,248 @@
+import * as React from 'react';
+import {
+  Button,
+  Form,
+  FormGroup,
+  HelperText,
+  HelperTextItem,
+  TextInput,
+} from '@patternfly/react-core';
+import { Modal } from '@patternfly/react-core/deprecated';
+import { useNavigate } from 'react-router';
+import ModelRegistryCreateModalFooter from '~/app/pages/settings/ModelRegistryCreateModalFooter';
+import FormSection from '~/shared/components/pf-overrides/FormSection';
+
+import ModelRegistryDatabasePassword from '~/app/pages/settings/ModelRegistryDatabasePassword';
+import K8sNameDescriptionField from '~/concepts/k8s/K8sNameDescriptionField/K8sNameDescriptionField';
+
+type CreateModalProps = {
+  onClose: () => void;
+  // refresh: () => Promise<unknown>;
+  // modelRegistry: ModelRegistry;
+};
+
+const CreateModal: React.FC<CreateModalProps> = ({
+  onClose,
+  // refresh,
+  // modelRegistry,
+}) => {
+  const [error, setError] = React.useState<Error>();
+
+  const [host, setHost] = React.useState('');
+  const [port, setPort] = React.useState('');
+  const [username, setUsername] = React.useState('');
+  const [password, setPassword] = React.useState('');
+  const [database, setDatabase] = React.useState('');
+  //   const [addSecureDB, setAddSecureDB] = React.useState(false);
+  const [isHostTouched, setIsHostTouched] = React.useState(false);
+  const [isPortTouched, setIsPortTouched] = React.useState(false);
+  const [isUsernameTouched, setIsUsernameTouched] = React.useState(false);
+  const [isPasswordTouched, setIsPasswordTouched] = React.useState(false);
+  const [isDatabaseTouched, setIsDatabaseTouched] = React.useState(false);
+  const [showPassword, setShowPassword] = React.useState(false);
+
+  const navigate = useNavigate();
+
+  const onBeforeClose = () => {
+    // setIsSubmitting(false);
+    setError(undefined);
+
+    setHost('');
+    setPort('');
+    setUsername('');
+    setPassword('');
+    setDatabase('');
+    setIsHostTouched(false);
+    setIsPortTouched(false);
+    setIsUsernameTouched(false);
+    setIsPasswordTouched(false);
+    setIsDatabaseTouched(false);
+    setShowPassword(false);
+    onClose();
+  };
+
+  const hasContent = (value: string): boolean => !!value.trim().length;
+
+  const canSubmit = () =>
+    // TODO: implement once we have the endpoint
+    // !isSubmitting &&
+    // isValidK8sName(nameDesc.k8sName.value || translateDisplayNameForK8s(nameDesc.name))
+    // &&
+    hasContent(host) &&
+    hasContent(password) &&
+    hasContent(port) &&
+    hasContent(username) &&
+    hasContent(database);
+  // &&
+  // (!addSecureDB || (secureDBInfo.isValid && !configSecretsError))
+
+  const onSubmit = () => {
+    navigate(`/model-registry-settings`);
+    onClose();
+  };
+
+  return (
+    <Modal
+      isOpen
+      title="Create model registry"
+      onClose={onBeforeClose}
+      actions={[
+        <Button key="create-button" variant="primary" isDisabled={!canSubmit()} onClick={onSubmit}>
+          Create
+        </Button>,
+        <Button key="cancel-button" variant="secondary" onClick={onBeforeClose}>
+          Cancel
+        </Button>,
+      ]}
+      variant="medium"
+      footer={
+        <ModelRegistryCreateModalFooter
+          onCancel={onBeforeClose}
+          onSubmit={onSubmit}
+          submitLabel="Create"
+          // isSubmitLoading={isSubmitting}
+          isSubmitDisabled={!canSubmit()}
+          error={error}
+          alertTitle={`Error ${'creating'} model registry`}
+        />
+      }
+    >
+      <Form>
+        <K8sNameDescriptionField
+          dataTestId="mr"
+          // data={nameDesc}
+          //  onDataChange={setNameDesc}
+        />
+        <FormSection
+          title="Connect to external MySQL database"
+          description="This external database is where model data is stored."
+        >
+          <FormGroup label="Host" isRequired fieldId="mr-host">
+            <TextInput
+              isRequired
+              type="text"
+              id="mr-host"
+              name="mr-host"
+              value={host}
+              onBlur={() => setIsHostTouched(true)}
+              onChange={(_e, value) => setHost(value)}
+              validated={isHostTouched && !hasContent(host) ? 'error' : 'default'}
+            />
+            {isHostTouched && !hasContent(host) && (
+              <HelperText>
+                <HelperTextItem variant="error" data-testid="mr-host-error">
+                  Host cannot be empty
+                </HelperTextItem>
+              </HelperText>
+            )}
+          </FormGroup>
+          <FormGroup label="Port" isRequired fieldId="mr-port">
+            <TextInput
+              isRequired
+              type="text"
+              id="mr-port"
+              name="mr-port"
+              value={port}
+              onBlur={() => setIsPortTouched(true)}
+              onChange={(_e, value) => setPort(value)}
+              validated={isPortTouched && !hasContent(port) ? 'error' : 'default'}
+            />
+            {isPortTouched && !hasContent(port) && (
+              <HelperText>
+                <HelperTextItem variant="error" data-testid="mr-port-error">
+                  Port cannot be empty
+                </HelperTextItem>
+              </HelperText>
+            )}
+          </FormGroup>
+          <FormGroup label="Username" isRequired fieldId="mr-username">
+            <TextInput
+              isRequired
+              type="text"
+              id="mr-username"
+              name="mr-username"
+              value={username}
+              onBlur={() => setIsUsernameTouched(true)}
+              onChange={(_e, value) => setUsername(value)}
+              validated={isUsernameTouched && !hasContent(username) ? 'error' : 'default'}
+            />
+            {isUsernameTouched && !hasContent(username) && (
+              <HelperText>
+                <HelperTextItem variant="error" data-testid="mr-username-error">
+                  Username cannot be empty
+                </HelperTextItem>
+              </HelperText>
+            )}
+          </FormGroup>
+          <FormGroup label="Password" isRequired fieldId="mr-password">
+            <ModelRegistryDatabasePassword
+              password={password || ''}
+              setPassword={setPassword}
+              isPasswordTouched={isPasswordTouched}
+              setIsPasswordTouched={setIsPasswordTouched}
+              showPassword={showPassword}
+              //   editRegistry={mr}
+            />
+          </FormGroup>
+          <FormGroup label="Database" isRequired fieldId="mr-database">
+            <TextInput
+              isRequired
+              type="text"
+              id="mr-database"
+              name="mr-database"
+              value={database}
+              onBlur={() => setIsDatabaseTouched(true)}
+              onChange={(_e, value) => setDatabase(value)}
+              validated={isDatabaseTouched && !hasContent(database) ? 'error' : 'default'}
+            />
+            {isDatabaseTouched && !hasContent(database) && (
+              <HelperText>
+                <HelperTextItem variant="error" data-testid="mr-database-error">
+                  Database cannot be empty
+                </HelperTextItem>
+              </HelperText>
+            )}
+          </FormGroup>
+          {/* {secureDbEnabled && (
+            <>
+              <FormGroup>
+                <Checkbox
+                  label="Add CA certificate to secure database connection"
+                  isChecked={addSecureDB}
+                  onChange={(_e, value) => setAddSecureDB(value)}
+                  id="add-secure-db"
+                  data-testid="add-secure-db-mr-checkbox"
+                  name="add-secure-db"
+                />
+              </FormGroup>
+              {addSecureDB &&
+                (!configSecretsLoaded && !configSecretsError ? (
+                  <EmptyState icon={Spinner} />
+                ) : configSecretsLoaded ? (
+                  <CreateMRSecureDBSection
+                    secureDBInfo={secureDBInfo}
+                    modelRegistryNamespace={modelRegistryNamespace}
+                    k8sName={nameDesc.k8sName.value}
+                    existingCertConfigMaps={configSecrets.configMaps}
+                    existingCertSecrets={configSecrets.secrets}
+                    setSecureDBInfo={setSecureDBInfo}
+                  />
+                ) : (
+                  <Alert
+                    isInline
+                    variant="danger"
+                    title="Error fetching config maps and secrets"
+                    data-testid="error-fetching-resource-alert"
+                  >
+                    {configSecretsError?.message}
+                  </Alert>
+                ))}
+            </>
+          )} */}
+        </FormSection>
+      </Form>
+    </Modal>
+  );
+};
+
+export default CreateModal;
diff --git a/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModalFooter.tsx b/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModalFooter.tsx
new file mode 100644
index 00000000..f3113b22
--- /dev/null
+++ b/clients/ui/frontend/src/app/pages/settings/ModelRegistryCreateModalFooter.tsx
@@ -0,0 +1,85 @@
+import * as React from 'react';
+import {
+  ActionList,
+  ActionListItem,
+  ActionListGroup,
+  Alert,
+  Button,
+  ButtonProps,
+  Stack,
+  StackItem,
+} from '@patternfly/react-core';
+
+type ModelRegistryCreateModalFooterProps = {
+  submitLabel: string;
+  submitButtonVariant?: ButtonProps['variant'];
+  onSubmit: () => void;
+  onCancel: () => void;
+  isSubmitDisabled?: boolean;
+  isSubmitLoading?: boolean;
+  isCancelDisabled?: boolean;
+  alertTitle?: string;
+  error?: Error;
+  alertLinks?: React.ReactNode;
+};
+
+const ModelRegistryCreateModalFooter: React.FC<ModelRegistryCreateModalFooterProps> = ({
+  submitLabel,
+  submitButtonVariant = 'primary',
+  onSubmit,
+  onCancel,
+  isSubmitDisabled,
+  isSubmitLoading,
+  isCancelDisabled,
+  error,
+  alertTitle,
+  alertLinks,
+}) => (
+  // make sure alert uses the full width
+  <Stack hasGutter style={{ flex: 'auto' }}>
+    {error && (
+      <StackItem>
+        <Alert
+          data-testid="error-message-alert"
+          isInline
+          variant="danger"
+          title={alertTitle}
+          actionLinks={alertLinks}
+        >
+          {error.message}
+        </Alert>
+      </StackItem>
+    )}
+    <StackItem>
+      <ActionList>
+        <ActionListGroup>
+          <ActionListItem>
+            <Button
+              key="submit"
+              variant={submitButtonVariant}
+              isDisabled={isSubmitDisabled}
+              onClick={onSubmit}
+              isLoading={isSubmitLoading}
+              data-testid="modal-submit-button"
+            >
+              {submitLabel}
+            </Button>
+          </ActionListItem>
+          <ActionListItem>
+            <Button
+              key="cancel"
+              variant="link"
+              isDisabled={isCancelDisabled}
+              onClick={onCancel}
+              data-testid="modal-cancel-button"
+            >
+              Cancel
+            </Button>
+          </ActionListItem>
+        </ActionListGroup>
+      </ActionList>
+    </StackItem>
+  </Stack>
+);
+
+export default ModelRegistryCreateModalFooter;
diff --git a/clients/ui/frontend/src/app/pages/settings/ModelRegistryDatabasePassword.tsx b/clients/ui/frontend/src/app/pages/settings/ModelRegistryDatabasePassword.tsx
new file mode 100644
index 00000000..1cf7667e
--- /dev/null
+++ b/clients/ui/frontend/src/app/pages/settings/ModelRegistryDatabasePassword.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { HelperText, HelperTextItem } from '@patternfly/react-core';
+import PasswordInput from '~/app/pages/settings/PasswordInput';
+
+type ModelRegistryDatabasePasswordProps = {
+  password: string | undefined;
+  setPassword: (value: string) => void;
+  showPassword?: boolean;
+  isPasswordTouched?: boolean;
+  setIsPasswordTouched: (value: boolean) => void;
+  // editRegistry?: ModelRegistryKind;
+};
+
+const ModelRegistryDatabasePassword: React.FC<ModelRegistryDatabasePasswordProps> = ({
+  password = '',
+  setPassword,
+  showPassword,
+  isPasswordTouched,
+  setIsPasswordTouched,
+  // editRegistry: mr,
+}) => {
+  // TODO: Implement this
+  // const [existingDbPassword, passwordLoaded, passwordLoadError] = useFetchState(
+  //   React.useCallback<FetchStateCallbackPromise<string | undefined>>(async () => {
+  //     if (!mr) {
+  //       return Promise.reject(new NotReadyError('Model registry does not exist'));
+  //     }
+
+  //     const { databasePassword } = await getModelRegistryBackend(mr.metadata.name);
+  //     return databasePassword;
+  //   }, [mr]),
+  //   undefined,
+  // );
+
+  // React.useEffect(() => {
+  //   if (existingDbPassword && mr) {
+  //     setPassword(existingDbPassword);
+  //   }
+  // }, [existingDbPassword, setPassword, mr]);
+
+  const hasContent = (value: string): boolean => !!value.trim().length;
+
+  // if (!passwordLoaded && !passwordLoadError && mr) {
+  //   return <Skeleton screenreaderText="Loading contents" />;
+  // }
+
+  // if (passwordLoadError) {
+  //   return (
+  //     <Alert
+  //       variant="danger"
+  //       isInline
+  //       isPlain
+  //       title="Failed to load the password. The Secret file is missing."
+  //     />
+  //   );
+  // }
+
+  return (
+    <>
+      <PasswordInput
+        isRequired
+        type={showPassword ? 'text' : 'password'}
+        id="mr-password"
+        name="mr-password"
+        value={password}
+        onBlur={() => setIsPasswordTouched(true)}
+        onChange={(_e, value) => setPassword(value)}
+        validated={isPasswordTouched && !hasContent(password) ? 'error' : 'default'}
+      />
+      {isPasswordTouched && !hasContent(password) && (
+        <HelperText>
+          <HelperTextItem variant="error" data-testid="mr-password-error">
+            Password cannot be empty
+          </HelperTextItem>
+        </HelperText>
+      )}
+    </>
+  );
+};
+
+export default ModelRegistryDatabasePassword;
diff --git a/clients/ui/frontend/src/app/pages/settings/ModelRegistrySettings.tsx b/clients/ui/frontend/src/app/pages/settings/ModelRegistrySettings.tsx
index d0e37aa1..3b791c5c 100644
--- a/clients/ui/frontend/src/app/pages/settings/ModelRegistrySettings.tsx
+++ b/clients/ui/frontend/src/app/pages/settings/ModelRegistrySettings.tsx
@@ -7,14 +7,30 @@ import useQueryParamNamespaces from '~/shared/hooks/useQueryParamNamespaces';
 import { isMUITheme } from '~/shared/utilities/const';
 import TitleWithIcon from '~/shared/components/design/TitleWithIcon';
 import { ProjectObjectType } from '~/shared/components/design/utils';
+// import { ModelRegistrySelectorContext } from '~/app/context/ModelRegistrySelectorContext';
 import ModelRegistriesTable from './ModelRegistriesTable';
+import CreateModal from './ModelRegistryCreateModal';
 
 const ModelRegistrySettings: React.FC = () => {
   const queryParams = useQueryParamNamespaces();
-  const [modelRegistries, mrloaded, loadError] = useModelRegistries(queryParams);
+  const [
+    modelRegistries,
+    mrloaded,
+    loadError,
+    // refreshModelRegistries
+  ] = useModelRegistries(queryParams);
+  const [createModalOpen, setCreateModalOpen] = React.useState(false);
   // TODO: [Midstream] Implement this when adding logic for rules review
+  // const { refreshRulesReview } = React.useContext(ModelRegistrySelectorContext);
+
   const loaded = mrloaded; //&& roleBindings.loaded;
 
+  // TODO: implement when refreshModelRegistries() and refreshRulesReview() are added
+  // const refreshAll = React.useCallback(
+  //   () => Promise.all([refreshModelRegistries(), refreshRulesReview()]),
+  //   [refreshModelRegistries, refreshRulesReview],
+  // );
+
   return (
     <>
       <ApplicationsPage
@@ -54,8 +70,21 @@ const ModelRegistrySettings: React.FC = () => {
         }
         provideChildrenPadding
       >
-        <ModelRegistriesTable modelRegistries={modelRegistries} />
+        <ModelRegistriesTable
+          modelRegistries={modelRegistries}
+          onCreateModelRegistryClick={() => {
+            setCreateModalOpen(true);
+          }}
+          // eslint-disable-next-line @typescript-eslint/no-empty-function
+          refresh={() => {}}
+        />
       </ApplicationsPage>
+      {createModalOpen ? (
+        <CreateModal
+          onClose={() => setCreateModalOpen(false)}
+          // refresh={refreshAll}
+        />
+      ) : null}
     </>
   );
 };
diff --git a/clients/ui/frontend/src/app/pages/settings/PasswordInput.tsx b/clients/ui/frontend/src/app/pages/settings/PasswordInput.tsx
new file mode 100644
index 00000000..0f16d8bf
--- /dev/null
+++ b/clients/ui/frontend/src/app/pages/settings/PasswordInput.tsx
@@ -0,0 +1,35 @@
+import * as React from 'react';
+import { Button, InputGroup, TextInput, InputGroupItem } from '@patternfly/react-core';
+import { EyeIcon, EyeSlashIcon } from '@patternfly/react-icons';
+
+type Props = React.ComponentProps<typeof TextInput> & {
+  ariaLabelShow?: string;
+  ariaLabelHide?: string;
+};
+
+const PasswordInput: React.FC<Props> = ({
+  ariaLabelShow = 'Show password',
+  ariaLabelHide = 'Hide password',
+  ...props
+}) => {
+  const [isPasswordHidden, setPasswordHidden] = React.useState(true);
+
+  return (
+    <InputGroup>
+      <InputGroupItem isFill>
+        <TextInput {...props} type={isPasswordHidden ? 'password' : 'text'} />
+      </InputGroupItem>
+      <InputGroupItem>
+        <Button
+          aria-label={isPasswordHidden ? ariaLabelShow : ariaLabelHide}
+          variant="control"
+          onClick={() => setPasswordHidden(!isPasswordHidden)}
+        >
+          {isPasswordHidden ? <EyeSlashIcon /> : <EyeIcon />}
+        </Button>
+      </InputGroupItem>
+    </InputGroup>
+  );
+};
+
+export default PasswordInput;
diff --git a/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/K8sNameDescriptionField.tsx b/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/K8sNameDescriptionField.tsx
new file mode 100644
index 00000000..1d6f3566
--- /dev/null
+++ b/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/K8sNameDescriptionField.tsx
@@ -0,0 +1,125 @@
+import * as React from 'react';
+import {
+  Button,
+  FormGroup,
+  HelperText,
+  HelperTextItem,
+  TextArea,
+  TextInput,
+} from '@patternfly/react-core';
+import ResourceNameDefinitionTooltip from '~/concepts/k8s/ResourceNameDefinitionTootip';
+import ResourceNameField from './ResourceNameField';
+
+// TODO: replace with the actual call once we have the endpoint
+
+/** Companion data hook */
+// export const useK8sNameDescriptionFieldData = (
+//   configuration: UseK8sNameDescriptionDataConfiguration = {},
+// ): UseK8sNameDescriptionFieldData => {
+//   const [data, setData] = React.useState<K8sNameDescriptionFieldData>(() =>
+//     setupDefaults(configuration),
+//   );
+
+//   const onDataChange = React.useCallback<K8sNameDescriptionFieldUpdateFunction>((key, value) => {
+//     setData((currentData) => handleUpdateLogic(currentData)(key, value));
+//   }, []);
+
+//   return { data, onDataChange };
+// };
+
+type K8sNameDescriptionFieldProps = {
+  autoFocusName?: boolean;
+  //   data: UseK8sNameDescriptionFieldData['data'];
+  dataTestId: string;
+  descriptionLabel?: string;
+  nameLabel?: string;
+  nameHelperText?: React.ReactNode;
+  //   onDataChange?: UseK8sNameDescriptionFieldData['onDataChange'];
+  hideDescription?: boolean;
+};
+
+/**
+ * Use in place of any K8s Resource creation / edit.
+ * @see useK8sNameDescriptionFieldData
+ */
+const K8sNameDescriptionField: React.FC<K8sNameDescriptionFieldProps> = ({
+  autoFocusName,
+  //   data,
+  dataTestId,
+  descriptionLabel = 'Description',
+  //   onDataChange,
+  nameLabel = 'Name',
+  nameHelperText,
+  hideDescription,
+}) => {
+  const [showK8sField, setShowK8sField] = React.useState(false);
+
+  //   const { name, description, k8sName } = data;
+
+  return (
+    <>
+      <FormGroup label={nameLabel} isRequired fieldId={`${dataTestId}-name`}>
+        <TextInput
+          //   aria-readonly={!onDataChange}
+          data-testid={`${dataTestId}-name`}
+          id={`${dataTestId}-name`}
+          name={`${dataTestId}-name`}
+          autoFocus={autoFocusName}
+          isRequired
+          //   value={name}
+          //   onChange={(event, value) => onDataChange?.('name', value)}
+        />
+        {nameHelperText || !showK8sField ? (
+          //  &&
+          // !k8sName.state.immutable
+          <HelperText>
+            {nameHelperText && <HelperTextItem>{nameHelperText}</HelperTextItem>}
+            {!showK8sField && (
+              // !k8sName.state.immutable
+              // &&
+              <>
+                {/* {k8sName.value && (
+                  <HelperTextItem>
+                    The resource name will be <b>{k8sName.value}</b>.
+                  </HelperTextItem>
+                )} */}
+                <HelperTextItem>
+                  <Button
+                    data-testid={`${dataTestId}-editResourceLink`}
+                    variant="link"
+                    isInline
+                    onClick={() => setShowK8sField(true)}
+                  >
+                    Edit resource name
+                  </Button>{' '}
+                  <ResourceNameDefinitionTooltip />
+                </HelperTextItem>
+              </>
+            )}
+          </HelperText>
+        ) : null}
+      </FormGroup>
+      <ResourceNameField
+        allowEdit={showK8sField}
+        dataTestId={dataTestId}
+        // k8sName={k8sName}
+        // onDataChange={onDataChange}
+      />
+      {!hideDescription ? (
+        <FormGroup label={descriptionLabel} fieldId={`${dataTestId}-description`}>
+          <TextArea
+            // aria-readonly={!onDataChange}
+            data-testid={`${dataTestId}-description`}
+            id={`${dataTestId}-description`}
+            name={`${dataTestId}-description`}
+            // value={description}
+            // onChange={(event, value) => onDataChange?.('description', value)}
+            resizeOrientation="vertical"
+          />
+        </FormGroup>
+      ) : null}
+    </>
+  );
+};
+
+export default K8sNameDescriptionField;
diff --git a/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/ResourceNameField.tsx b/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/ResourceNameField.tsx
new file mode 100644
index 00000000..88481826
--- /dev/null
+++ b/clients/ui/frontend/src/concepts/k8s/K8sNameDescriptionField/ResourceNameField.tsx
@@ -0,0 +1,83 @@
+import * as React from 'react';
+import { FormGroup, HelperText, TextInput } from '@patternfly/react-core';
+import ResourceNameDefinitionTooltip from '~/concepts/k8s/ResourceNameDefinitionTootip';
+
+type ResourceNameFieldProps = {
+  allowEdit: boolean;
+  dataTestId: string;
+  //   k8sName: K8sNameDescriptionFieldData['k8sName'];
+  //   onDataChange?: K8sNameDescriptionFieldUpdateFunction;
+};
+
+/** Sub-resource; not for public consumption */
+const ResourceNameField: React.FC<ResourceNameFieldProps> = ({
+  allowEdit,
+  dataTestId,
+  //   k8sName,
+  //   onDataChange,
+}) => {
+  const formGroupProps: React.ComponentProps<typeof FormGroup> = {
+    label: 'Resource name',
+    labelHelp: <ResourceNameDefinitionTooltip />,
+    fieldId: `${dataTestId}-resourceName`,
+  };
+
+  // TODO: Implement this once we have the endpoint.
+
+  //   if (k8sName.state.immutable) {
+  //     return <FormGroup {...formGroupProps}>{k8sName.value}</FormGroup>;
+  //   }
+
+  if (!allowEdit) {
+    return null;
+  }
+
+  // TODO: Implement this once we have the endpoint.
+
+  //   let validated: ValidatedOptions = ValidatedOptions.default;
+  //   if (k8sName.state.invalidLength || k8sName.state.invalidCharacters) {
+  //     validated = ValidatedOptions.error;
+  //   } else if (k8sName.value.length > 0) {
+  //     validated = ValidatedOptions.success;
+  //   }
+
+  //   const usePrefix = k8sName.state.staticPrefix && !!k8sName.state.safePrefix;
+  const textInput = (
+    <TextInput
+      id={`${dataTestId}-resourceName`}
+      data-testid={`${dataTestId}-resourceName`}
+      name={`${dataTestId}-resourceName`}
+      isRequired
+      //   value={
+      //     usePrefix && k8sName.state.safePrefix
+      //       ? k8sName.value.replace(new RegExp(`^${k8sName.state.safePrefix}`), '')
+      //       : k8sName.value
+      //   }
+      //   onChange={(event, value) =>
+      //     onDataChange?.(
+      //       'k8sName',
+      //       usePrefix && k8sName.state.safePrefix ? `${k8sName.state.safePrefix}${value}` : value,
+      //     )
+      //   }
+      //   validated={validated}
+    />
+  );
+  return (
+    <FormGroup {...formGroupProps} isRequired>
+      {/* {usePrefix ? (
+        <InputGroup>
+          <InputGroupText>{k8sName.state.safePrefix}</InputGroupText>
+          <InputGroupItem isFill>{textInput}</InputGroupItem>
+        </InputGroup>
+      ) : ( */}
+      {textInput}
+      {/* )} */}
+      <HelperText>
+        {/* <HelperTextItemMaxLength k8sName={k8sName} />
+        <HelperTextItemValidCharacters k8sName={k8sName} /> */}
+      </HelperText>
+    </FormGroup>
+  );
+};
+
+export default ResourceNameField;
diff --git a/clients/ui/frontend/src/concepts/k8s/ResourceNameDefinitionTootip.tsx b/clients/ui/frontend/src/concepts/k8s/ResourceNameDefinitionTootip.tsx
new file mode 100644
index 00000000..b516d6bd
--- /dev/null
+++ b/clients/ui/frontend/src/concepts/k8s/ResourceNameDefinitionTootip.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import { Stack, StackItem } from '@patternfly/react-core';
+import FieldGroupHelpLabelIcon from '~/shared/components/FieldGroupHelpLabelIcon';
+
+const ResourceNameDefinitionTooltip: React.FC = () => (
+  <FieldGroupHelpLabelIcon
+    content={
+      <Stack hasGutter>
+        <StackItem>
+          The resource name is used to identify your resource, and is generated based on the name
+          you enter.
+        </StackItem>
+        <StackItem>The resource name cannot be edited after creation.</StackItem>
+      </Stack>
+    }
+  />
+);
+
+export default ResourceNameDefinitionTooltip;
diff --git a/clients/ui/frontend/src/shared/components/FieldGroupHelpLabelIcon.tsx b/clients/ui/frontend/src/shared/components/FieldGroupHelpLabelIcon.tsx
new file mode 100644
index 00000000..e9609505
--- /dev/null
+++ b/clients/ui/frontend/src/shared/components/FieldGroupHelpLabelIcon.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import { Popover } from '@patternfly/react-core';
+import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
+import DashboardPopupIconButton from '~/shared/components/dashboard/DashboardPopupIconButton';
+
+type FieldGroupHelpLabelIconProps = {
+  content: React.ComponentProps<typeof Popover>['bodyContent'];
+};
+
+const FieldGroupHelpLabelIcon: React.FC<FieldGroupHelpLabelIconProps> = ({ content }) => (
+  <Popover bodyContent={content}>
+    <DashboardPopupIconButton icon={<OutlinedQuestionCircleIcon />} aria-label="More info" />
+  </Popover>
+);
+
+export default FieldGroupHelpLabelIcon;
diff --git a/devenv/.dockerignore b/devenv/.dockerignore
new file mode 100644
index 00000000..245af879
--- /dev/null
+++ b/devenv/.dockerignore
@@ -0,0 +1,3 @@
+!bin/
+!wheels/
+!ui-static/
\ No newline at end of file
diff --git a/devenv/.gitignore b/devenv/.gitignore
new file mode 100644
index 00000000..b2deb9a4
--- /dev/null
+++ b/devenv/.gitignore
@@ -0,0 +1,5 @@
+bin/
+venv/
+VERSION
+wheels/
+ui-static/
\ No newline at end of file
diff --git a/devenv/Makefile b/devenv/Makefile
new file mode 100644
index 00000000..e6a75a23
--- /dev/null
+++ b/devenv/Makefile
@@ -0,0 +1,42 @@
+detected_OS := $(shell uname -s)
+real_OS := $(detected_OS)
+arch := $(shell uname -m)
+ifeq ($(detected_OS),Darwin)
+	detected_OS := mac
+	real_OS := darwin
+endif
+ifeq ($(detected_OS),Linux)
+	detected_OS := linux
+	real_OS := linux
+endif
+
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+	mkdir -p $(LOCALBIN)
+
+## Tool Binaries
+TILT ?= $(LOCALBIN)/tilt
+POETRY ?= $(LOCALBIN)/poetry
+
+## Tool Versions
+TILT_VERSION := 0.33.22
+
+.PHONY: poetry
+.PHONY: $(POETRY)
+poetry: $(POETRY) ## Download poetry locally if necessary.
+$(POETRY): $(LOCALBIN)
+	test -s $(LOCALBIN)/poetry || curl -sSL https://install.python-poetry.org | POETRY_HOME=$(shell pwd) python3 -
+
+.PHONY: tilt
+.PHONY: $(TILT)
+tilt: $(TILT) ## Download tilt locally if necessary. Architecture is locked at x86_64.
+$(TILT): $(LOCALBIN)
+	test -s $(LOCALBIN)/tilt || curl -fsSL https://github.com/tilt-dev/tilt/releases/download/v$(TILT_VERSION)/tilt.$(TILT_VERSION).$(detected_OS).$(arch).tar.gz | tar -xz -C $(LOCALBIN) tilt
+
+tilt-up: poetry tilt
+	$(LOCALBIN)/tilt up
+
+tilt-down: tilt
+	$(LOCALBIN)/tilt down
diff --git a/devenv/README.md b/devenv/README.md
new file mode 100644
index 00000000..2156ac1e
--- /dev/null
+++ b/devenv/README.md
@@ -0,0 +1,48 @@
+# Kubeflow Notebooks -- Dev Environment
+
+## Requirements
+
+- Go >= 1.23
+- Helm >= 3.16.1
+- Python >= 3.8
+- Node >= 20
+
+## Tilt
+
+This project uses [Tilt](https://tilt.dev/) to manage the development environment. Tilt will watch for changes in the project and automatically rebuild the Docker image and redeploy the application in the current Kubernetes context.
+
+See this example using a kind cluster:
+
+```bash
+kind create cluster
+```
+
+then:
+
+```bash
+make tilt-up
+```
+
+Hit `space` to open the Tilt dashboard in your browser and here you can see the logs and status of every resource deployed.
+
+## Notebooks
+
+The development environment creates automatically a user for JupyterHub. The user is created with the following credentials:
+
+- Username: `mr`
+- Password: `1234`
+
+You can reach the jupter notebook at `http://localhost:8086/user/mr/lab`.
+
+Tilt will watch for file changes in the clients/python folder and build/upload the wheel output to the notebook pod, you can find the wheel under the `~/wheels` folder.
+
+
+## Troubleshooting
+
+### "Build Failed: failed to dial gRPC: unable to upgrade to h2c, received 404"
+
+If you see the following error message when tilt builds the image, try setting `DOCKER_BUILDKIT=0`:
+
+```bash
+DOCKER_BUILDKIT=0 make tilt-up
+```
diff --git a/devenv/Tiltfile b/devenv/Tiltfile
new file mode 100644
index 00000000..4eb9c3c4
--- /dev/null
+++ b/devenv/Tiltfile
@@ -0,0 +1,89 @@
+load("ext://restart_process", "docker_build_with_restart")
+
+load_dynamic("./configs/tiltfiles/setup.tiltfile")
+
+config.define_string_list("services")
+
+parsed_config = config.parse()
+
+for service in parsed_config.get("services", []):
+  load_dynamic("./configs/tiltfiles/%s.tiltfile" % (service))
+
+manifests = kustomize("../manifests/kustomize/overlays/db")
+
+objects = decode_yaml_stream(manifests)
+
+for o in objects:
+    if o["kind"] == "Deployment" and o.get("metadata").get("name") in ["model-registry-deployment"]:
+        o["spec"]["template"]["spec"]["securityContext"] = {"runAsNonRoot": False, "readOnlyRootFilesystem": False}
+        o["spec"]["template"]["spec"]["containers"][0]["imagePullPolicy"] = "Always"
+
+overridden_manifests = encode_yaml_stream(objects)
+
+k8s_yaml(overridden_manifests, allow_duplicates=True)
+
+k8s_resource(
+    new_name="db-reqs",
+    objects=[
+       "metadata-mysql:persistentvolumeclaim",
+       "model-registry-db-parameters:configmap",
+       "model-registry-db-secrets:secret"
+    ],
+    labels="requirements",
+    resource_deps=["kubeflow-namespace"],
+)
+
+k8s_resource(
+    workload="model-registry-db",
+    new_name="db",
+    labels="backend",
+    resource_deps=["kubeflow-namespace"],
+)
+
+k8s_resource(
+    new_name="proxy-reqs",
+    objects=[
+       "model-registry-server:serviceaccount",
+       "model-registry-configmap:configmap"
+    ],
+    labels="requirements",
+    resource_deps=["kubeflow-namespace", "db"],
+)
+
+k8s_resource(
+    workload="model-registry-deployment",
+    new_name="proxy",
+    labels="backend",
+    resource_deps=["db", "kubeflow-namespace"],
+    port_forwards="8080",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+local_resource(
+    "bin",
+    "CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o devenv/bin/model-registry main.go",
+    dir="../",
+    deps=[
+        "../cmd",
+        "../internal",
+        "../pkg",
+        "../go.mod",
+        "../go.sum",
+        "../main.go"
+    ],
+    labels="backend",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+docker_build_with_restart(
+    "kubeflow/model-registry:latest",
+    context="../",
+    dockerfile="../tilt.dockerfile",
+    entrypoint=["/model-registry", "proxy"],
+    only=[
+      "devenv/bin/model-registry",
+    ],
+    live_update=[
+        sync("bin/model-registry", "/model-registry"),
+    ],
+)
diff --git a/devenv/configs/helm-values/jupyterhub.yaml b/devenv/configs/helm-values/jupyterhub.yaml
new file mode 100644
index 00000000..75a33a28
--- /dev/null
+++ b/devenv/configs/helm-values/jupyterhub.yaml
@@ -0,0 +1,31 @@
+hub:
+  services:
+    myservice:
+      api_token: "dummydummy"
+  loadRoles:
+    myadmin:
+      scopes:
+        - "servers"
+        - "admin:users"
+        - "admin:servers"
+      users:
+        - "mr"
+      services:
+        - "myservice"
+      groups:
+        - "admin-group"
+  config:
+    JupyterHub:
+      admin_access: true
+      authenticator_class: dummy
+      DummyAuthenticator:
+        password: "1234"
+    Authenticator:
+      allowed_users:
+        - mr
+    admin_users:
+      - mr
+singleuser:
+  networkPolicy:
+    egressAllowRules:
+      privateIPs: true
diff --git a/devenv/configs/kustomize/ui-rbac/clusterrolebinding.yaml b/devenv/configs/kustomize/ui-rbac/clusterrolebinding.yaml
new file mode 100644
index 00000000..ff226e8a
--- /dev/null
+++ b/devenv/configs/kustomize/ui-rbac/clusterrolebinding.yaml
@@ -0,0 +1,11 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: devenv-cluster-admin
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: cluster-admin
+subjects:
+- kind: User
+  name: test
\ No newline at end of file
diff --git a/devenv/configs/kustomize/ui-rbac/kustomization.yaml b/devenv/configs/kustomize/ui-rbac/kustomization.yaml
new file mode 100644
index 00000000..92892432
--- /dev/null
+++ b/devenv/configs/kustomize/ui-rbac/kustomization.yaml
@@ -0,0 +1,4 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+- clusterrolebinding.yaml
\ No newline at end of file
diff --git a/devenv/configs/tiltfiles/frontend.tiltfile b/devenv/configs/tiltfiles/frontend.tiltfile
new file mode 100644
index 00000000..3b4f8f9b
--- /dev/null
+++ b/devenv/configs/tiltfiles/frontend.tiltfile
@@ -0,0 +1,118 @@
+load('ext://namespace', 'namespace_inject')
+load("ext://restart_process", "docker_build_with_restart")
+
+manifests = kustomize("../../../manifests/kustomize/options/ui/overlays/standalone")
+
+objects = decode_yaml_stream(manifests)
+
+for o in objects:
+    if o["kind"] == "Deployment" and o.get("metadata").get("name") in ["model-registry-ui"]:
+        o["spec"]["template"]["spec"]["securityContext"] = {"runAsNonRoot": False, "readOnlyRootFilesystem": False}
+        o["spec"]["template"]["spec"]["containers"][0]["imagePullPolicy"] = "Always"
+
+overridden_manifests = encode_yaml_stream(objects)
+
+k8s_yaml(namespace_inject(overridden_manifests, "kubeflow"), allow_duplicates=True)
+
+k8s_resource(
+    workload="model-registry-ui",
+    new_name="ui",
+    labels="frontend",
+    resource_deps=["proxy", "kubeflow-namespace"],
+    port_forwards=[port_forward(
+        local_port=8081,
+        container_port=8080,
+    )],
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+rbac_manifests = kustomize("../kustomize/ui-rbac")
+
+k8s_yaml(rbac_manifests, allow_duplicates=True)
+
+k8s_resource(
+    new_name="ui-rbac-reqs",
+    objects=[
+        "devenv-cluster-admin:clusterrolebinding",
+    ],
+    labels="requirements",
+)
+
+k8s_resource(
+    new_name="ui-reqs",
+    objects=[
+       "model-registry-ui:serviceaccount",
+       "model-registry-create-sars:clusterrole",
+       "model-registry-retrieve-clusterrolebindings:clusterrole",
+       "model-registry-ui-namespaces-reader:clusterrole",
+       "model-registry-ui-services-reader:clusterrole",
+       "model-registry-create-sars-binding:clusterrolebinding",
+       "model-registry-retrieve-clusterrolebindings-binding:clusterrolebinding",
+       "model-registry-ui-namespaces-reader-binding:clusterrolebinding",
+       "model-registry-ui-services-reader-binding:clusterrolebinding",
+       "service-access-cluster-binding:clusterrolebinding",
+    ],
+    labels="requirements",
+    resource_deps=["kubeflow-namespace", "ui-rbac-reqs"],
+)
+
+local_resource(
+    "frontend-static-reqs",
+    "npm install",
+    dir="../../../clients/ui/frontend",
+    deps=[
+        "../../../clients/ui/frontend/package.json",
+    ],
+    labels="requirements",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+local_resource(
+    "frontend-build",
+    "npm run build:prod",
+    dir="../../../clients/ui/frontend",
+    deps=[
+        "../../../clients/ui/frontend/src",
+        "../../../clients/ui/frontend/config",
+        "../../../clients/ui/frontend/package.json",
+        "../../../clients/ui/frontend/package-lock.json",
+    ],
+    resource_deps=["frontend-static-reqs", "kubeflow-namespace"],
+    env={
+        "DIST_DIR": "../../../devenv/ui-static",
+        "DEPLOYMENT_MODE": "standalone",
+        "MOCK_AUTH": "true",
+        "USERNAME": "test",
+    },
+    labels="frontend",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+local_resource(
+    "bff-bin",
+    "CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../../devenv/bin/bff ./cmd",
+    dir="../../../clients/ui/bff",
+    deps=[
+        "../../../clients/ui/bff/cmd",
+        "../../../clients/ui/bff/internal",
+        "../../../clients/ui/bff/go.mod",
+        "../../../clients/ui/bff/go.sum",
+    ],
+    labels="frontend",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+docker_build_with_restart(
+    "docker.io/kubeflow/model-registry-ui:latest",
+    context="../../../",
+    dockerfile="../../../tilt-ui.dockerfile",
+    entrypoint=["/bff"],
+    only=[
+      "devenv/bin/bff",
+      "devenv/ui-static",
+    ],
+    live_update=[
+        sync("../../bin/bff", "/bff"),
+        sync("../../ui-static", "/static"),
+    ],
+)
diff --git a/devenv/configs/tiltfiles/jupyterhub.tiltfile b/devenv/configs/tiltfiles/jupyterhub.tiltfile
new file mode 100644
index 00000000..821d236a
--- /dev/null
+++ b/devenv/configs/tiltfiles/jupyterhub.tiltfile
@@ -0,0 +1,58 @@
+load("ext://helm_resource", "helm_repo", "helm_resource")
+
+# Set default trigger mode to manual
+trigger_mode(TRIGGER_MODE_MANUAL)
+
+helm_repo(
+  name="jupyterhub",
+  url="https://hub.jupyter.org/helm-chart/",
+  resource_name="jupyterhub-repo",
+  labels="jupyterhub"
+)
+
+helm_resource(
+  name="jupyterhub",
+  chart="jupyterhub/jupyterhub",
+  release_name="jupyterhub",
+  namespace="kubeflow",
+  flags=["--values", "../helm-values/jupyterhub.yaml", "--version=4.1.0"],
+  resource_deps=["kubeflow-namespace", "jupyterhub-repo"],
+  deps=["../helm-values/jupyterhub.yaml"],
+  labels="jupyterhub"
+)
+
+k8s_resource(
+    "jupyterhub",
+    extra_pod_selectors={
+        "app": "jupyterhub",
+        "component": "proxy"
+    },
+    discovery_strategy="selectors-only",
+    port_forwards="8086:8000",
+    trigger_mode=TRIGGER_MODE_AUTO,
+)
+
+local_resource(
+  "hub-ready",
+  resource_deps=["jupyterhub"],
+  serve_cmd="sleep infinity",
+  readiness_probe=probe(exec=exec_action(command=["curl", "-H", "Authorization: token dummydummy", "-f", "http://localhost:8086/hub/api/users"]), initial_delay_secs=5, timeout_secs=60),
+  labels="jupyterhub"
+)
+
+local_resource(
+    "mr-notebook-create",
+    resource_deps=["hub-ready"],
+    cmd="curl --silent -X 'POST' -H 'Authorization: token dummydummy' 'localhost:8086/hub/api/users/mr/server'",
+    labels="notebooks"
+)
+
+local_resource(
+    "mr-notebook",
+    resource_deps=["mr-notebook-create"],
+    serve_cmd="sleep infinity",
+    labels="notebooks",
+    readiness_probe=probe(exec=exec_action(command=["/bin/bash", "-c", "kubectl get pod -n kubeflow -l app=jupyterhub,component=singleuser-server,hub.jupyter.org/username=mr -o jsonpath={.items[0].status.phase} | grep -q '^Running$' && exit 0 || exit 1"]), initial_delay_secs=5, timeout_secs=60),
+    links=[link("http://localhost:8086/user/mr/lab", "Open JupyterLab")]
+)
+
diff --git a/devenv/configs/tiltfiles/pythonclient.tiltfile b/devenv/configs/tiltfiles/pythonclient.tiltfile
new file mode 100644
index 00000000..9ea3ded3
--- /dev/null
+++ b/devenv/configs/tiltfiles/pythonclient.tiltfile
@@ -0,0 +1,35 @@
+local_resource(
+    "python-bin-reqs",
+    "../../bin/poetry install -C ../../../clients/python",
+    deps=[
+        "../../../clients/python/pyproject.toml",
+    ],
+    labels="requirements",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+local_resource(
+    "python-wheel-build",
+    "../../devenv/bin/poetry build -o ../../devenv/wheels",
+    dir="../../../clients/python",
+    deps=[
+        "../../../clients/python/src",
+        "../../../clients/python/pyproject.toml",
+        "../../../clients/python/poetry.lock",
+    ],
+    resource_deps=["python-bin-reqs"],
+    labels="python-client",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
+
+local_resource(
+    "copy-wheel-into-notebook",
+    "kubectl cp wheels kubeflow/jupyter-mr:/home/jovyan/ -c notebook",
+    dir="../../",
+    deps=[
+        "../../wheels",
+    ],
+    resource_deps=["python-wheel-build", "mr-notebook"],
+    labels="python-client",
+    trigger_mode=TRIGGER_MODE_AUTO
+)
diff --git a/devenv/configs/tiltfiles/setup.tiltfile b/devenv/configs/tiltfiles/setup.tiltfile
new file mode 100644
index 00000000..8e56abe5
--- /dev/null
+++ b/devenv/configs/tiltfiles/setup.tiltfile
@@ -0,0 +1,14 @@
+load("ext://namespace", "namespace_create")
+
+# Disable analytics
+analytics_settings(False)
+
+update_settings(k8s_upsert_timeout_secs = 120)
+
+namespace_create("kubeflow")
+
+k8s_resource(
+    new_name="kubeflow-namespace",
+    objects=["kubeflow:namespace"],
+    labels="common",
+)
diff --git a/devenv/tilt_config.json b/devenv/tilt_config.json
new file mode 100644
index 00000000..437641d5
--- /dev/null
+++ b/devenv/tilt_config.json
@@ -0,0 +1,7 @@
+{
+    "services": [
+        "jupyterhub",
+        "pythonclient",
+        "frontend"
+    ]
+}
diff --git a/docs/logical_model.md b/docs/logical_model.md
index 4e7ce13e..3262e273 100644
--- a/docs/logical_model.md
+++ b/docs/logical_model.md
@@ -346,13 +346,18 @@ A ML model from S3 bucket using a ServiceAccount:
 
 ```python
 model = registry.register_model(
-    "mnist-s3",  
-    "s3://kserve-examples/mnist",  
+    "mnist-s3",
+    "s3://kserve-examples/mnist",
     version="v1",
     description="used for demo purposes",
     model_format_name="onnx",
     model_format_version="1",
     service_account_name="sa",
+    model_source_kind="pipelines",
+    model_source_class="pipelinerun",
+    model_source_group="my-ns",
+    model_source_id="run-id1",
+    model_source_name="my-ns/run-id1",
 )
 ```
 
diff --git a/internal/converter/generated/mlmd_openapi_converter.gen.go b/internal/converter/generated/mlmd_openapi_converter.gen.go
index 239e700e..b58c32bf 100644
--- a/internal/converter/generated/mlmd_openapi_converter.gen.go
+++ b/internal/converter/generated/mlmd_openapi_converter.gen.go
@@ -104,6 +104,11 @@ func (c *MLMDToOpenAPIConverterImpl) ConvertModelArtifact(source *proto.Artifact
 		openapiModelArtifact.StoragePath = converter.MapModelArtifactStoragePath((*source).Properties)
 		openapiModelArtifact.ModelFormatVersion = converter.MapModelArtifactFormatVersion((*source).Properties)
 		openapiModelArtifact.ServiceAccountName = converter.MapModelArtifactServiceAccountName((*source).Properties)
+		openapiModelArtifact.ModelSourceKind = converter.MapModelArtifactModelSourceKind((*source).Properties)
+		openapiModelArtifact.ModelSourceClass = converter.MapModelArtifactModelSourceClass((*source).Properties)
+		openapiModelArtifact.ModelSourceGroup = converter.MapModelArtifactModelSourceGroup((*source).Properties)
+		openapiModelArtifact.ModelSourceId = converter.MapModelArtifactModelSourceId((*source).Properties)
+		openapiModelArtifact.ModelSourceName = converter.MapModelArtifactModelSourceName((*source).Properties)
 		pOpenapiModelArtifact = &openapiModelArtifact
 	}
 	return pOpenapiModelArtifact, nil
@@ -201,7 +206,7 @@ func (c *MLMDToOpenAPIConverterImpl) ConvertServingEnvironment(source *proto.Con
 			xstring := *(*source).ExternalId
 			openapiServingEnvironment.ExternalId = &xstring
 		}
-		openapiServingEnvironment.Name = converter.MapNameFromOwned((*source).Name)
+		openapiServingEnvironment.Name = converter.MapName((*source).Name)
 		openapiServingEnvironment.Id = converter.Int64ToString((*source).Id)
 		openapiServingEnvironment.CreateTimeSinceEpoch = converter.Int64ToString((*source).CreateTimeSinceEpoch)
 		openapiServingEnvironment.LastUpdateTimeSinceEpoch = converter.Int64ToString((*source).LastUpdateTimeSinceEpoch)
diff --git a/internal/converter/generated/openapi_converter.gen.go b/internal/converter/generated/openapi_converter.gen.go
index 5ca99cd6..ed94216c 100644
--- a/internal/converter/generated/openapi_converter.gen.go
+++ b/internal/converter/generated/openapi_converter.gen.go
@@ -270,6 +270,26 @@ func (c *OpenAPIConverterImpl) ConvertModelArtifactCreate(source *openapi.ModelA
 			xstring9 := *(*source).ServiceAccountName
 			openapiModelArtifact.ServiceAccountName = &xstring9
 		}
+		if (*source).ModelSourceKind != nil {
+			xstring10 := *(*source).ModelSourceKind
+			openapiModelArtifact.ModelSourceKind = &xstring10
+		}
+		if (*source).ModelSourceClass != nil {
+			xstring11 := *(*source).ModelSourceClass
+			openapiModelArtifact.ModelSourceClass = &xstring11
+		}
+		if (*source).ModelSourceGroup != nil {
+			xstring12 := *(*source).ModelSourceGroup
+			openapiModelArtifact.ModelSourceGroup = &xstring12
+		}
+		if (*source).ModelSourceId != nil {
+			xstring13 := *(*source).ModelSourceId
+			openapiModelArtifact.ModelSourceId = &xstring13
+		}
+		if (*source).ModelSourceName != nil {
+			xstring14 := *(*source).ModelSourceName
+			openapiModelArtifact.ModelSourceName = &xstring14
+		}
 		pOpenapiModelArtifact = &openapiModelArtifact
 	}
 	return pOpenapiModelArtifact, nil
@@ -327,6 +347,26 @@ func (c *OpenAPIConverterImpl) ConvertModelArtifactUpdate(source *openapi.ModelA
 			xstring8 := *(*source).ServiceAccountName
 			openapiModelArtifact.ServiceAccountName = &xstring8
 		}
+		if (*source).ModelSourceKind != nil {
+			xstring9 := *(*source).ModelSourceKind
+			openapiModelArtifact.ModelSourceKind = &xstring9
+		}
+		if (*source).ModelSourceClass != nil {
+			xstring10 := *(*source).ModelSourceClass
+			openapiModelArtifact.ModelSourceClass = &xstring10
+		}
+		if (*source).ModelSourceGroup != nil {
+			xstring11 := *(*source).ModelSourceGroup
+			openapiModelArtifact.ModelSourceGroup = &xstring11
+		}
+		if (*source).ModelSourceId != nil {
+			xstring12 := *(*source).ModelSourceId
+			openapiModelArtifact.ModelSourceId = &xstring12
+		}
+		if (*source).ModelSourceName != nil {
+			xstring13 := *(*source).ModelSourceName
+			openapiModelArtifact.ModelSourceName = &xstring13
+		}
 		pOpenapiModelArtifact = &openapiModelArtifact
 	}
 	return pOpenapiModelArtifact, nil
@@ -575,10 +615,7 @@ func (c *OpenAPIConverterImpl) ConvertServingEnvironmentCreate(source *openapi.S
 			xstring2 := *(*source).ExternalId
 			openapiServingEnvironment.ExternalId = &xstring2
 		}
-		if (*source).Name != nil {
-			xstring3 := *(*source).Name
-			openapiServingEnvironment.Name = &xstring3
-		}
+		openapiServingEnvironment.Name = (*source).Name
 		pOpenapiServingEnvironment = &openapiServingEnvironment
 	}
 	return pOpenapiServingEnvironment, nil
@@ -717,11 +754,10 @@ func (c *OpenAPIConverterImpl) OverrideNotEditableForServingEnvironment(source c
 	openapiServingEnvironment := converter.InitWithUpdate(source)
 	var pString *string
 	if source.Existing != nil {
-		pString = source.Existing.Name
+		pString = &source.Existing.Name
 	}
 	if pString != nil {
-		xstring := *pString
-		openapiServingEnvironment.Name = &xstring
+		openapiServingEnvironment.Name = *pString
 	}
 	return openapiServingEnvironment, nil
 }
diff --git a/internal/converter/generated/openapi_mlmd_converter.gen.go b/internal/converter/generated/openapi_mlmd_converter.gen.go
index 927d7e4c..55bc8f70 100644
--- a/internal/converter/generated/openapi_mlmd_converter.gen.go
+++ b/internal/converter/generated/openapi_mlmd_converter.gen.go
@@ -335,7 +335,7 @@ func (c *OpenAPIToMLMDConverterImpl) ConvertServingEnvironment(source *converter
 		protoContext.Id = pInt64
 		var pString2 *string
 		if (*source).Model != nil {
-			pString2 = (*source).Model.Name
+			pString2 = &(*source).Model.Name
 		}
 		if pString2 != nil {
 			xstring := *pString2
diff --git a/internal/converter/generated/openapi_reconciler.gen.go b/internal/converter/generated/openapi_reconciler.gen.go
index fcfccce9..6ef3311e 100644
--- a/internal/converter/generated/openapi_reconciler.gen.go
+++ b/internal/converter/generated/openapi_reconciler.gen.go
@@ -216,6 +216,46 @@ func (c *OpenAPIReconcilerImpl) UpdateExistingModelArtifact(source converter.Ope
 		xstring8 := *pString8
 		openapiModelArtifact.ServiceAccountName = &xstring8
 	}
+	var pString9 *string
+	if source.Update != nil {
+		pString9 = source.Update.ModelSourceKind
+	}
+	if pString9 != nil {
+		xstring9 := *pString9
+		openapiModelArtifact.ModelSourceKind = &xstring9
+	}
+	var pString10 *string
+	if source.Update != nil {
+		pString10 = source.Update.ModelSourceClass
+	}
+	if pString10 != nil {
+		xstring10 := *pString10
+		openapiModelArtifact.ModelSourceClass = &xstring10
+	}
+	var pString11 *string
+	if source.Update != nil {
+		pString11 = source.Update.ModelSourceGroup
+	}
+	if pString11 != nil {
+		xstring11 := *pString11
+		openapiModelArtifact.ModelSourceGroup = &xstring11
+	}
+	var pString12 *string
+	if source.Update != nil {
+		pString12 = source.Update.ModelSourceId
+	}
+	if pString12 != nil {
+		xstring12 := *pString12
+		openapiModelArtifact.ModelSourceId = &xstring12
+	}
+	var pString13 *string
+	if source.Update != nil {
+		pString13 = source.Update.ModelSourceName
+	}
+	if pString13 != nil {
+		xstring13 := *pString13
+		openapiModelArtifact.ModelSourceName = &xstring13
+	}
 	return openapiModelArtifact, nil
 }
 func (c *OpenAPIReconcilerImpl) UpdateExistingModelVersion(source converter.OpenapiUpdateWrapper[openapi.ModelVersion]) (openapi.ModelVersion, error) {
diff --git a/internal/converter/mlmd_openapi_converter.go b/internal/converter/mlmd_openapi_converter.go
index 21b2257b..14969c3b 100644
--- a/internal/converter/mlmd_openapi_converter.go
+++ b/internal/converter/mlmd_openapi_converter.go
@@ -35,6 +35,11 @@ type MLMDToOpenAPIConverter interface {
 	// goverter:map Properties StorageKey | MapModelArtifactStorageKey
 	// goverter:map Properties StoragePath | MapModelArtifactStoragePath
 	// goverter:map Properties ServiceAccountName | MapModelArtifactServiceAccountName
+	// goverter:map Properties ModelSourceKind | MapModelArtifactModelSourceKind
+	// goverter:map Properties ModelSourceClass | MapModelArtifactModelSourceClass
+	// goverter:map Properties ModelSourceGroup | MapModelArtifactModelSourceGroup
+	// goverter:map Properties ModelSourceId | MapModelArtifactModelSourceId
+	// goverter:map Properties ModelSourceName | MapModelArtifactModelSourceName
 	ConvertModelArtifact(source *proto.Artifact) (*openapi.ModelArtifact, error)
 
 	// goverter:map Name | MapNameFromOwned
@@ -43,7 +48,7 @@ type MLMDToOpenAPIConverter interface {
 	// goverter:map Properties Description | MapDescription
 	ConvertDocArtifact(source *proto.Artifact) (*openapi.DocArtifact, error)
 
-	// goverter:map Name | MapNameFromOwned
+	// goverter:map Name | MapName
 	// goverter:map Properties Description | MapDescription
 	ConvertServingEnvironment(source *proto.Context) (*openapi.ServingEnvironment, error)
 
diff --git a/internal/converter/mlmd_openapi_converter_util.go b/internal/converter/mlmd_openapi_converter_util.go
index 851d0af6..f47bf8fa 100644
--- a/internal/converter/mlmd_openapi_converter_util.go
+++ b/internal/converter/mlmd_openapi_converter_util.go
@@ -247,6 +247,26 @@ func MapModelArtifactServiceAccountName(properties map[string]*proto.Value) *str
 	return MapStringProperty(properties, "service_account_name")
 }
 
+func MapModelArtifactModelSourceKind(properties map[string]*proto.Value) *string {
+	return MapStringProperty(properties, "model_source_kind")
+}
+
+func MapModelArtifactModelSourceClass(properties map[string]*proto.Value) *string {
+	return MapStringProperty(properties, "model_source_class")
+}
+
+func MapModelArtifactModelSourceGroup(properties map[string]*proto.Value) *string {
+	return MapStringProperty(properties, "model_source_group")
+}
+
+func MapModelArtifactModelSourceId(properties map[string]*proto.Value) *string {
+	return MapStringProperty(properties, "model_source_id")
+}
+
+func MapModelArtifactModelSourceName(properties map[string]*proto.Value) *string {
+	return MapStringProperty(properties, "model_source_name")
+}
+
 func MapPropertyModelVersionId(properties map[string]*proto.Value) *string {
 	return MapIntProperty(properties, "model_version_id")
 }
diff --git a/internal/converter/openapi_converter.go b/internal/converter/openapi_converter.go
index df704ddd..66f3ea1e 100644
--- a/internal/converter/openapi_converter.go
+++ b/internal/converter/openapi_converter.go
@@ -91,7 +91,7 @@ type OpenAPIConverter interface {
 	// Ignore all fields that ARE editable
 	// goverter:default InitWithUpdate
 	// goverter:autoMap Existing
-	// goverter:ignore Id CreateTimeSinceEpoch LastUpdateTimeSinceEpoch Description ExternalId CustomProperties Uri State ServiceAccountName ModelFormatName ModelFormatVersion StorageKey StoragePath
+	// goverter:ignore Id CreateTimeSinceEpoch LastUpdateTimeSinceEpoch Description ExternalId CustomProperties Uri State ServiceAccountName ModelFormatName ModelFormatVersion StorageKey StoragePath ModelSourceKind ModelSourceClass ModelSourceGroup ModelSourceId ModelSourceName
 	OverrideNotEditableForModelArtifact(source OpenapiUpdateWrapper[openapi.ModelArtifact]) (openapi.ModelArtifact, error)
 
 	// Ignore all fields that ARE editable
diff --git a/internal/converter/openapi_mlmd_converter_util.go b/internal/converter/openapi_mlmd_converter_util.go
index 71c511f5..493bdb97 100644
--- a/internal/converter/openapi_mlmd_converter_util.go
+++ b/internal/converter/openapi_mlmd_converter_util.go
@@ -309,6 +309,42 @@ func MapModelArtifactProperties(source *openapi.ModelArtifact) (map[string]*prot
 				},
 			}
 		}
+		if source.ModelSourceKind != nil {
+			props["model_source_kind"] = &proto.Value{
+				Value: &proto.Value_StringValue{
+					StringValue: *source.ModelSourceKind,
+				},
+			}
+		}
+		if source.ModelSourceClass != nil {
+			props["model_source_class"] = &proto.Value{
+				Value: &proto.Value_StringValue{
+					StringValue: *source.ModelSourceClass,
+				},
+			}
+		}
+		if source.ModelSourceGroup != nil {
+			props["model_source_group"] = &proto.Value{
+				Value: &proto.Value_StringValue{
+					StringValue: *source.ModelSourceGroup,
+				},
+			}
+		}
+		if source.ModelSourceId != nil {
+			props["model_source_id"] = &proto.Value{
+				Value: &proto.Value_StringValue{
+					StringValue: *source.ModelSourceId,
+				},
+			}
+		}
+		if source.ModelSourceName != nil {
+			props["model_source_name"] = &proto.Value{
+				Value: &proto.Value_StringValue{
+					StringValue: *source.ModelSourceName,
+				},
+			}
+		}
+
 	}
 	return props, nil
 }
diff --git a/internal/mlmdtypes/mlmdtypes.go b/internal/mlmdtypes/mlmdtypes.go
index 7a7f6eff..a7843323 100644
--- a/internal/mlmdtypes/mlmdtypes.go
+++ b/internal/mlmdtypes/mlmdtypes.go
@@ -85,6 +85,11 @@ func CreateMLMDTypes(cc grpc.ClientConnInterface, nameConfig MLMDTypeNamesConfig
 				"storage_key":          proto.PropertyType_STRING,
 				"storage_path":         proto.PropertyType_STRING,
 				"service_account_name": proto.PropertyType_STRING,
+				"model_source_kind":    proto.PropertyType_STRING,
+				"model_source_class":   proto.PropertyType_STRING,
+				"model_source_group":   proto.PropertyType_STRING,
+				"model_source_id":      proto.PropertyType_STRING,
+				"model_source_name":    proto.PropertyType_STRING,
 			},
 		},
 	}
diff --git a/internal/server/openapi/type_asserts.go b/internal/server/openapi/type_asserts.go
index 6a0d7d56..a3dc15d5 100644
--- a/internal/server/openapi/type_asserts.go
+++ b/internal/server/openapi/type_asserts.go
@@ -815,6 +815,15 @@ func AssertServingEnvironmentCreateConstraints(obj model.ServingEnvironmentCreat
 
 // AssertServingEnvironmentCreateRequired checks if the required fields are not zero-ed
 func AssertServingEnvironmentCreateRequired(obj model.ServingEnvironmentCreate) error {
+	elements := map[string]interface{}{
+		"name": obj.Name,
+	}
+	for name, el := range elements {
+		if isZero := IsZeroValue(el); isZero {
+			return &RequiredError{Field: name}
+		}
+	}
+
 	return nil
 }
 
@@ -847,6 +856,15 @@ func AssertServingEnvironmentListRequired(obj model.ServingEnvironmentList) erro
 
 // AssertServingEnvironmentRequired checks if the required fields are not zero-ed
 func AssertServingEnvironmentRequired(obj model.ServingEnvironment) error {
+	elements := map[string]interface{}{
+		"name": obj.Name,
+	}
+	for name, el := range elements {
+		if isZero := IsZeroValue(el); isZero {
+			return &RequiredError{Field: name}
+		}
+	}
+
 	return nil
 }
 
diff --git a/manifests/kustomize/base/model-registry-deployment.yaml b/manifests/kustomize/base/model-registry-deployment.yaml
index be6503d5..8c402cf8 100644
--- a/manifests/kustomize/base/model-registry-deployment.yaml
+++ b/manifests/kustomize/base/model-registry-deployment.yaml
@@ -11,9 +11,8 @@ spec:
       component: model-registry-server
   template:
     metadata:
-      annotations:
-        sidecar.istio.io/inject: "true"
       labels:
+        sidecar.istio.io/inject: "true"
         component: model-registry-server
     spec:
       securityContext:
diff --git a/manifests/kustomize/overlays/db/model-registry-db-deployment.yaml b/manifests/kustomize/overlays/db/model-registry-db-deployment.yaml
index 3d0affb6..f19ce5f8 100644
--- a/manifests/kustomize/overlays/db/model-registry-db-deployment.yaml
+++ b/manifests/kustomize/overlays/db/model-registry-db-deployment.yaml
@@ -16,7 +16,6 @@ spec:
       name: db
       labels:
         component: db
-      annotations:
         sidecar.istio.io/inject: "false"
     spec:
       securityContext:
diff --git a/manifests/kustomize/overlays/postgres/model-registry-db-deployment.yaml b/manifests/kustomize/overlays/postgres/model-registry-db-deployment.yaml
index 5851fc6b..7128631b 100644
--- a/manifests/kustomize/overlays/postgres/model-registry-db-deployment.yaml
+++ b/manifests/kustomize/overlays/postgres/model-registry-db-deployment.yaml
@@ -16,7 +16,6 @@ spec:
       name: db
       labels:
         component: db
-      annotations:
         sidecar.istio.io/inject: "false"
     spec:
       securityContext:
diff --git a/pkg/inferenceservice-controller/controller.go b/pkg/inferenceservice-controller/controller.go
index 8d3cd2c4..60986a92 100644
--- a/pkg/inferenceservice-controller/controller.go
+++ b/pkg/inferenceservice-controller/controller.go
@@ -353,7 +353,7 @@ func (r *InferenceServiceController) getOrCreateServingEnvironment(ctx context.C
 		log.Info("ServingEnvironment not found, creating it..")
 
 		servingEnvironment, _, err = mr.ModelRegistryServiceAPI.CreateServingEnvironment(ctx).ServingEnvironmentCreate(openapi.ServingEnvironmentCreate{
-			Name: &namespace,
+			Name: namespace,
 		}).Execute()
 		if err != nil {
 			return nil, fmt.Errorf("unable to create ServingEnvironment: %w", err)
diff --git a/pkg/openapi/model_model_artifact.go b/pkg/openapi/model_model_artifact.go
index 18abf6bd..614c0c7c 100644
--- a/pkg/openapi/model_model_artifact.go
+++ b/pkg/openapi/model_model_artifact.go
@@ -47,6 +47,16 @@ type ModelArtifact struct {
 	ModelFormatVersion *string `json:"modelFormatVersion,omitempty"`
 	// Name of the service account with storage secret.
 	ServiceAccountName *string `json:"serviceAccountName,omitempty"`
+	// A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.
+	ModelSourceKind *string `json:"modelSourceKind,omitempty"`
+	// A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.
+	ModelSourceClass *string `json:"modelSourceClass,omitempty"`
+	// Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.
+	ModelSourceGroup *string `json:"modelSourceGroup,omitempty"`
+	// A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.
+	ModelSourceId *string `json:"modelSourceId,omitempty"`
+	// A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.
+	ModelSourceName *string `json:"modelSourceName,omitempty"`
 }
 
 // NewModelArtifact instantiates a new ModelArtifact object
@@ -554,6 +564,166 @@ func (o *ModelArtifact) SetServiceAccountName(v string) {
 	o.ServiceAccountName = &v
 }
 
+// GetModelSourceKind returns the ModelSourceKind field value if set, zero value otherwise.
+func (o *ModelArtifact) GetModelSourceKind() string {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceKind
+}
+
+// GetModelSourceKindOk returns a tuple with the ModelSourceKind field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifact) GetModelSourceKindOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		return nil, false
+	}
+	return o.ModelSourceKind, true
+}
+
+// HasModelSourceKind returns a boolean if a field has been set.
+func (o *ModelArtifact) HasModelSourceKind() bool {
+	if o != nil && !IsNil(o.ModelSourceKind) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceKind gets a reference to the given string and assigns it to the ModelSourceKind field.
+func (o *ModelArtifact) SetModelSourceKind(v string) {
+	o.ModelSourceKind = &v
+}
+
+// GetModelSourceClass returns the ModelSourceClass field value if set, zero value otherwise.
+func (o *ModelArtifact) GetModelSourceClass() string {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceClass
+}
+
+// GetModelSourceClassOk returns a tuple with the ModelSourceClass field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifact) GetModelSourceClassOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		return nil, false
+	}
+	return o.ModelSourceClass, true
+}
+
+// HasModelSourceClass returns a boolean if a field has been set.
+func (o *ModelArtifact) HasModelSourceClass() bool {
+	if o != nil && !IsNil(o.ModelSourceClass) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceClass gets a reference to the given string and assigns it to the ModelSourceClass field.
+func (o *ModelArtifact) SetModelSourceClass(v string) {
+	o.ModelSourceClass = &v
+}
+
+// GetModelSourceGroup returns the ModelSourceGroup field value if set, zero value otherwise.
+func (o *ModelArtifact) GetModelSourceGroup() string {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceGroup
+}
+
+// GetModelSourceGroupOk returns a tuple with the ModelSourceGroup field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifact) GetModelSourceGroupOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		return nil, false
+	}
+	return o.ModelSourceGroup, true
+}
+
+// HasModelSourceGroup returns a boolean if a field has been set.
+func (o *ModelArtifact) HasModelSourceGroup() bool {
+	if o != nil && !IsNil(o.ModelSourceGroup) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceGroup gets a reference to the given string and assigns it to the ModelSourceGroup field.
+func (o *ModelArtifact) SetModelSourceGroup(v string) {
+	o.ModelSourceGroup = &v
+}
+
+// GetModelSourceId returns the ModelSourceId field value if set, zero value otherwise.
+func (o *ModelArtifact) GetModelSourceId() string {
+	if o == nil || IsNil(o.ModelSourceId) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceId
+}
+
+// GetModelSourceIdOk returns a tuple with the ModelSourceId field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifact) GetModelSourceIdOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceId) {
+		return nil, false
+	}
+	return o.ModelSourceId, true
+}
+
+// HasModelSourceId returns a boolean if a field has been set.
+func (o *ModelArtifact) HasModelSourceId() bool {
+	if o != nil && !IsNil(o.ModelSourceId) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceId gets a reference to the given string and assigns it to the ModelSourceId field.
+func (o *ModelArtifact) SetModelSourceId(v string) {
+	o.ModelSourceId = &v
+}
+
+// GetModelSourceName returns the ModelSourceName field value if set, zero value otherwise.
+func (o *ModelArtifact) GetModelSourceName() string {
+	if o == nil || IsNil(o.ModelSourceName) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceName
+}
+
+// GetModelSourceNameOk returns a tuple with the ModelSourceName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifact) GetModelSourceNameOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceName) {
+		return nil, false
+	}
+	return o.ModelSourceName, true
+}
+
+// HasModelSourceName returns a boolean if a field has been set.
+func (o *ModelArtifact) HasModelSourceName() bool {
+	if o != nil && !IsNil(o.ModelSourceName) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceName gets a reference to the given string and assigns it to the ModelSourceName field.
+func (o *ModelArtifact) SetModelSourceName(v string) {
+	o.ModelSourceName = &v
+}
+
 func (o ModelArtifact) MarshalJSON() ([]byte, error) {
 	toSerialize, err := o.ToMap()
 	if err != nil {
@@ -609,6 +779,21 @@ func (o ModelArtifact) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.ServiceAccountName) {
 		toSerialize["serviceAccountName"] = o.ServiceAccountName
 	}
+	if !IsNil(o.ModelSourceKind) {
+		toSerialize["modelSourceKind"] = o.ModelSourceKind
+	}
+	if !IsNil(o.ModelSourceClass) {
+		toSerialize["modelSourceClass"] = o.ModelSourceClass
+	}
+	if !IsNil(o.ModelSourceGroup) {
+		toSerialize["modelSourceGroup"] = o.ModelSourceGroup
+	}
+	if !IsNil(o.ModelSourceId) {
+		toSerialize["modelSourceId"] = o.ModelSourceId
+	}
+	if !IsNil(o.ModelSourceName) {
+		toSerialize["modelSourceName"] = o.ModelSourceName
+	}
 	return toSerialize, nil
 }
 
diff --git a/pkg/openapi/model_model_artifact_create.go b/pkg/openapi/model_model_artifact_create.go
index 06cdbdc2..821786fd 100644
--- a/pkg/openapi/model_model_artifact_create.go
+++ b/pkg/openapi/model_model_artifact_create.go
@@ -41,6 +41,16 @@ type ModelArtifactCreate struct {
 	ModelFormatVersion *string `json:"modelFormatVersion,omitempty"`
 	// Name of the service account with storage secret.
 	ServiceAccountName *string `json:"serviceAccountName,omitempty"`
+	// A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.
+	ModelSourceKind *string `json:"modelSourceKind,omitempty"`
+	// A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.
+	ModelSourceClass *string `json:"modelSourceClass,omitempty"`
+	// Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.
+	ModelSourceGroup *string `json:"modelSourceGroup,omitempty"`
+	// A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.
+	ModelSourceId *string `json:"modelSourceId,omitempty"`
+	// A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.
+	ModelSourceName *string `json:"modelSourceName,omitempty"`
 }
 
 // NewModelArtifactCreate instantiates a new ModelArtifactCreate object
@@ -452,6 +462,166 @@ func (o *ModelArtifactCreate) SetServiceAccountName(v string) {
 	o.ServiceAccountName = &v
 }
 
+// GetModelSourceKind returns the ModelSourceKind field value if set, zero value otherwise.
+func (o *ModelArtifactCreate) GetModelSourceKind() string {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceKind
+}
+
+// GetModelSourceKindOk returns a tuple with the ModelSourceKind field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactCreate) GetModelSourceKindOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		return nil, false
+	}
+	return o.ModelSourceKind, true
+}
+
+// HasModelSourceKind returns a boolean if a field has been set.
+func (o *ModelArtifactCreate) HasModelSourceKind() bool {
+	if o != nil && !IsNil(o.ModelSourceKind) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceKind gets a reference to the given string and assigns it to the ModelSourceKind field.
+func (o *ModelArtifactCreate) SetModelSourceKind(v string) {
+	o.ModelSourceKind = &v
+}
+
+// GetModelSourceClass returns the ModelSourceClass field value if set, zero value otherwise.
+func (o *ModelArtifactCreate) GetModelSourceClass() string {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceClass
+}
+
+// GetModelSourceClassOk returns a tuple with the ModelSourceClass field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactCreate) GetModelSourceClassOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		return nil, false
+	}
+	return o.ModelSourceClass, true
+}
+
+// HasModelSourceClass returns a boolean if a field has been set.
+func (o *ModelArtifactCreate) HasModelSourceClass() bool {
+	if o != nil && !IsNil(o.ModelSourceClass) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceClass gets a reference to the given string and assigns it to the ModelSourceClass field.
+func (o *ModelArtifactCreate) SetModelSourceClass(v string) {
+	o.ModelSourceClass = &v
+}
+
+// GetModelSourceGroup returns the ModelSourceGroup field value if set, zero value otherwise.
+func (o *ModelArtifactCreate) GetModelSourceGroup() string {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceGroup
+}
+
+// GetModelSourceGroupOk returns a tuple with the ModelSourceGroup field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactCreate) GetModelSourceGroupOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		return nil, false
+	}
+	return o.ModelSourceGroup, true
+}
+
+// HasModelSourceGroup returns a boolean if a field has been set.
+func (o *ModelArtifactCreate) HasModelSourceGroup() bool {
+	if o != nil && !IsNil(o.ModelSourceGroup) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceGroup gets a reference to the given string and assigns it to the ModelSourceGroup field.
+func (o *ModelArtifactCreate) SetModelSourceGroup(v string) {
+	o.ModelSourceGroup = &v
+}
+
+// GetModelSourceId returns the ModelSourceId field value if set, zero value otherwise.
+func (o *ModelArtifactCreate) GetModelSourceId() string {
+	if o == nil || IsNil(o.ModelSourceId) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceId
+}
+
+// GetModelSourceIdOk returns a tuple with the ModelSourceId field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactCreate) GetModelSourceIdOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceId) {
+		return nil, false
+	}
+	return o.ModelSourceId, true
+}
+
+// HasModelSourceId returns a boolean if a field has been set.
+func (o *ModelArtifactCreate) HasModelSourceId() bool {
+	if o != nil && !IsNil(o.ModelSourceId) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceId gets a reference to the given string and assigns it to the ModelSourceId field.
+func (o *ModelArtifactCreate) SetModelSourceId(v string) {
+	o.ModelSourceId = &v
+}
+
+// GetModelSourceName returns the ModelSourceName field value if set, zero value otherwise.
+func (o *ModelArtifactCreate) GetModelSourceName() string {
+	if o == nil || IsNil(o.ModelSourceName) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceName
+}
+
+// GetModelSourceNameOk returns a tuple with the ModelSourceName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactCreate) GetModelSourceNameOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceName) {
+		return nil, false
+	}
+	return o.ModelSourceName, true
+}
+
+// HasModelSourceName returns a boolean if a field has been set.
+func (o *ModelArtifactCreate) HasModelSourceName() bool {
+	if o != nil && !IsNil(o.ModelSourceName) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceName gets a reference to the given string and assigns it to the ModelSourceName field.
+func (o *ModelArtifactCreate) SetModelSourceName(v string) {
+	o.ModelSourceName = &v
+}
+
 func (o ModelArtifactCreate) MarshalJSON() ([]byte, error) {
 	toSerialize, err := o.ToMap()
 	if err != nil {
@@ -498,6 +668,21 @@ func (o ModelArtifactCreate) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.ServiceAccountName) {
 		toSerialize["serviceAccountName"] = o.ServiceAccountName
 	}
+	if !IsNil(o.ModelSourceKind) {
+		toSerialize["modelSourceKind"] = o.ModelSourceKind
+	}
+	if !IsNil(o.ModelSourceClass) {
+		toSerialize["modelSourceClass"] = o.ModelSourceClass
+	}
+	if !IsNil(o.ModelSourceGroup) {
+		toSerialize["modelSourceGroup"] = o.ModelSourceGroup
+	}
+	if !IsNil(o.ModelSourceId) {
+		toSerialize["modelSourceId"] = o.ModelSourceId
+	}
+	if !IsNil(o.ModelSourceName) {
+		toSerialize["modelSourceName"] = o.ModelSourceName
+	}
 	return toSerialize, nil
 }
 
diff --git a/pkg/openapi/model_model_artifact_update.go b/pkg/openapi/model_model_artifact_update.go
index bb74d14b..1a9e1510 100644
--- a/pkg/openapi/model_model_artifact_update.go
+++ b/pkg/openapi/model_model_artifact_update.go
@@ -39,6 +39,16 @@ type ModelArtifactUpdate struct {
 	ModelFormatVersion *string `json:"modelFormatVersion,omitempty"`
 	// Name of the service account with storage secret.
 	ServiceAccountName *string `json:"serviceAccountName,omitempty"`
+	// A string identifier describing the source kind. It differentiates various sources of model artifacts. This identifier should be agreed upon by producers and consumers of source model metadata. It is not an enumeration to keep the source of model metadata open ended.  E.g. Kubeflow pipelines could use `pipelines` to identify models it produces.
+	ModelSourceKind *string `json:"modelSourceKind,omitempty"`
+	// A subgroup within the source kind. It is a specific sub-component or instance within the source kind. E.g. `pipelinerun` for a Kubeflow pipeline run.
+	ModelSourceClass *string `json:"modelSourceClass,omitempty"`
+	// Unique identifier for a source group for models from source class.  It maps to a physical group of source models.  E.g. a Kubernetes namespace where the pipeline run was executed.
+	ModelSourceGroup *string `json:"modelSourceGroup,omitempty"`
+	// A unique identifier for a source model within kind, class, and group. It should be a url friendly string if source supports using URLs to locate source models. E.g. a pipeline run ID.
+	ModelSourceId *string `json:"modelSourceId,omitempty"`
+	// A human-readable name for the source model.  E.g. `my-project/1`, `ibm-granite/granite-3.1-8b-base:2.1.2`.
+	ModelSourceName *string `json:"modelSourceName,omitempty"`
 }
 
 // NewModelArtifactUpdate instantiates a new ModelArtifactUpdate object
@@ -418,6 +428,166 @@ func (o *ModelArtifactUpdate) SetServiceAccountName(v string) {
 	o.ServiceAccountName = &v
 }
 
+// GetModelSourceKind returns the ModelSourceKind field value if set, zero value otherwise.
+func (o *ModelArtifactUpdate) GetModelSourceKind() string {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceKind
+}
+
+// GetModelSourceKindOk returns a tuple with the ModelSourceKind field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactUpdate) GetModelSourceKindOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceKind) {
+		return nil, false
+	}
+	return o.ModelSourceKind, true
+}
+
+// HasModelSourceKind returns a boolean if a field has been set.
+func (o *ModelArtifactUpdate) HasModelSourceKind() bool {
+	if o != nil && !IsNil(o.ModelSourceKind) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceKind gets a reference to the given string and assigns it to the ModelSourceKind field.
+func (o *ModelArtifactUpdate) SetModelSourceKind(v string) {
+	o.ModelSourceKind = &v
+}
+
+// GetModelSourceClass returns the ModelSourceClass field value if set, zero value otherwise.
+func (o *ModelArtifactUpdate) GetModelSourceClass() string {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceClass
+}
+
+// GetModelSourceClassOk returns a tuple with the ModelSourceClass field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactUpdate) GetModelSourceClassOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceClass) {
+		return nil, false
+	}
+	return o.ModelSourceClass, true
+}
+
+// HasModelSourceClass returns a boolean if a field has been set.
+func (o *ModelArtifactUpdate) HasModelSourceClass() bool {
+	if o != nil && !IsNil(o.ModelSourceClass) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceClass gets a reference to the given string and assigns it to the ModelSourceClass field.
+func (o *ModelArtifactUpdate) SetModelSourceClass(v string) {
+	o.ModelSourceClass = &v
+}
+
+// GetModelSourceGroup returns the ModelSourceGroup field value if set, zero value otherwise.
+func (o *ModelArtifactUpdate) GetModelSourceGroup() string {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceGroup
+}
+
+// GetModelSourceGroupOk returns a tuple with the ModelSourceGroup field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactUpdate) GetModelSourceGroupOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceGroup) {
+		return nil, false
+	}
+	return o.ModelSourceGroup, true
+}
+
+// HasModelSourceGroup returns a boolean if a field has been set.
+func (o *ModelArtifactUpdate) HasModelSourceGroup() bool {
+	if o != nil && !IsNil(o.ModelSourceGroup) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceGroup gets a reference to the given string and assigns it to the ModelSourceGroup field.
+func (o *ModelArtifactUpdate) SetModelSourceGroup(v string) {
+	o.ModelSourceGroup = &v
+}
+
+// GetModelSourceId returns the ModelSourceId field value if set, zero value otherwise.
+func (o *ModelArtifactUpdate) GetModelSourceId() string {
+	if o == nil || IsNil(o.ModelSourceId) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceId
+}
+
+// GetModelSourceIdOk returns a tuple with the ModelSourceId field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactUpdate) GetModelSourceIdOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceId) {
+		return nil, false
+	}
+	return o.ModelSourceId, true
+}
+
+// HasModelSourceId returns a boolean if a field has been set.
+func (o *ModelArtifactUpdate) HasModelSourceId() bool {
+	if o != nil && !IsNil(o.ModelSourceId) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceId gets a reference to the given string and assigns it to the ModelSourceId field.
+func (o *ModelArtifactUpdate) SetModelSourceId(v string) {
+	o.ModelSourceId = &v
+}
+
+// GetModelSourceName returns the ModelSourceName field value if set, zero value otherwise.
+func (o *ModelArtifactUpdate) GetModelSourceName() string {
+	if o == nil || IsNil(o.ModelSourceName) {
+		var ret string
+		return ret
+	}
+	return *o.ModelSourceName
+}
+
+// GetModelSourceNameOk returns a tuple with the ModelSourceName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *ModelArtifactUpdate) GetModelSourceNameOk() (*string, bool) {
+	if o == nil || IsNil(o.ModelSourceName) {
+		return nil, false
+	}
+	return o.ModelSourceName, true
+}
+
+// HasModelSourceName returns a boolean if a field has been set.
+func (o *ModelArtifactUpdate) HasModelSourceName() bool {
+	if o != nil && !IsNil(o.ModelSourceName) {
+		return true
+	}
+
+	return false
+}
+
+// SetModelSourceName gets a reference to the given string and assigns it to the ModelSourceName field.
+func (o *ModelArtifactUpdate) SetModelSourceName(v string) {
+	o.ModelSourceName = &v
+}
+
 func (o ModelArtifactUpdate) MarshalJSON() ([]byte, error) {
 	toSerialize, err := o.ToMap()
 	if err != nil {
@@ -461,6 +631,21 @@ func (o ModelArtifactUpdate) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.ServiceAccountName) {
 		toSerialize["serviceAccountName"] = o.ServiceAccountName
 	}
+	if !IsNil(o.ModelSourceKind) {
+		toSerialize["modelSourceKind"] = o.ModelSourceKind
+	}
+	if !IsNil(o.ModelSourceClass) {
+		toSerialize["modelSourceClass"] = o.ModelSourceClass
+	}
+	if !IsNil(o.ModelSourceGroup) {
+		toSerialize["modelSourceGroup"] = o.ModelSourceGroup
+	}
+	if !IsNil(o.ModelSourceId) {
+		toSerialize["modelSourceId"] = o.ModelSourceId
+	}
+	if !IsNil(o.ModelSourceName) {
+		toSerialize["modelSourceName"] = o.ModelSourceName
+	}
 	return toSerialize, nil
 }
 
diff --git a/pkg/openapi/model_serving_environment.go b/pkg/openapi/model_serving_environment.go
index b04d29d9..d6f24422 100644
--- a/pkg/openapi/model_serving_environment.go
+++ b/pkg/openapi/model_serving_environment.go
@@ -25,8 +25,8 @@ type ServingEnvironment struct {
 	Description *string `json:"description,omitempty"`
 	// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
 	ExternalId *string `json:"externalId,omitempty"`
-	// The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.
-	Name *string `json:"name,omitempty"`
+	// The name of the ServingEnvironment.
+	Name string `json:"name"`
 	// The unique server generated id of the resource.
 	Id *string `json:"id,omitempty"`
 	// Output only. Create time of the resource in millisecond since epoch.
@@ -39,8 +39,9 @@ type ServingEnvironment struct {
 // This constructor will assign default values to properties that have it defined,
 // and makes sure properties required by API are set, but the set of arguments
 // will change when the set of required properties is changed
-func NewServingEnvironment() *ServingEnvironment {
+func NewServingEnvironment(name string) *ServingEnvironment {
 	this := ServingEnvironment{}
+	this.Name = name
 	return &this
 }
 
@@ -148,36 +149,28 @@ func (o *ServingEnvironment) SetExternalId(v string) {
 	o.ExternalId = &v
 }
 
-// GetName returns the Name field value if set, zero value otherwise.
+// GetName returns the Name field value
 func (o *ServingEnvironment) GetName() string {
-	if o == nil || IsNil(o.Name) {
+	if o == nil {
 		var ret string
 		return ret
 	}
-	return *o.Name
+
+	return o.Name
 }
 
-// GetNameOk returns a tuple with the Name field value if set, nil otherwise
+// GetNameOk returns a tuple with the Name field value
 // and a boolean to check if the value has been set.
 func (o *ServingEnvironment) GetNameOk() (*string, bool) {
-	if o == nil || IsNil(o.Name) {
+	if o == nil {
 		return nil, false
 	}
-	return o.Name, true
-}
-
-// HasName returns a boolean if a field has been set.
-func (o *ServingEnvironment) HasName() bool {
-	if o != nil && !IsNil(o.Name) {
-		return true
-	}
-
-	return false
+	return &o.Name, true
 }
 
-// SetName gets a reference to the given string and assigns it to the Name field.
+// SetName sets field value
 func (o *ServingEnvironment) SetName(v string) {
-	o.Name = &v
+	o.Name = v
 }
 
 // GetId returns the Id field value if set, zero value otherwise.
@@ -295,9 +288,7 @@ func (o ServingEnvironment) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.ExternalId) {
 		toSerialize["externalId"] = o.ExternalId
 	}
-	if !IsNil(o.Name) {
-		toSerialize["name"] = o.Name
-	}
+	toSerialize["name"] = o.Name
 	if !IsNil(o.Id) {
 		toSerialize["id"] = o.Id
 	}
diff --git a/pkg/openapi/model_serving_environment_create.go b/pkg/openapi/model_serving_environment_create.go
index 093e9fec..8fcfc3d6 100644
--- a/pkg/openapi/model_serving_environment_create.go
+++ b/pkg/openapi/model_serving_environment_create.go
@@ -25,16 +25,17 @@ type ServingEnvironmentCreate struct {
 	Description *string `json:"description,omitempty"`
 	// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
 	ExternalId *string `json:"externalId,omitempty"`
-	// The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.
-	Name *string `json:"name,omitempty"`
+	// The name of the ServingEnvironment.
+	Name string `json:"name"`
 }
 
 // NewServingEnvironmentCreate instantiates a new ServingEnvironmentCreate object
 // This constructor will assign default values to properties that have it defined,
 // and makes sure properties required by API are set, but the set of arguments
 // will change when the set of required properties is changed
-func NewServingEnvironmentCreate() *ServingEnvironmentCreate {
+func NewServingEnvironmentCreate(name string) *ServingEnvironmentCreate {
 	this := ServingEnvironmentCreate{}
+	this.Name = name
 	return &this
 }
 
@@ -142,36 +143,28 @@ func (o *ServingEnvironmentCreate) SetExternalId(v string) {
 	o.ExternalId = &v
 }
 
-// GetName returns the Name field value if set, zero value otherwise.
+// GetName returns the Name field value
 func (o *ServingEnvironmentCreate) GetName() string {
-	if o == nil || IsNil(o.Name) {
+	if o == nil {
 		var ret string
 		return ret
 	}
-	return *o.Name
+
+	return o.Name
 }
 
-// GetNameOk returns a tuple with the Name field value if set, nil otherwise
+// GetNameOk returns a tuple with the Name field value
 // and a boolean to check if the value has been set.
 func (o *ServingEnvironmentCreate) GetNameOk() (*string, bool) {
-	if o == nil || IsNil(o.Name) {
+	if o == nil {
 		return nil, false
 	}
-	return o.Name, true
-}
-
-// HasName returns a boolean if a field has been set.
-func (o *ServingEnvironmentCreate) HasName() bool {
-	if o != nil && !IsNil(o.Name) {
-		return true
-	}
-
-	return false
+	return &o.Name, true
 }
 
-// SetName gets a reference to the given string and assigns it to the Name field.
+// SetName sets field value
 func (o *ServingEnvironmentCreate) SetName(v string) {
-	o.Name = &v
+	o.Name = v
 }
 
 func (o ServingEnvironmentCreate) MarshalJSON() ([]byte, error) {
@@ -193,9 +186,7 @@ func (o ServingEnvironmentCreate) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.ExternalId) {
 		toSerialize["externalId"] = o.ExternalId
 	}
-	if !IsNil(o.Name) {
-		toSerialize["name"] = o.Name
-	}
+	toSerialize["name"] = o.Name
 	return toSerialize, nil
 }
 
diff --git a/tilt-ui.dockerfile b/tilt-ui.dockerfile
new file mode 100644
index 00000000..610943cc
--- /dev/null
+++ b/tilt-ui.dockerfile
@@ -0,0 +1,8 @@
+FROM alpine:3.12
+
+WORKDIR /
+
+COPY devenv/bin/bff /bff
+COPY devenv/ui-static /static
+
+ENTRYPOINT ["/bff"]
diff --git a/tilt.dockerfile b/tilt.dockerfile
new file mode 100644
index 00000000..2079c9fb
--- /dev/null
+++ b/tilt.dockerfile
@@ -0,0 +1,7 @@
+FROM alpine:3.12
+
+WORKDIR /
+
+COPY devenv/bin/model-registry /model-registry
+
+ENTRYPOINT ["/model-registry"]



Test patch:
diff --git a/clients/python/tests/.gitignore b/clients/python/tests/.gitignore
index 8fce6030..ef3c045a 100644
--- a/clients/python/tests/.gitignore
+++ b/clients/python/tests/.gitignore
@@ -1 +1,2 @@
 data/
+tmp/
diff --git a/clients/python/tests/conftest.py b/clients/python/tests/conftest.py
index 43f2493a..2cfcda61 100644
--- a/clients/python/tests/conftest.py
+++ b/clients/python/tests/conftest.py
@@ -1,15 +1,19 @@
 import asyncio
 import inspect
 import os
+import pathlib
+import shutil
 import subprocess
 import tempfile
 import time
 from contextlib import asynccontextmanager
 from pathlib import Path
+from unittest.mock import Mock
 from urllib.parse import urlparse
 
 import pytest
 import requests
+import uvloop
 
 from model_registry import ModelRegistry
 
@@ -92,12 +96,35 @@ def event_loop():
     loop.close()
 
 
+@pytest.fixture
+def uv_event_loop():
+    old_policy = asyncio.get_event_loop_policy()
+    policy = uvloop.EventLoopPolicy()
+    asyncio.set_event_loop_policy(policy)
+    loop = uvloop.new_event_loop()
+    asyncio.set_event_loop(loop)
+    yield loop
+    loop.close()
+    asyncio.set_event_loop_policy(old_policy)
+
+
 @pytest.fixture
 @cleanup
 def client() -> ModelRegistry:
     return ModelRegistry(REGISTRY_HOST, REGISTRY_PORT, author="author", is_secure=False)
 
 
+@pytest.fixture
+@cleanup
+def client_attrs() -> dict[str, any]:
+    return {
+        "host": REGISTRY_HOST,
+        "port": REGISTRY_PORT,
+        "author": "author",
+        "ssl": False,
+    }
+
+
 @pytest.fixture(scope="module")
 def setup_env_user_token():
     with tempfile.NamedTemporaryFile(delete=False) as token_file:
@@ -143,6 +170,15 @@ def get_temp_dir_with_models():
     os.rmdir(temp_dir)
 
 
+@pytest.fixture
+def get_temp_dir():
+    temp_dir = tempfile.mkdtemp()
+
+    yield temp_dir
+
+    shutil.rmtree(temp_dir)
+
+
 @pytest.fixture
 def get_temp_dir_with_nested_models():
     temp_dir = tempfile.mkdtemp()
@@ -171,9 +207,43 @@ def patch_s3_env(monkeypatch: pytest.MonkeyPatch):
     access_key_id = os.getenv("KF_MR_TEST_ACCESS_KEY_ID")
     secret_access_key = os.getenv("KF_MR_TEST_SECRET_ACCESS_KEY")
     bucket = os.getenv("KF_MR_TEST_BUCKET_NAME") or "default"
+    region = "east"
 
     monkeypatch.setenv("AWS_S3_ENDPOINT", s3_endpoint)
     monkeypatch.setenv("AWS_ACCESS_KEY_ID", access_key_id)
     monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", secret_access_key)
-    monkeypatch.setenv("AWS_DEFAULT_REGION", "east")
+    monkeypatch.setenv("AWS_DEFAULT_REGION", region)
     monkeypatch.setenv("AWS_S3_BUCKET", bucket)
+
+    return (bucket, s3_endpoint, access_key_id, secret_access_key, region)
+
+
+# These are trimmed down versions of whats found in the example specs found here: https://github.com/opencontainers/image-spec/blob/main/image-layout.md#oci-layout-file
+index_json_contents = """{
+  "schemaVersion": 2,
+  "mediaType": "application/vnd.oci.image.index.v1+json",
+  "manifests": [],
+  "annotations": {
+    "com.example.index.revision": "r124356"
+  }
+}"""
+oci_layout_contents = """{"imageLayoutVersion": "1.0.0"}"""
+
+
+@pytest.fixture
+def get_mock_custom_oci_backend():
+    is_available_mock = Mock()
+    is_available_mock.return_value = True
+    pull_mock = Mock()
+    push_mock = Mock()
+
+    def pull_mock_imple(base_image, dest_dir):
+        pathlib.Path(dest_dir).joinpath("oci-layout").write_text(oci_layout_contents)
+        pathlib.Path(dest_dir).joinpath("index.json").write_text(index_json_contents)
+
+    pull_mock.side_effect = pull_mock_imple
+    return {
+        "is_available": is_available_mock,
+        "pull": pull_mock,
+        "push": push_mock,
+    }
diff --git a/clients/python/tests/extras/__init__.py b/clients/python/tests/extras/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/clients/python/tests/extras/async_task_runner.py b/clients/python/tests/extras/async_task_runner.py
new file mode 100644
index 00000000..3028a0b5
--- /dev/null
+++ b/clients/python/tests/extras/async_task_runner.py
@@ -0,0 +1,94 @@
+# from https://gist.github.com/blink1073/969aeba85f32c285235750626f2eadd8
+# Slightly reformatted for linting purposes March 3, 2025, no logic changed.
+# License below. Not shipped for use within the library code, only used for test examples.
+
+"""
+Copyright (c) 2022 Steven Silvester
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import asyncio
+import atexit
+from collections.abc import Coroutine
+from threading import Lock, Thread
+from typing import Any, Optional
+
+
+class AsyncTaskRunner:
+    """A singleton task runner that runs an asyncio event loop on a background thread."""
+
+    __instance = None
+
+    @staticmethod
+    def get_instance():
+        """Get an AsyncTaskRunner (singleton)."""
+        if AsyncTaskRunner.__instance is None:
+            AsyncTaskRunner()
+        assert AsyncTaskRunner.__instance is not None
+        return AsyncTaskRunner.__instance
+
+    def __init__(self):
+        """Initialize."""
+        # make sure it is a singleton
+        if AsyncTaskRunner.__instance is not None:
+            msg = "This class is a singleton!"
+            raise Exception(msg)
+        AsyncTaskRunner.__instance = self
+        # initialize variables
+        self.__io_loop: Optional[asyncio.AbstractEventLoop] = None
+        self.__runner_thread: Optional[Thread] = None
+        self.__lock = Lock()
+        # register exit handler
+        atexit.register(self._close)
+
+    def _close(self):
+        """Clean up. Stop the loop if running."""
+        if self.__io_loop:
+            self.__io_loop.stop()
+
+    def _runner(self) -> None:
+        """Function to run in a thread."""
+        loop = self.__io_loop
+        assert loop is not None
+        try:
+            loop.run_forever()
+        finally:
+            loop.close()
+
+    def run(self, coro: Coroutine) -> Any:
+        """Synchronously run a coroutine on a background thread."""
+        with self.__lock:
+            if self.__io_loop is None:
+                # If the asyncio loop does not exist
+                self.__io_loop = asyncio.new_event_loop()
+                self.__runner_thread = Thread(target=self._runner, daemon=True)
+                self.__runner_thread.start()
+        # run coroutine thread safe inside a thread. This return concurrent future
+        fut = asyncio.run_coroutine_threadsafe(coro, self.__io_loop)
+        # get concurrent future result
+        return fut.result()
diff --git a/clients/python/tests/test_client.py b/clients/python/tests/test_client.py
index 8ad5707d..18cbffc7 100644
--- a/clients/python/tests/test_client.py
+++ b/clients/python/tests/test_client.py
@@ -8,6 +8,8 @@ from model_registry import ModelRegistry, utils
 from model_registry.exceptions import StoreError
 from model_registry.types import ModelArtifact
 
+from .extras.async_task_runner import AsyncTaskRunner
+
 
 def test_secure_client():
     os.environ["CERT"] = ""
@@ -708,7 +710,7 @@ def test_singular_store_in_s3(get_model_file, patch_s3_env, client: ModelRegistr
     assert model_name_pfx in objects_by_name
 
     # Test file not exists
-    with pytest.raises(StoreError) as e:
+    with pytest.raises(ValueError, match="Please ensure path is correct.") as e:
         client.save_to_s3(
             path=f"{get_model_file}x", s3_prefix=prefix, bucket_name=bucket
         )
@@ -767,7 +769,7 @@ def test_recursive_store_in_s3(
         assert path in objects_by_name
 
     # Test incorrect folder
-    with pytest.raises(StoreError) as e:
+    with pytest.raises(ValueError, match="Please ensure path is correct.") as e:
         client.save_to_s3(path=f"{model_dir}x", s3_prefix=prefix, bucket_name=bucket)
     assert "please ensure path is correct" in str(e.value).lower()
 
@@ -814,8 +816,8 @@ def test_nested_recursive_store_in_s3(
     objects = s3.list_objects_v2(Bucket="default")["Contents"]
     objects_by_name = [obj["Key"] for obj in objects]
     s3_uri = utils.s3_uri_from(
-            bucket=bucket, path=prefix, endpoint=s3_endpoint, region=default_region
-        )
+        bucket=bucket, path=prefix, endpoint=s3_endpoint, region=default_region
+    )
     # this is creating a list of all the file names + their immediate parent folder only
     formatted_paths = [
         os.path.join(
@@ -830,6 +832,139 @@ def test_nested_recursive_store_in_s3(
         assert path in objects_by_name
 
     # Test incorrect folder
-    with pytest.raises(StoreError) as e:
+    with pytest.raises(ValueError, match="Please ensure path is correct.") as e:
         client.save_to_s3(path=f"{model_dir}x", s3_prefix=prefix, bucket_name=bucket)
     assert "please ensure path is correct" in str(e.value).lower()
+
+
+@pytest.mark.usefixtures("uv_event_loop")
+@pytest.mark.e2e
+async def test_custom_async_runner_with_ray(
+    client_attrs: dict[str, any], client: ModelRegistry
+):
+    import asyncio
+
+    import ray
+    import uvloop
+
+    # Check to make sure the uvloop event loop is running
+    loop = asyncio.get_event_loop()
+    assert isinstance(loop, uvloop.Loop)
+
+    @ray.remote
+    def test_with_ray():
+        atr = AsyncTaskRunner()
+        # we have to construct a client from scratch due to serialization issues from Ray
+        client = ModelRegistry(
+            server_address=client_attrs["host"],
+            port=client_attrs["port"],
+            author=client_attrs["author"],
+            is_secure=client_attrs["ssl"],
+            async_runner=atr.run,
+        )
+        client.register_model(
+            name="test_model",
+            uri="https://acme.org/something",
+            version="v1",
+            model_format_version="random",
+            model_format_name="onnx",
+        )
+        ma = client.get_model_artifact(name="test_model", version="v1")
+        assert ma.uri == "https://acme.org/something"
+        assert ma.model_format_name == "onnx"
+
+    ray.get(test_with_ray.remote())
+
+
+@pytest.mark.e2e
+def test_upload_artifact_and_register_model_with_default_oci(
+    client: ModelRegistry,
+    get_temp_dir_with_models,
+) -> None:
+    # olot is required to run this test
+    pytest.importorskip("olot")
+    name = "oci-test/defaults"
+    version = "0.0.1"
+    oci_ref = "localhost:5001/foo/bar:latest"
+
+    model_dir, _ = get_temp_dir_with_models
+
+    upload_params = utils.OCIParams(
+        "quay.io/mmortari/hello-world-wait:latest",
+        oci_ref,
+    )
+
+    assert client.upload_artifact_and_register_model(
+        name,
+        model_files_path=model_dir,
+        author="Tester McTesterson",
+        version=version,
+        model_format_name="test format",
+        model_format_version="test version",
+        upload_params=upload_params,
+    )
+
+    assert (ma := client.get_model_artifact(name, version))
+    assert ma.uri == f"oci://{oci_ref}"
+
+
+@pytest.mark.e2e
+def test_upload_artifact_and_register_model_with_default_s3(
+    client: ModelRegistry,
+    patch_s3_env,
+    get_temp_dir_with_models,
+) -> None:
+    # olot is required to run this test
+    pytest.importorskip("olot")
+    name = "oci-test/defaults"
+    version = "0.0.1"
+
+    s3_prefix = f"my-model-{version}"
+    model_dir, _ = get_temp_dir_with_models
+
+    bucket, s3_endpoint, access_key_id, secret_access_key, region = patch_s3_env
+
+    upload_params = utils.S3Params(
+        bucket,
+        s3_prefix,
+        s3_endpoint,
+        access_key_id,
+        secret_access_key,
+        region,
+    )
+
+    assert client.upload_artifact_and_register_model(
+        name,
+        model_files_path=model_dir,
+        author="Tester McTesterson",
+        version=version,
+        model_format_name="test format",
+        model_format_version="test version",
+        upload_params=upload_params,
+    )
+
+    assert (ma := client.get_model_artifact(name, version))
+    assert (
+        ma.uri
+        == f"s3://{bucket}/{s3_prefix}?endpoint={s3_endpoint}&defaultRegion={region}"
+    )
+
+
+@pytest.mark.e2e
+def test_upload_artifact_and_register_model_missing_upload_params(client):
+    with pytest.raises(
+        ValueError, match='Param "upload_params" is required to perform an upload'
+    ) as e:
+        client.upload_artifact_and_register_model(
+            "a name",
+            model_files_path="/doesnt/matter",
+            author="Tester McTesterson",
+            version="v0.0.1",
+            model_format_name="test format",
+            model_format_version="test version",
+            upload_params=None,
+        )
+    assert (
+        'Param "upload_params" is required to perform an upload. Please ensure the value provided is valid'
+        in str(e.value)
+    )
diff --git a/clients/python/tests/test_core.py b/clients/python/tests/test_core.py
index 9a261feb..ac7f7d4b 100644
--- a/clients/python/tests/test_core.py
+++ b/clients/python/tests/test_core.py
@@ -53,8 +53,8 @@ async def registered_model(client: ModelRegistryAPIClient) -> RegisteredModel:
 
 @pytest.mark.e2e
 async def test_get_registered_model_by_id(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
 ):
     assert (rm := await client.get_registered_model_by_id(str(registered_model.id)))
     assert rm == registered_model
@@ -62,8 +62,8 @@ async def test_get_registered_model_by_id(
 
 @pytest.mark.e2e
 async def test_get_registered_model_by_name(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
 ):
     assert (
         rm := await client.get_registered_model_by_params(name=registered_model.name)
@@ -73,8 +73,8 @@ async def test_get_registered_model_by_name(
 
 @pytest.mark.e2e
 async def test_get_registered_model_by_external_id(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
 ):
     assert registered_model.external_id
     assert (
@@ -87,7 +87,7 @@ async def test_get_registered_model_by_external_id(
 
 @pytest.mark.e2e
 async def test_get_registered_models(
-    client: ModelRegistryAPIClient, registered_model: RegisteredModel
+        client: ModelRegistryAPIClient, registered_model: RegisteredModel
 ):
     rm2 = await client.upsert_registered_model(RegisteredModel(name="rm2"))
 
@@ -109,8 +109,8 @@ async def test_page_through_registered_models(client: ModelRegistryAPIClient):
 
 @pytest.mark.e2e
 async def test_insert_model_version(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
 ):
     model_version = ModelVersion(name="test version", author="test author")
     mv = await client.upsert_model_version(model_version, str(registered_model.id))
@@ -125,7 +125,7 @@ async def test_insert_model_version(
 
 @pytest.mark.e2e
 async def test_update_model_version(
-    client: ModelRegistryAPIClient, registered_model: RegisteredModel
+        client: ModelRegistryAPIClient, registered_model: RegisteredModel
 ):
     model_version = ModelVersion(name="updated mv", author="test author")
     mv = await client.upsert_model_version(model_version, str(registered_model.id))
@@ -139,7 +139,7 @@ async def test_update_model_version(
 
 @pytest.fixture
 async def model_version(
-    client: ModelRegistryAPIClient, registered_model: RegisteredModel
+        client: ModelRegistryAPIClient, registered_model: RegisteredModel
 ) -> ModelVersion:
     return await client.upsert_model_version(
         ModelVersion(name="version", author="author", external_id="mv id"),
@@ -149,7 +149,7 @@ async def model_version(
 
 @pytest.mark.e2e
 async def test_get_model_version_by_id(
-    client: ModelRegistryAPIClient, model_version: ModelVersion
+        client: ModelRegistryAPIClient, model_version: ModelVersion
 ):
     assert (mv := await client.get_model_version_by_id(str(model_version.id)))
     assert mv == model_version
@@ -157,9 +157,9 @@ async def test_get_model_version_by_id(
 
 @pytest.mark.e2e
 async def test_get_model_version_by_name(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
-    model_version: ModelVersion,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
+        model_version: ModelVersion,
 ):
     assert (
         mv := await client.get_model_version_by_params(
@@ -171,7 +171,7 @@ async def test_get_model_version_by_name(
 
 @pytest.mark.e2e
 async def test_get_model_version_by_external_id(
-    client: ModelRegistryAPIClient, model_version: ModelVersion
+        client: ModelRegistryAPIClient, model_version: ModelVersion
 ):
     assert (
         mv := await client.get_model_version_by_params(
@@ -183,9 +183,9 @@ async def test_get_model_version_by_external_id(
 
 @pytest.mark.e2e
 async def test_get_model_versions(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
-    model_version: ModelVersion,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
+        model_version: ModelVersion,
 ):
     mv2 = await client.upsert_model_version(
         ModelVersion(name="mv2", author="author"), str(registered_model.id)
@@ -197,7 +197,7 @@ async def test_get_model_versions(
 
 @pytest.mark.e2e
 async def test_page_through_model_versions(
-    client: ModelRegistryAPIClient, registered_model: RegisteredModel
+        client: ModelRegistryAPIClient, registered_model: RegisteredModel
 ):
     models = 6
     for i in range(models):
@@ -215,7 +215,7 @@ async def test_page_through_model_versions(
 
 @pytest.mark.e2e
 async def test_insert_model_version_artifact(
-    client: ModelRegistryAPIClient, model_version: ModelVersion
+        client: ModelRegistryAPIClient, model_version: ModelVersion
 ):
     model = DocArtifact(
         name="test model",
@@ -232,7 +232,7 @@ async def test_insert_model_version_artifact(
 
 @pytest.mark.e2e
 async def test_update_model_version_artifact(
-    client: ModelRegistryAPIClient, model_version: ModelVersion
+        client: ModelRegistryAPIClient, model_version: ModelVersion
 ):
     model = DocArtifact(name="updated model", uri="uri")
     da = await client.upsert_model_version_artifact(model, str(model_version.id))
@@ -247,7 +247,7 @@ async def test_update_model_version_artifact(
 
 @pytest.mark.e2e
 async def test_insert_model_artifact(
-    client: ModelRegistryAPIClient,
+        client: ModelRegistryAPIClient,
 ):
     model = ModelArtifact(
         name="test model",
@@ -257,6 +257,11 @@ async def test_insert_model_artifact(
         storage_key="test key",
         storage_path="test path",
         service_account_name="test service account",
+        model_source_kind="test source kind",
+        model_source_class="test source class",
+        model_source_group="test source group",
+        model_source_id="test source id",
+        model_source_name="test source name",
     )
     ma = await client.upsert_model_artifact(model)
     assert ma.id
@@ -271,6 +276,11 @@ async def test_insert_model_artifact(
     assert ma.storage_key
     assert ma.storage_path
     assert ma.service_account_name
+    assert ma.model_source_kind
+    assert ma.model_source_class
+    assert ma.model_source_group
+    assert ma.model_source_id
+    assert ma.model_source_name
 
 
 @pytest.mark.e2e
@@ -287,8 +297,8 @@ async def test_update_model_artifact(client: ModelRegistryAPIClient):
 
 @pytest.fixture
 async def model(
-    client: ModelRegistryAPIClient,
-    model_version: ModelVersion,
+        client: ModelRegistryAPIClient,
+        model_version: ModelVersion,
 ) -> ModelArtifact:
     return await client.upsert_model_version_artifact(
         ModelArtifact(name="model", uri="uri", external_id="ma id"),
@@ -298,7 +308,7 @@ async def model(
 
 @pytest.mark.e2e
 async def test_get_model_artifact_by_id(
-    client: ModelRegistryAPIClient, model: ModelArtifact
+        client: ModelRegistryAPIClient, model: ModelArtifact
 ):
     assert (ma := await client.get_model_artifact_by_id(str(model.id)))
     assert ma == model
@@ -306,7 +316,7 @@ async def test_get_model_artifact_by_id(
 
 @pytest.mark.e2e
 async def test_get_model_artifact_by_name(
-    client: ModelRegistryAPIClient, model_version: ModelVersion, model: ModelArtifact
+        client: ModelRegistryAPIClient, model_version: ModelVersion, model: ModelArtifact
 ):
     assert (
         ma := await client.get_model_artifact_by_params(
@@ -318,7 +328,7 @@ async def test_get_model_artifact_by_name(
 
 @pytest.mark.e2e
 async def test_get_model_artifact_by_external_id(
-    client: ModelRegistryAPIClient, model: ModelArtifact
+        client: ModelRegistryAPIClient, model: ModelArtifact
 ):
     assert (
         ma := await client.get_model_artifact_by_params(
@@ -330,7 +340,7 @@ async def test_get_model_artifact_by_external_id(
 
 @pytest.mark.e2e
 async def test_get_all_model_artifacts(
-    client: ModelRegistryAPIClient, model: ModelArtifact
+        client: ModelRegistryAPIClient, model: ModelArtifact
 ):
     ma2 = await client.upsert_model_artifact(ModelArtifact(name="ma2", uri="uri"))
 
@@ -340,7 +350,7 @@ async def test_get_all_model_artifacts(
 
 @pytest.mark.e2e
 async def test_get_model_version_artifacts_by_mv_id(
-    client: ModelRegistryAPIClient, model_version: ModelVersion, model: ModelArtifact
+        client: ModelRegistryAPIClient, model_version: ModelVersion, model: ModelArtifact
 ):
     ma2 = await client.upsert_model_version_artifact(
         ModelArtifact(name="ma2", uri="uri"), str(model_version.id)
@@ -352,9 +362,9 @@ async def test_get_model_version_artifacts_by_mv_id(
 
 @pytest.mark.e2e
 async def test_page_through_model_version_artifacts(
-    client: ModelRegistryAPIClient,
-    registered_model: RegisteredModel,
-    model_version: ModelVersion,
+        client: ModelRegistryAPIClient,
+        registered_model: RegisteredModel,
+        model_version: ModelVersion,
 ):
     _ = registered_model
     models = 6
diff --git a/clients/python/tests/test_utils.py b/clients/python/tests/test_utils.py
index c1480173..072c5894 100644
--- a/clients/python/tests/test_utils.py
+++ b/clients/python/tests/test_utils.py
@@ -1,11 +1,13 @@
 import os
-import pathlib
-from unittest.mock import Mock
 
 import pytest
 
 from model_registry.exceptions import MissingMetadata
-from model_registry.utils import s3_uri_from, save_to_oci_registry
+from model_registry.utils import (
+    get_files_from_path,
+    s3_uri_from,
+    save_to_oci_registry,
+)
 
 
 def test_s3_uri_builder():
@@ -74,101 +76,72 @@ def test_s3_uri_builder_with_complete_env():
     os.environ["AWS_DEFAULT_REGION"] = "test-region"
     assert s3_uri_from("test-path") == s3_uri_from("test-path", "test-bucket")
 
+
 @pytest.mark.e2e(type="oci")
-def test_save_to_oci_registry_with_skopeo():
-    # TODO: We need a good source registry which is oci-compliant and very small in size
-    base_image = "quay.io/mmortari/hello-world-wait:latest"
-    dest_dir = "tests/data"
+def test_save_to_oci_registry_with_skopeo(get_temp_dir_with_models, get_temp_dir):
+    base_image = "busybox:latest"
+    dest_dir, _ = get_temp_dir_with_models
     oci_ref = "localhost:5001/foo/bar:latest"
 
-    # Create a sample file named README.md to be added to the registry
-    pathlib.Path(dest_dir).mkdir(parents=True, exist_ok=True)
-    readme_file_path = os.path.join(dest_dir, "README.md")
-    with open(readme_file_path, "w") as f:
-        f.write("")
-
-    model_files = [readme_file_path]
     backend = "skopeo"
 
-    save_to_oci_registry(base_image, oci_ref, model_files, dest_dir, backend)
-
-
-# These are trimmed down versions of whats found in the example specs found here: https://github.com/opencontainers/image-spec/blob/main/image-layout.md#oci-layout-file
-index_json_contents = """{
-  "schemaVersion": 2,
-  "mediaType": "application/vnd.oci.image.index.v1+json",
-  "manifests": [],
-  "annotations": {
-    "com.example.index.revision": "r124356"
-  }
-}"""
-oci_layout_contents = """{"imageLayoutVersion": "1.0.0"}"""
-
-def test_save_to_oci_registry_with_custom_backend():
-    is_available_mock = Mock()
-    is_available_mock.return_value = True
-    pull_mock = Mock()
-    push_mock = Mock()
-    def pull_mock_imple(base_image, dest_dir):
-        pathlib.Path(dest_dir).joinpath("oci-layout").write_text(oci_layout_contents)
-        pathlib.Path(dest_dir).joinpath("index.json").write_text(index_json_contents)
-
-    pull_mock.side_effect = pull_mock_imple
+    save_to_oci_registry(base_image, oci_ref, dest_dir, get_temp_dir, backend)
 
 
+def test_save_to_oci_registry_with_custom_backend(
+    get_temp_dir_with_models, get_temp_dir, get_mock_custom_oci_backend
+):
     backend = "something_custom"
-    backend_registry = {
-        "something_custom": lambda: {
-            "is_available": is_available_mock,
-            "pull": pull_mock,
-            "push": push_mock,
-        }
-    }
-
     # similar to other test
-    base_image = "quay.io/mmortari/hello-world-wait:latest"
-    dest_dir = "tests/data"
+    base_image = "busybox:latest"
+    dest_dir, _ = get_temp_dir_with_models
     oci_ref = "localhost:5001/foo/bar:latest"
 
-    # Create a sample file named README.md to be added to the registry
-    pathlib.Path(dest_dir).mkdir(parents=True, exist_ok=True)
-    readme_file_path = os.path.join(dest_dir, "README.md")
-    with open(readme_file_path, "w") as f:
-        f.write("")
-
-    model_files = [readme_file_path]
-
-    uri = save_to_oci_registry(base_image, oci_ref, model_files, dest_dir, backend, None, backend_registry)
+    uri = save_to_oci_registry(
+        base_image=base_image,
+        oci_ref=oci_ref,
+        model_files_path=dest_dir,
+        dest_dir=get_temp_dir,
+        backend=backend,
+        modelcard=None,
+        custom_oci_backend=get_mock_custom_oci_backend,
+    )
     # Ensure our mocked backend was called
-    is_available_mock.assert_called_once()
-    pull_mock.assert_called_once()
-    push_mock.assert_called_once()
+    get_mock_custom_oci_backend["is_available"].assert_called_once()
+    get_mock_custom_oci_backend["pull"].assert_called_once()
+    get_mock_custom_oci_backend["push"].assert_called_once()
     assert uri == f"oci://{oci_ref}"
 
-def test_save_to_oci_registry_with_custom_backend_unavailable():
-    is_available_mock = Mock()
-    is_available_mock.return_value = False # Backend is unavailable, expect an error
-    pull_mock = Mock()
-    push_mock = Mock()
-
-    backend = "something_custom"
-    backend_registry = {
-        "something_custom": lambda: {
-            "is_available": is_available_mock,
-            "pull": pull_mock,
-            "push": push_mock,
-        }
-    }
-
-
-    with pytest.raises(ValueError, match=f"Backend '{backend}' is selected, but not available on the system. Ensure the dependencies for '{backend}' are installed in your environment.") as e:
-        save_to_oci_registry("", "", [], "", backend, backend_registry=backend_registry)
-
-    assert f"Backend '{backend}' is selected, but not available on the system." in str(e.value)
 
 def test_save_to_oci_registry_backend_not_found():
     backend = "non-existent"
-    with pytest.raises(ValueError, match=f"'{backend}' is not an available backend to use.") as e:
+    with pytest.raises(
+        ValueError, match=f"'{backend}' is not an available backend to use."
+    ) as e:
         save_to_oci_registry("", "", [], "", backend)
 
     assert f"'{backend}' is not an available backend to use." in str(e.value)
+
+
+def test_get_files_from_path_no_path():
+    path = "/in/val/id/pa/th"
+    with pytest.raises(ValueError, match="Please ensure path is correct.") as e:
+        get_files_from_path(path)
+    assert e
+
+
+def test_get_files_from_path_single_file(get_model_file):
+    file = get_files_from_path(get_model_file)
+    # It returns only 1 file in the list, and it is a tuple of (absolute_path, filename)
+    assert len(file) == 1
+    assert file[0] == (get_model_file, os.path.basename(get_model_file))
+
+
+def test_get_files_from_path_multiple_files(get_temp_dir_with_models):
+    path, generated_files = get_temp_dir_with_models
+    files = get_files_from_path(path)
+    # It returns the same number of files as were generated, and it is a list tuple of (absolute_path, filename)
+    assert len(files) == len(generated_files)
+    for abs, filename in files:
+        assert abs == os.path.join(path, filename)
+        assert filename == os.path.relpath(abs, path)
diff --git a/clients/ui/bff/internal/api/model_registry_handler_test.go b/clients/ui/bff/internal/api/model_registry_handler_test.go
index 2e37d080..86caa602 100644
--- a/clients/ui/bff/internal/api/model_registry_handler_test.go
+++ b/clients/ui/bff/internal/api/model_registry_handler_test.go
@@ -4,15 +4,16 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+
 	"github.com/kubeflow/model-registry/ui/bff/internal/constants"
 	"github.com/kubeflow/model-registry/ui/bff/internal/mocks"
 	"github.com/kubeflow/model-registry/ui/bff/internal/models"
 	"github.com/kubeflow/model-registry/ui/bff/internal/repositories"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	"io"
-	"net/http"
-	"net/http/httptest"
 )
 
 var _ = Describe("TestModelRegistryHandler", func() {
@@ -38,7 +39,7 @@ var _ = Describe("TestModelRegistryHandler", func() {
 			rr := httptest.NewRecorder()
 
 			By("creating the http request for the handler")
-			testApp.ModelRegistryHandler(rr, req, nil)
+			testApp.GetAllModelRegistriesHandler(rr, req, nil)
 			rs := rr.Result()
 			defer rs.Body.Close()
 			body, err := io.ReadAll(rs.Body)
@@ -52,8 +53,8 @@ var _ = Describe("TestModelRegistryHandler", func() {
 
 			By("should match the expected model registries")
 			var expected = []models.ModelRegistryModel{
-				{Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"},
-				{Name: "model-registry-one", Description: "Model Registry One description", DisplayName: "Model Registry One"},
+				{Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry", ServerAddress: "http://127.0.0.1:8080/api/model_registry/v1alpha3"},
+				{Name: "model-registry-one", Description: "Model Registry One description", DisplayName: "Model Registry One", ServerAddress: "http://127.0.0.1:8080/api/model_registry/v1alpha3"},
 			}
 			Expect(actual.Data).To(ConsistOf(expected))
 		})
diff --git a/clients/ui/bff/internal/repositories/model_registry_test.go b/clients/ui/bff/internal/repositories/model_registry_test.go
index 06ef1262..3aab6b54 100644
--- a/clients/ui/bff/internal/repositories/model_registry_test.go
+++ b/clients/ui/bff/internal/repositories/model_registry_test.go
@@ -19,8 +19,8 @@ var _ = Describe("TestFetchAllModelRegistry", func() {
 
 			By("should match the expected model registries")
 			expectedRegistries := []models.ModelRegistryModel{
-				{Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"},
-				{Name: "model-registry-one", Description: "Model Registry One description", DisplayName: "Model Registry One"},
+				{Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry", ServerAddress: "http://127.0.0.1:8080/api/model_registry/v1alpha3"},
+				{Name: "model-registry-one", Description: "Model Registry One description", DisplayName: "Model Registry One", ServerAddress: "http://127.0.0.1:8080/api/model_registry/v1alpha3"},
 			}
 			Expect(registries).To(ConsistOf(expectedRegistries))
 		})
@@ -34,7 +34,7 @@ var _ = Describe("TestFetchAllModelRegistry", func() {
 
 			By("should match the expected model registries")
 			expectedRegistries := []models.ModelRegistryModel{
-				{Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora"},
+				{Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora", ServerAddress: "http://127.0.0.1:8080/api/model_registry/v1alpha3"},
 			}
 			Expect(registries).To(ConsistOf(expectedRegistries))
 		})
diff --git a/internal/converter/mlmd_converter_util_test.go b/internal/converter/mlmd_converter_util_test.go
index 084d7732..f4f17484 100644
--- a/internal/converter/mlmd_converter_util_test.go
+++ b/internal/converter/mlmd_converter_util_test.go
@@ -270,15 +270,25 @@ func TestMapModelArtifactProperties(t *testing.T) {
 		StorageKey:         of("storage-key"),
 		StoragePath:        of("storage-path"),
 		ServiceAccountName: of("service-account-name"),
+		ModelSourceKind:    of("pipelines"),
+		ModelSourceClass:   of("pipelinerun"),
+		ModelSourceGroup:   of("my-ns"),
+		ModelSourceId:      of("run-id1"),
+		ModelSourceName:    of("my-ns/run-id1"),
 	})
 	assertion.Nil(err)
-	assertion.Equal(6, len(props))
+	assertion.Equal(11, len(props))
 	assertion.Equal("my model art description", props["description"].GetStringValue())
 	assertion.Equal("sklearn", props["model_format_name"].GetStringValue())
 	assertion.Equal("1.0", props["model_format_version"].GetStringValue())
 	assertion.Equal("storage-key", props["storage_key"].GetStringValue())
 	assertion.Equal("storage-path", props["storage_path"].GetStringValue())
 	assertion.Equal("service-account-name", props["service_account_name"].GetStringValue())
+	assertion.Equal("pipelines", props["model_source_kind"].GetStringValue())
+	assertion.Equal("pipelinerun", props["model_source_class"].GetStringValue())
+	assertion.Equal("my-ns", props["model_source_group"].GetStringValue())
+	assertion.Equal("run-id1", props["model_source_id"].GetStringValue())
+	assertion.Equal("my-ns/run-id1", props["model_source_name"].GetStringValue())
 
 	props, err = MapModelArtifactProperties(&openapi.ModelArtifact{
 		Name: of("v1"),
@@ -520,6 +530,76 @@ func TestMapModelArtifactServiceAccountName(t *testing.T) {
 	assertion.Equal("my-account", *extracted)
 }
 
+func TestMapModelArtifactModelSourceKind(t *testing.T) {
+	assertion := setup(t)
+
+	extracted := MapModelArtifactModelSourceKind(map[string]*proto.Value{
+		"model_source_kind": {
+			Value: &proto.Value_StringValue{
+				StringValue: "my-source-kind",
+			},
+		},
+	})
+
+	assertion.Equal("my-source-kind", *extracted)
+}
+
+func TestMapModelArtifactModelSourceClass(t *testing.T) {
+	assertion := setup(t)
+
+	extracted := MapModelArtifactModelSourceClass(map[string]*proto.Value{
+		"model_source_class": {
+			Value: &proto.Value_StringValue{
+				StringValue: "my-source-class",
+			},
+		},
+	})
+
+	assertion.Equal("my-source-class", *extracted)
+}
+
+func TestMapModelArtifactModelSourceGroup(t *testing.T) {
+	assertion := setup(t)
+
+	extracted := MapModelArtifactModelSourceGroup(map[string]*proto.Value{
+		"model_source_group": {
+			Value: &proto.Value_StringValue{
+				StringValue: "my-source-group",
+			},
+		},
+	})
+
+	assertion.Equal("my-source-group", *extracted)
+}
+
+func TestMapModelArtifactModelSourceId(t *testing.T) {
+	assertion := setup(t)
+
+	extracted := MapModelArtifactModelSourceId(map[string]*proto.Value{
+		"model_source_id": {
+			Value: &proto.Value_StringValue{
+				StringValue: "my-source-id",
+			},
+		},
+	})
+
+	assertion.Equal("my-source-id", *extracted)
+}
+
+func TestMapModelArtifactModelSourceName(t *testing.T) {
+	assertion := setup(t)
+
+	extracted := MapModelArtifactModelSourceName(map[string]*proto.Value{
+		"model_source_name": {
+			Value: &proto.Value_StringValue{
+				StringValue: "my-source-name",
+			},
+		},
+	})
+
+	assertion.Equal("my-source-name", *extracted)
+}
+
 func TestMapPropertyModelVersionId(t *testing.T) {
 	assertion := setup(t)
 
diff --git a/internal/mapper/mapper_test.go b/internal/mapper/mapper_test.go
index 5f1b99be..75db4df9 100644
--- a/internal/mapper/mapper_test.go
+++ b/internal/mapper/mapper_test.go
@@ -104,7 +104,7 @@ func TestMapFromModelArtifactsEmpty(t *testing.T) {
 func TestMapFromServingEnvironment(t *testing.T) {
 	assertion, m := setup(t)
 
-	ctx, err := m.MapFromServingEnvironment(&openapi.ServingEnvironment{Name: of("Env")})
+	ctx, err := m.MapFromServingEnvironment(&openapi.ServingEnvironment{Name: *of("Env")})
 	assertion.Nil(err)
 	assertion.Equal("Env", ctx.GetName())
 	assertion.Equal(servingEnvironmentTypeId, ctx.GetTypeId())
diff --git a/pkg/core/core_test.go b/pkg/core/core_test.go
index e625e39a..fbd069c6 100644
--- a/pkg/core/core_test.go
+++ b/pkg/core/core_test.go
@@ -172,11 +172,11 @@ func (suite *CoreTestSuite) registerModel(service api.ModelRegistryApi, override
 }
 
 // utility function that register a new simple ServingEnvironment and return its ID
-func (suite *CoreTestSuite) registerServingEnvironment(service api.ModelRegistryApi, overrideName *string, overrideExternalId *string) string {
+func (suite *CoreTestSuite) registerServingEnvironment(service api.ModelRegistryApi, overrideName string, overrideExternalId *string) string {
 	eutName := "Simple ServingEnvironment"
 	eutExtID := "Simple ServingEnvironment ExtID"
 	eut := &openapi.ServingEnvironment{
-		Name:        &eutName,
+		Name:        eutName,
 		ExternalId:  &eutExtID,
 		Description: &entityDescription,
 		CustomProperties: &map[string]openapi.MetadataValue{
@@ -186,7 +186,7 @@ func (suite *CoreTestSuite) registerServingEnvironment(service api.ModelRegistry
 		},
 	}
 
-	if overrideName != nil {
+	if overrideName != "" {
 		eut.Name = overrideName
 	}
 
@@ -233,7 +233,7 @@ func (suite *CoreTestSuite) registerModelVersion(
 }
 
 // utility function that register a new simple ServingEnvironment and return its ID
-func (suite *CoreTestSuite) registerInferenceService(service api.ModelRegistryApi, registerdModelId string, overrideParentResourceName *string, overrideParentResourceExternalId *string, overrideName *string, overrideExternalId *string) string {
+func (suite *CoreTestSuite) registerInferenceService(service api.ModelRegistryApi, registerdModelId string, overrideParentResourceName string, overrideParentResourceExternalId *string, overrideName *string, overrideExternalId *string) string {
 	servingEnvironmentId := suite.registerServingEnvironment(service, overrideParentResourceName, overrideParentResourceExternalId)
 
 	eutName := "simpleInferenceService"
@@ -388,7 +388,7 @@ func (suite *CoreTestSuite) TestModelRegistryStartupWithExistingEmptyTypes() {
 	})
 	suite.NotNilf(modelArtifactResp.ArtifactType, "model artifact type %s should exists", *modelArtifactTypeName)
 	suite.Equal(*modelArtifactTypeName, *modelArtifactResp.ArtifactType.Name)
-	suite.Equal(6, len(modelArtifactResp.ArtifactType.Properties))
+	suite.Equal(11, len(modelArtifactResp.ArtifactType.Properties))
 
 	servingEnvResp, _ = suite.mlmdClient.GetContextType(ctx, &proto.GetContextTypeRequest{
 		TypeName: servingEnvironmentTypeName,
diff --git a/pkg/core/inference_service_test.go b/pkg/core/inference_service_test.go
index d6b290b3..fd06894f 100644
--- a/pkg/core/inference_service_test.go
+++ b/pkg/core/inference_service_test.go
@@ -17,7 +17,7 @@ func (suite *CoreTestSuite) TestCreateInferenceService() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 	runtime := "model-server"
 	desiredState := openapi.INFERENCESERVICESTATE_DEPLOYED
@@ -70,7 +70,7 @@ func (suite *CoreTestSuite) TestCreateDuplicateInferenceServiceFailure() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 	runtime := "model-server"
 	desiredState := openapi.INFERENCESERVICESTATE_DEPLOYED
@@ -125,7 +125,7 @@ func (suite *CoreTestSuite) TestCreateInferenceServiceFailure() {
 	suite.NotNil(err)
 	suite.Equal("no serving environment found for id 9999: not found", err.Error())
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	eut.ServingEnvironmentId = parentResourceId
 
 	_, err = service.UpsertInferenceService(eut)
@@ -137,7 +137,7 @@ func (suite *CoreTestSuite) TestUpdateInferenceService() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -231,7 +231,7 @@ func (suite *CoreTestSuite) TestUpdateInferenceServiceFailure() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -271,7 +271,7 @@ func (suite *CoreTestSuite) TestGetInferenceServiceById() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	state := openapi.INFERENCESERVICESTATE_UNDEPLOYED
@@ -317,7 +317,7 @@ func (suite *CoreTestSuite) TestGetRegisteredModelByInferenceServiceId() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -346,7 +346,7 @@ func (suite *CoreTestSuite) TestGetModelVersionByInferenceServiceId() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	modelVersion1Name := "v1"
@@ -396,7 +396,7 @@ func (suite *CoreTestSuite) TestGetModelArtifactByInferenceServiceId() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	modelVersion1Name := "v1"
@@ -449,7 +449,7 @@ func (suite *CoreTestSuite) TestGetInferenceServiceByParamsWithNoResults() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 
 	_, err := service.GetInferenceServiceByParams(apiutils.Of("not-present"), &parentResourceId, nil)
 	suite.NotNil(err)
@@ -460,7 +460,7 @@ func (suite *CoreTestSuite) TestGetInferenceServiceByParamsName() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -503,7 +503,7 @@ func (suite *CoreTestSuite) TestGetInferenceServiceByParamInvalid() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -535,7 +535,7 @@ func (suite *CoreTestSuite) TestGetInfernenceServiceByParamsExternalId() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -578,7 +578,7 @@ func (suite *CoreTestSuite) TestGetInferenceServiceByEmptyParams() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut := &openapi.InferenceService{
@@ -608,7 +608,7 @@ func (suite *CoreTestSuite) TestGetInferenceServices() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
-	parentResourceId := suite.registerServingEnvironment(service, nil, nil)
+	parentResourceId := suite.registerServingEnvironment(service, "", nil)
 	registeredModelId := suite.registerModel(service, nil, nil)
 
 	eut1 := &openapi.InferenceService{
@@ -650,7 +650,7 @@ func (suite *CoreTestSuite) TestGetInferenceServices() {
 
 	anotherParentResourceName := "AnotherModel"
 	anotherParentResourceExtId := "org.another"
-	anotherParentResourceId := suite.registerServingEnvironment(service, &anotherParentResourceName, &anotherParentResourceExtId)
+	anotherParentResourceId := suite.registerServingEnvironment(service, anotherParentResourceName, &anotherParentResourceExtId)
 
 	anotherName := "v1.0"
 	anotherExtId := "org.another@v1.0"
diff --git a/pkg/core/serve_model_test.go b/pkg/core/serve_model_test.go
index e9412a34..ab2bd7af 100644
--- a/pkg/core/serve_model_test.go
+++ b/pkg/core/serve_model_test.go
@@ -17,7 +17,7 @@ func (suite *CoreTestSuite) TestCreateServeModel() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion := &openapi.ModelVersion{
 		Name:        modelVersionName,
@@ -81,7 +81,7 @@ func (suite *CoreTestSuite) TestCreateDuplicateServeModelFailure() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion := &openapi.ModelVersion{
 		Name:        modelVersionName,
@@ -124,7 +124,7 @@ func (suite *CoreTestSuite) TestCreateServeModelFailure() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 	// end of data preparation
 
 	_, err := service.UpsertServeModel(nil, nil)
@@ -158,7 +158,7 @@ func (suite *CoreTestSuite) TestUpdateServeModel() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion := &openapi.ModelVersion{
 		Name:        modelVersionName,
@@ -220,7 +220,7 @@ func (suite *CoreTestSuite) TestUpdateServeModelFailure() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion := &openapi.ModelVersion{
 		Name:        modelVersionName,
@@ -267,7 +267,7 @@ func (suite *CoreTestSuite) TestGetServeModelById() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion := &openapi.ModelVersion{
 		Name:        modelVersionName,
@@ -314,7 +314,7 @@ func (suite *CoreTestSuite) TestGetServeModels() {
 	service := suite.setupModelRegistryService()
 
 	registeredModelId := suite.registerModel(service, nil, nil)
-	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, nil, nil, nil, nil)
+	inferenceServiceId := suite.registerInferenceService(service, registeredModelId, "", nil, nil, nil)
 
 	modelVersion1Name := "v1"
 	modelVersion1 := &openapi.ModelVersion{Name: modelVersion1Name, Description: &modelVersionDescription}
diff --git a/pkg/core/serving_environment_test.go b/pkg/core/serving_environment_test.go
index 9e874336..43f3a2be 100644
--- a/pkg/core/serving_environment_test.go
+++ b/pkg/core/serving_environment_test.go
@@ -18,7 +18,7 @@ func (suite *CoreTestSuite) TestCreateServingEnvironment() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:        &entityName,
+		Name:        entityName,
 		ExternalId:  &entityExternalId,
 		Description: &entityDescription,
 		CustomProperties: &map[string]openapi.MetadataValue{
@@ -54,13 +54,27 @@ func (suite *CoreTestSuite) TestCreateServingEnvironment() {
 	suite.Equal(1, len(getAllResp.Contexts), "there should be just one context saved in mlmd")
 }
 
+func (suite *CoreTestSuite) TestCreateServingEnvironmentEmptyFailure() {
+	// create mode registry service
+	service := suite.setupModelRegistryService()
+
+	// use a ServingEnvironment with no params
+	eut := &openapi.ServingEnvironment{}
+
+	// test
+	_, err := service.UpsertServingEnvironment(eut)
+
+	// catch error for required name missing
+	suite.NotNilf(err, "name required to create new serving environment", err)
+}
+
 func (suite *CoreTestSuite) TestCreateDuplicateServingEnvironmentFailure() {
 	// create mode registry service
 	service := suite.setupModelRegistryService()
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:        &entityName,
+		Name:        entityName,
 		ExternalId:  &entityExternalId,
 		Description: &entityDescription,
 		CustomProperties: &map[string]openapi.MetadataValue{
@@ -88,7 +102,7 @@ func (suite *CoreTestSuite) TestUpdateServingEnvironment() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 		CustomProperties: &map[string]openapi.MetadataValue{
 			"myCustomProp": {
@@ -106,7 +120,7 @@ func (suite *CoreTestSuite) TestUpdateServingEnvironment() {
 	createdEntityId, _ := converter.StringToInt64(createdEntity.Id)
 
 	// checks created entity matches original one except for Id
-	suite.Equal(*eut.Name, *createdEntity.Name, "returned entity should match the original one")
+	suite.Equal(eut.Name, createdEntity.Name, "returned entity should match the original one")
 	suite.Equal(*eut.ExternalId, *createdEntity.ExternalId, "returned entity external id should match the original one")
 	suite.Equal(*eut.CustomProperties, *createdEntity.CustomProperties, "returned entity custom props should match the original one")
 
@@ -143,7 +157,7 @@ func (suite *CoreTestSuite) TestUpdateServingEnvironment() {
 	// update the entity under test, keeping nil name
 	newExternalId = "newNewExternalId"
 	createdEntity.ExternalId = &newExternalId
-	createdEntity.Name = nil
+	createdEntity.Name = ""
 	createdEntity, err = service.UpsertServingEnvironment(createdEntity)
 	suite.Nilf(err, "error creating entity: %v", err)
 
@@ -171,7 +185,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentById() {
 
 	// register a new entity
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 		CustomProperties: &map[string]openapi.MetadataValue{
 			"myCustomProp": {
@@ -190,7 +204,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentById() {
 	suite.Nilf(err, "error getting eut by id %s: %v", *createdEntity.Id, err)
 
 	// checks created entity matches original one except for Id
-	suite.Equal(*eut.Name, *getEntityById.Name, "saved name should match the original one")
+	suite.Equal(eut.Name, getEntityById.Name, "saved name should match the original one")
 	suite.Equal(*eut.ExternalId, *getEntityById.ExternalId, "saved external id should match the original one")
 	suite.Equal(*eut.CustomProperties, *getEntityById.CustomProperties, "saved custom props should match the original one")
 }
@@ -210,7 +224,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentByParamsName() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -228,7 +242,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentByParamsInvalid() {
 	service := suite.setupModelRegistryService()
 
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -250,7 +264,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentByParamsExternalId() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -269,7 +283,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentByEmptyParams() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -289,7 +303,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsOrderedById() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -298,14 +312,14 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsOrderedById() {
 
 	newName := "Pricingentity2"
 	newExternalId := "myExternalId2"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	_, err = service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)
 
 	newName = "Pricingentity3"
 	newExternalId = "myExternalId3"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	_, err = service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)
@@ -341,7 +355,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsOrderedByLastUpdate() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -350,14 +364,14 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsOrderedByLastUpdate() {
 
 	newName := "Pricingentity2"
 	newExternalId := "myExternalId2"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	secondEntity, err := service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)
 
 	newName = "Pricingentity3"
 	newExternalId = "myExternalId3"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	thirdEntity, err := service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)
@@ -401,7 +415,7 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsWithPageSize() {
 
 	// register a new ServingEnvironment
 	eut := &openapi.ServingEnvironment{
-		Name:       &entityName,
+		Name:       entityName,
 		ExternalId: &entityExternalId,
 	}
 
@@ -410,14 +424,14 @@ func (suite *CoreTestSuite) TestGetServingEnvironmentsWithPageSize() {
 
 	newName := "Pricingentity2"
 	newExternalId := "myExternalId2"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	secondEntity, err := service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)
 
 	newName = "Pricingentity3"
 	newExternalId = "myExternalId3"
-	eut.Name = &newName
+	eut.Name = newName
 	eut.ExternalId = &newExternalId
 	thirdEntity, err := service.UpsertServingEnvironment(eut)
 	suite.Nilf(err, "error creating ServingEnvironment: %v", err)