Switch the gate from `currentUser.is_admin` to `access.canSeeOrgAdmin`
so Org owners get the same all-keys view (Creator column, creator
filter, `user_id: '*'` default) that platform admins have. Mirrors
the BE's "platform admin OR current-Org owner" gate on listing every
key in scope.
The user picker now fetches `/user-directory` instead of the
admin-only `/users` endpoint, which would 403 Org owners. Rename the
filter placeholder from the misnamed `models.table.filterByName` to
`common.filter.byCreator`.
Opening an org-scoped deployment in the Playground pre-selected the bare
model name (e.g. `qwen3-0.6b`), which never matched the org-namespaced
option the `/v1/models` dropdown actually lists (`org1/qwen3-0.6b`), so
the selection showed an unmatched value.
Resolve the owning org from the route row's `owner_principal_id` (falling
back to the org the caller is currently acting under for the admin "All"
view) and reconstruct the same `{org}/{name}` id the server reports. The
platform org carries no prefix, matching the server's behaviour.
- user.ts: add `getOrgById`/`getCurrentOrg` that scan both org caches
(`organizationList` + admin-only `allOrganizations`) with string-
normalised id comparison; `getOrgNameById` is now a thin wrapper.
Helpers accept `undefined` so optional row fields type-check.
- use-open-playground: build the prefix from the resolved org record,
keying the skip-prefix decision off `is_platform`.
- RouteItem: declare the `owner_principal_id` the list API returns.
- RouteTargetFormItem: drop the duplicate `overridden_model_name`
declaration that TS flagged as a duplicate identifier.
Add the organization picker to the benchmark create form (platform-admin
"all organizations" view). Fetch the chosen org's clusters and keep only
those it owns; refetch the org-scoped model / instance list on org change;
clear a stale cluster or target when switching to an org with no clusters.
Add the organization picker to the model deployment and model-file
download forms (platform-admin "all organizations" view). Filter the
cluster / worker pickers to clusters the chosen org owns, so the created
resource's owner stays aligned with where it runs and a cross-org cluster
can't be selected. Carry owner ids on the cluster / worker options to
drive the filter.
In the platform-admin "all organizations" view, add an organization picker
(the CreateOrgScopeField slot) to the SSH public key, storage type,
storage, GPU instance and instance-template create forms, placed below the
name / display-name fields. Drop the hidden owner field and let the owner
derive from the request context, matching the model-route form.
For GPU instances, scope the instance-type list to clusters the chosen org
owns (client-side, by cluster owner) so an instance can't be scheduled onto
another org's cluster; when the org owns none, show "no instance type
available" and clear the selection, cluster and CPU/memory fields. Instance
templates gain a Global level (NULL owner) and an owner tag on the card.
Add `extraResponseInterceptors` next to the existing request-interceptor
seam and wire it into the request config, so build-time tooling can react
to responses (e.g. clear a one-shot request-scoped hint after a write
completes). Default is an empty list — no behavior change by default.
Rename the api-keys list column from User Name to Creator for clarity and reorder it to sit immediately before the Created column. Add the common.table.creator locale key across all supported languages.
Surface the top-level system_default_container_registry as a dedicated
input (both Docker and K8s), coerce blank to null, and drop its
redundant worker_config YAML hint.
Align the Kubernetes cluster create/edit page with the backend's
promotion of operator/K8s knobs out of worker_config and the removal
of gpuVendorOverrides.
- k8s_options: drop gpuVendorOverrides; add operatorImage, namespace,
and gpuInstanceOptions (presence = GPU instances enabled, carrying an
optional gpuInstancesAccessStaticAddress).
- Add a top-level system_default_container_registry to the cluster type
(promoted out of worker_config on the backend).
- Group all k8s_options fields into a new top-level "K8s Deployment
Options" collapsible section (sibling of Advanced, rendered above it),
with namespace first followed by volume mounts, image credentials,
node selector, operator image, and GPU instances.
- Drive the GPU instances toggle from local state instead of
Form.useWatch (an unregistered nested path never re-rendered, leaving
the switch unresponsive); use antd's borderless Switch and keep the
label/switch grouped together. Namespace gains a gpustack-system
placeholder.
- Remove the gpuVendorOverrides validation from the form and the stale
operator_image / namespace / gpu_instances_access_static_address hints
from the worker_config YAML template and JSON schema.
- Add-worker GPU picker: always allow multi-select for K8s clusters
(runtime node selectors are now auto-derived), dropping the
override-gating, cluster fetch, and single-only hint.
- Update locales (en/zh/ja/ru/tr) for the removed and added keys.
Move the model-route access modal off the deprecated allowed_users
policy/field onto the unified allowed_principals + principals surface,
persisting everything through a single /access POST.
- "specific users" radio now uses allowed_principals; a returned legacy
allowed_users value is normalized so existing routes still select it.
- derive the picker's selection and the full grant set from `principals`
in GET /access (fall back to legacy `items` if a backend doesn't
return principals yet).
- save as `principals`: the principal-based override sends its staged
set; the user picker maps its selection to USER-kind grants and
preserves any non-user grants from the snapshot (no longer sends
`users`).
- guard saving before the GET seeds principals (would wipe grants);
share the ALLOWED_PRINCIPALS_POLICY constant.
- AccessControlFormData: `users` optional, add `principals`.
The two management pages now pass ``mine=true`` to their list APIs.
With multi-tenant cluster_access in place an Org Owner would
otherwise see rows that came in via cross-Org grants — usable but
read-only — and editing/deleting them would 403 from the backend.
Restricting the management lists to rows the caller's scope owns
keeps the UX honest. PV-create / GPU-instance-create pickers still
query without ``mine`` so usable cross-Org clusters and types are
selectable there.
Two changes that together restore the per-row Edit / Delete menu
for non-admin users:
- ``templateActions`` drops the blanket ``access: 'canSeeAdmin'``
gate on every menu item. With the gate on, non-admin callers got
an empty filtered menu, so clicking the "..." did nothing.
- The management page passes ``mine=true`` to the list API. The
endpoint now drops Global rows for non-admin callers (rows they
can't edit anyway), so the dropdown shows up only on rows the
caller may manage. The GPU-instance create picker reuses the
same API without ``mine``, keeping Global presets visible there.
The catalog was gated on ``currentStep === startStep`` so it would
re-appear at step 1 whenever ``providerHint`` skipped the catalog (e.g.
GPU Service's "Add a Kubernetes Cluster" empty-state CTA). Result: the
configure step rendered the catalog *plus* the Name/Description/Advanced
form together, with Kubernetes shown as already selected — confusing
and ugly.
Pin the catalog to step 0 explicitly. ``startStep`` was only used to
seed ``currentStep``, so it's inlined into the ``useState`` initializer
to keep the "skip the catalog when providerHint is set" intent in one
place. Pre-selecting a provider now correctly lands the user straight
on the configure form alone.