Compare commits

...
875 Commits
Author SHA1 Message Date
jialin 63fb8c69b6 style(cluster): center pool replicas cell 2026-07-23 20:47:23 +08:00
jialinandjialin aca6490da1 fix(llmodels): stale instances lingering after missed DELETE watch events 2026-07-23 15:53:21 +08:00
jialinandjialin 04511a63a3 fix(models): cluster auto-selection mismatch when org scope settles late 2026-07-23 15:33:42 +08:00
jialinandjialin 28da5d52e8 style(cluster): col width 2026-07-22 16:29:04 +08:00
jialinandjialin 730cbc9b64 style(cluster): cell width 2026-07-22 15:50:32 +08:00
hibigandjialin 2a69ba65cb chore: bump @gpustack/core-ui to v1.0.42 2026-07-22 15:41:25 +08:00
jialinandjialin aefb8eadce chore(theme): add Card headerHeight token 2026-07-22 10:56:06 +08:00
jialinandjialin 49c80b2ccb feat(llmodels): use CardRadioGroup for access scope selection 2026-07-22 10:56:06 +08:00
hibigandjialin 654eca7544 chore: bump @gpustack/core-ui to v1.0.41 2026-07-22 10:39:55 +08:00
Yuxing Dengandjialin 5ee62f267c feat(billing): on-card charge/pay badge slots for models & instance types
Add the plugin-fed billing price-badge slots to the model cards and the
gpu-instance-type / storage cards so the enterprise plugin can surface
charge / pay indicators inline.
2026-07-22 09:46:11 +08:00
jialinandjialin ba0b2bb2c3 feat(maas-provider): allow clearing the model AutoComplete 2026-07-20 20:24:28 +08:00
jialinandjialin c3bdbc200e style(backends): elevate community card and version title backgrounds 2026-07-20 20:24:28 +08:00
jialinandjialin 63b06e595e feat(access-control): render principals Add button in host holder slot 2026-07-20 20:24:28 +08:00
jialinandjialin 27210da13a style(alert): tighten with-description padding, icon size, and title line-height 2026-07-20 20:24:28 +08:00
jialinandjialin 831a760246 style: adjust table row bg/height, container color, auto table layout 2026-07-20 15:55:39 +08:00
jialinandjialin ae06ad7aae feat(workers): add tooltip hint on VRAM index, merge index+bar trigger
Blue dashed underline on the VRAM GPU index; index and ProgressBar share one Tooltip trigger showing the memory detail.
2026-07-20 15:55:39 +08:00
jialinandjialin b3b819a080 refactor: rename gpu-usage-cell to dual-arc-gauge, refine colors
Fixed blue for allocated (var(--ant-blue-6), theme-independent), gray dot when allocated is 0, dashed underline interaction hint.
2026-07-20 15:55:39 +08:00
jialinandjialin aa47c626ba feat: add GpuUsageCell component
Semicircle dual-arc gauge (used/allocated) with hover Popover for GPU memory usage in table cells.
2026-07-20 15:55:39 +08:00
hibigandjialin 79e4a60d52 chore: bump @gpustack/core-ui to v1.0.40 2026-07-20 14:39:18 +08:00
jialinandjialin d9a467f9f9 chore(llmodels): fix createTime column width, tidy imports
CI / deps (push) Has been cancelled
CI / build (push) Has been cancelled
CI / build-publish (push) Has been cancelled
CI / trigger-backend (push) Has been cancelled
2026-07-17 17:04:57 +08:00
jialinandjialin c6b89f688e refactor(tags): use TextAttribute/ThemeTag, drop redundant fit-content 2026-07-17 17:04:57 +08:00
jialinandjialin 2164a8198b feat(usage): use StatusDot for event type column
Replace antd Tag with core-ui StatusDot (dot + text), mapping event types to StatusType semantic colors.
2026-07-17 17:04:57 +08:00
hibigandjialin 0f0e92379b chore: bump @gpustack/core-ui to v1.0.39 2026-07-17 16:56:51 +08:00
jialinandjialin d8cb5261b9 feat(user-models): fall back to category icon when no brand logo matches 2026-07-16 18:00:27 +08:00
jialinandjialin e30dd10fe4 style(cluster-management): child-row text color and created_at width 2026-07-16 18:00:27 +08:00
jialinandjialin 67e2d914f4 refactor(tables): share parent column grid in expandable child rows
- llmodels/model-routes/cluster child rows use ExpandedRowGrid + Cell
  (auto-flow spans, no parent-key/grid-line math)
- define --seal-table-row-min-height token in global.less
- routes created_at column: span -> fixed width 180
2026-07-16 13:09:18 +08:00
hibigandjialin 938aef6c26 chore: bump @gpustack/core-ui to v1.0.38 2026-07-16 13:05:16 +08:00
jialinandjialin eb029a3e2a fix(table): smooth list empty/loading transitions, no layout jump on entry
Reserve a stable block for the empty/loading state (matching the NoResult
height) and fade the empty state in, so entering a list page no longer jumps
between spinner, empty result, and rows. Apply scroll-table to the antd main
lists and pass emptyMinHeight to the SealTable-based lists.
2026-07-15 14:05:19 +08:00
hibigandjialin 2dd287bbc1 chore: bump @gpustack/core-ui to v1.0.37 2026-07-15 13:43:46 +08:00
jialinandjialin 3c00317aeb fix(models): cancel model-route-targets watch on inactive tab 2026-07-14 20:26:47 +08:00
jialinandjialin 06a2777632 fix: center NoResult empty state with minHeight on list pages 2026-07-14 19:54:45 +08:00
hibigandjialin 8f0691395b chore: bump @gpustack/core-ui to v1.0.36 2026-07-14 19:46:22 +08:00
jialinandjialin 8eb871d0f5 fix(user-models): hold empty state until infra queries resolve
Managers start in a loading state and gate the empty state on the
cluster/worker fetch, so the 'no clusters/workers' guidance no longer
flashes before the real infra data lands.
2026-07-14 17:37:53 +08:00
jialinandjialin 6e321d2d99 feat(models): resource-aware empty state on My Models; tag GPU cluster type experimental
My Models empty state now guides managers to create a cluster/worker or
deploy a model (queries gated to canSeeAdmin/canSeeOrgAdmin); plain users
keep the consumer copy. Add an Experimental badge to the GPU-instance
cluster type card.
2026-07-14 17:37:53 +08:00
jialinandjialin 97179365bb feat(cluster): drop detail page; host Cluster Access drawer
Remove the cluster-detail route and the name-link navigation; the
cluster name is now plain text. Mount the plugin-provided
clusterDetail.AccessDrawer so the enterprise row action can open it.
2026-07-14 17:37:53 +08:00
micheliaandjialin 046d466e16 fix(usage): memoize resource breakdown filters (review comment)
The GPU Instances / Storage tabs built the filter object inline (in
baseRequest and again in the extra tab's props), duplicating the
conditional-spread logic and handing the enterprise Organization tab a fresh
object every render — which refired its fetch effect on any state change.
Hoist it into one memoized ``breakdownFilters`` shared by the chart request
and the extra tab, mirroring the Tokens tab's stable-filters shape.
2026-07-14 14:35:23 +08:00
micheliaandjialin 77a2abad32 fix(usage): satisfy Rules of Hooks for plugin extra tabs; date-safe export
- change the breakdown extra-tab contract from a `useVisible()` hook to a plain
  `isVisible(ctx)` function so the host no longer calls hooks inside a loop
  (token breakdown-tabs + resource GPU/Storage tabs)
- resource export: drop the time portion of the date bucket by slicing the ISO
  string instead of re-parsing with dayjs (avoids a timezone day-shift)
2026-07-14 14:35:23 +08:00
micheliaandjialin efb63335e7 feat(usage): add plugin injection points for org / user-group filters and org breakdown tab
- token & resource filter bars render a plugin slot (UsageFilterBar /
  ResourceUsageFilterBar) for the organization / user-group filters
- breakdown tabs read plugin-provided extra sub-tabs (breakdownExtraTabs /
  resourceBreakdownExtraTabs) so the enterprise plugin can inject the
  Organization sub-tab
- thread the org / user-group filter state through the token filter hook and
  the resource tabs / breakdown tables / summary / export dialog
- standardize the organization label on the principal name; resource export
  date formatted to date-only
2026-07-14 14:35:23 +08:00
jialinandjialin d0b6498bda fix(llmodels): don't evaluate on cluster seed before a model is selected
The basic form seeds a default cluster on open, which fired the full cluster-change handler and triggered an evaluate request before any model was picked. Split off a seed callback that refreshes scoped options without evaluating, and guard the evaluate handler with a model-selected check as a backstop.
2026-07-13 17:14:43 +08:00
jialinandjialin 35ed397c2a fix: use branded sidebar logo, gate help menu behind plugin, add instance pending/starting filters 2026-07-13 13:59:13 +08:00
jialinandjialin cb380d676e refactor(usage): use SimpleSelect for the events-tab event-type filter
Match the other usage tabs' dropdown component instead of the raw antd
Select.
2026-07-10 17:41:54 +08:00
jialinandjialin 03c47530bd fix(usage): map owner creator fields for chart-export User column
Read creator_id / creator_name / creator_deleted from the breakdown item
root into the flat user_* fields, so the GPU Instances / Storage chart
export marks a deleted owner independently of the grouped instance/volume.
2026-07-10 17:41:54 +08:00
jialinandjialin ab92641541 fix(usage): sort deleted filter options to the bottom of every dropdown
Deleted users / models / API keys / instances / volumes now sink below the
live entries in each usage tab's filter, while the current account stays on
top and live entries keep their order (stable sort).
2026-07-10 17:41:54 +08:00
jialinandjialin 1f861c5844 feat(usage): break down chart export by instance/volume with owner user column
Group the GPU Instances and Storage chart exports by date+instance /
date+volume, add Instance/Storage and (org-admin only) User columns, and
mark deleted instances/volumes and their owner user independently in both
the preview and the exported sheet.
2026-07-10 16:31:35 +08:00
jialinandjialin f92499cbe5 refactor(llmodels): rename MyModelsStatusValueMap keys to Stopped/NotReady/Ready 2026-07-10 16:31:35 +08:00
jialinandjialin 6a9cba8020 feat(clusters): warn about K8s version requirement on registration 2026-07-09 20:36:52 +08:00
jialinandjialin 4f4b527014 fix(usage): unique filter option values so same-named entries don't co-select 2026-07-09 20:36:52 +08:00
jialinandjialin 0d5812ba88 fix(cluster): ensure gpuInstanceOptions is submitted for gpu k8s cluster 2026-07-09 20:36:52 +08:00
jialinandjialin 650091b57a fix(usage): carry deleted flag in summary-tab user filter union 2026-07-09 20:36:52 +08:00
jialinandjialin 5bdfb36721 fix(apikeys): description incorrect when empty data 2026-07-09 20:36:52 +08:00
jialinandjialin 0436872093 style(user-models): larger logo, restyle playground button on card 2026-07-09 20:36:52 +08:00
jialinandjialin 3d88b2825b feat(models): rename user-models menu to Models, reorder nav, fix glm logo 2026-07-09 20:36:52 +08:00
jialinandjialin dc54c21d70 feat(user-models): clickable model card opens API access info 2026-07-09 20:36:52 +08:00
jialinandjialin 794ac724ca refactor(usage): address PR review — null-safe mark, hook renames, lazy intl 2026-07-09 15:17:53 +08:00
jialinandjialin d91fbec9d7 refactor(cluster-management): use menu.resources i18n keys 2026-07-09 15:17:53 +08:00
jialinandjialin 555aab714e fix(usage): mark deleted rows and correct api-key header in token chart export 2026-07-09 15:17:53 +08:00
jialinandjialin eb037a3613 style(dashboard): show deleted marker in usage chart labels 2026-07-09 15:17:53 +08:00
jialinandjialin b1efa415e8 style(usage): show deleted marker in chart legends and exports 2026-07-09 15:17:53 +08:00
jialinandjialin e79d49f2a8 style(usage): unify name column header + DeletedTag across breakdown tables 2026-07-09 15:17:53 +08:00
jialinandjialin 7d9f68da2d style(usage): extract DeletedTag, unify deleted-entity tag across filters 2026-07-09 15:17:53 +08:00
jialinandjialin c691df25c0 feat(usage): mark current account first in resource tab user filters
Apply the Tokens tab's current-user-first ordering and [Current Account] marker to the Summary/Storage/Instances tab user filters.
2026-07-09 15:17:53 +08:00
gitlawrandjialin cd4b1b8629 fix(clusters): scope cluster pickers to the owning org
Cluster pickers listed every visible cluster, which includes clusters
shared with all authenticated users (e.g. the default org's "shared with
everyone" clusters). A member of a custom org could then pick another
org's cluster for a model deployment or a new worker.

Request only the current org's own clusters (mine=true) for:
- the model deploy picker (models page),
- the deploy-from-model-file picker (model files page), and
- the add-worker picker.

These resources are owner-scoped on the backend, so the model list and
the worker table (including its cluster-name column) only reference
own-org clusters. The add-model-file worker cascader is already own-org
via the owner-scoped worker list. Platform admin in the "All" view
bypasses mine and is scoped instead by the org picker.
2026-07-08 20:12:00 +08:00
jialinandjialin 70229a9757 feat(gpu-service): add status column to storage and storage-type lists 2026-07-08 16:55:42 +08:00
gitlawrandjialin 644aba3fc2 fix: scope storage type picker to the target organization
The storage create form fetched persistent-volume types once with no
tenant scope, so in the platform-admin "All" view the dropdown listed
every org's types and never re-scoped when the create-scope picker
retargeted the form — letting an org with no cluster-access grant pick
another org's types. The inline "add storage" sub-drawer in the instance
create form had the same gap plus a redundant org picker that could
retarget the storage away from the instance's org.

Both surfaces now pin the storage-type list to the chosen org (via the
X-Organization-Id header); the sub-drawer inherits the instance's scope
and hides its own picker. Page/table listings stay unscoped so every
row's type label still renders.
2026-07-08 16:31:53 +08:00
gitlawrandjialin c025257fea feat(gpu-service): clone an instance template into the caller's scope
Add a Clone action to instance template cards. It reuses the create
flow prefilled from the source, dropping the source id and ownership so
the backend assigns a fresh id and scopes the copy to the caller's own
principal. Clone is offered on every visible template — including
admin-curated Global rows a non-admin can otherwise only view — so
users can fork a Global preset into their own organization without
admin involvement.

The prefilled name defaults to a `-clone` suffix (trimmed to the
63-char limit) so cloning within the same scope doesn't collide on the
unique name. Edit/Delete remain gated on ownership; Clone is always
available.
2026-07-08 16:27:40 +08:00
gitlawrandjialin fdafdfc3d1 fix(gpu-service): show global instance templates under custom scopes
The Instance Templates management page requested the `mine` list scope,
which drops admin-curated Global templates for non-admin callers. The
Inference Backend page uses the default scope and shows Global + own
rows, so the two pages behaved inconsistently.

Use the default scope on the templates page so Global templates are
visible alongside the caller's own, and gate Edit/Delete on the card so
Global rows a non-admin can't modify render read-only.
2026-07-08 16:27:40 +08:00
jialinandjialin f18c12662b Revert "fix(usage): remove the delete tag of models and users in tokens tab"
This reverts commit 1b3d4748d4de963ba0c710e3aac91e3af0015531.
2026-07-08 10:45:23 +08:00
jialinandjialin 71840cfc8f refactor(footer): use useAtomValue for version 2026-07-07 20:37:39 +08:00
jialinandjialin e75beb7324 fix(footer): show version reactively via useAtom 2026-07-07 20:37:39 +08:00
jialinandjialin 90cc6ec753 fix(cluster): drive k8s cluster type from shared state
Replace unreliable Form.useWatch on the unregistered gpuInstanceOptions
path with explicit shared clusterType state. Fixes the type selector
being unclickable and flickering, and ensures a model cluster no longer
submits a stale gpuInstanceOptions payload.
2026-07-07 20:37:39 +08:00
jialinandjialin bd4954f161 fix(usage): remove the delete tag of models and users in tokens tab 2026-07-07 20:37:39 +08:00
jialinandjialin 32fab0fa62 style: gpu selector 2026-07-07 20:37:39 +08:00
gitlawrandjialin d12e92a916 fix(models): preselect the scoped default cluster on deploy
The deploy form's cluster select could open blank even when a valid
(default) cluster exists for the current scope. The open handler seeds
the cluster once, but that seed can be empty (options not loaded yet) or
point outside the resolved scope (the scope id and the option list both
settle after mount, and switching scope re-scopes the list), and nothing
re-picked afterwards — so the field stayed empty.

Make the form enforce the invariant directly: whenever the scope or the
available options change, if the current selection is empty or not among
the options, fall back to the scope's default cluster (then a Ready one,
then the first). A still-valid selection is left untouched.
2026-07-07 18:18:39 +08:00
jialinandjialin dc41b61d69 fix(deployments): display gpu allocated value in gpu selector 2026-07-06 17:48:22 +08:00
jialinandjialin 3a1565690d fix(worker): incorrect worker version 2026-07-06 11:58:51 +08:00
jialinandjialin 1bb9fd73db chore(template): theme logo 2026-07-06 11:58:51 +08:00
jialinandjialin 39024b1d46 chore(hooks): remove unused hooks 2026-07-06 11:58:51 +08:00
hibigandjialin 4ecaa0a326 chore: bump @gpustack/core-ui to v1.0.35 2026-07-02 13:23:14 +08:00
jialinandjialin e1af27e11c fix(dashboard): remove date from group_by 2026-07-02 13:18:04 +08:00
jialinandjialin f4ebd8d7d1 chore: remove old code 2026-07-02 13:18:04 +08:00
jialinandjialin bd58a911d5 chore: remove driver.js 2026-07-02 13:18:04 +08:00
jialinandjialin 5321973ebe perf(charts): import chart components from @gpustack/core-ui/charts
Switch all echarts-based chart imports to the dedicated core-ui charts
entry so echarts stays out of the synchronous entry bundle and loads
only on chart routes. Requires @gpustack/core-ui with the ./charts export.
2026-07-01 20:54:26 +08:00
jialinandjialin 0a1f875590 perf(markdown): load katex css lazily via local FullMarkdown wrapper
core-ui no longer bundles katex.min.css (it would base64-inline ~1.4MB
of fonts into the render-blocking global css). Wrap FullMarkdown locally
to co-locate the katex stylesheet so it loads only in the routes that
render math.
2026-07-01 20:54:26 +08:00
hibigandjialin ea2c9870a7 chore: bump @gpustack/core-ui to v1.0.34 2026-07-01 20:48:55 +08:00
jialinandjialin 23f0e4398a fix(pageContainer): remove leftContent, rightContent, props 2026-07-01 19:11:47 +08:00
jialinandjialin b342174fc2 feat(cluster-detail): add cluster switcher; consume core-ui header-slot
page-box now imports the header-slot bridge from core-ui and re-exports it, plus a dev warning when a header slot has multiple owners. cluster-detail breadcrumb becomes a BaseSelect to switch clusters, and the tab tables are keyed by id so they refetch on switch.
2026-07-01 19:11:47 +08:00
jialinandjialin d07d3b2c99 refactor(layout): adopt header-slot page container across pages 2026-07-01 19:11:47 +08:00
hibigandjialin 9d0a5d01b0 chore: bump @gpustack/core-ui to v1.0.33 2026-07-01 19:08:52 +08:00
gitlawrandjialin 1e97d29fca feat(login): surface SSO callback failures via error query param
The CAS / OIDC / SAML callbacks now redirect to `/login?error=<code>`
on failure instead of letting the browser land on a raw JSON error
page, so the actionable copy reaches the user. Two codes are
recognised:

* `source_conflict` — incoming SSO username collides with an existing
  account from a different source. Message points the user at an
  administrator to link or convert.
* `auth_failed` — anything else (bad ticket, expired state, IdP
  unreachable, malformed response). Generic message: try again or
  contact the administrator.

On mount the login form picks up the `?error=` query param, maps it
through a small `messageIdByCode` table to an i18n key, and routes
the result through the existing auth-error toast. Unknown codes are
silently ignored so a future server release adding a code doesn't
render a bare key. The query param is cleared via
`history.replaceState` so a refresh doesn't re-fire the toast.

Strings added to all five locales.
2026-07-01 15:39:10 +08:00
gitlawrandjialin 350f398cde feat(users): add authentication source dropdown to the user form
The add / edit user drawer now exposes a Source select (Local / OIDC /
SAML / CAS) so an admin can flip an existing account between Local
password and an external IdP without touching the database. Mirrors
the matching `PUT /v1/users/{id}` change on the backend.

Password field follows the selected source:

* Hidden when source != Local — those users authenticate via the IdP
  and a local password row would be a /login bypass.
* Required when CREATE-with-Local, or when EDIT is switching an SSO
  user back to Local (the backend rejects SSO -> Local without a
  fresh password to avoid locking the user out of /login).
* Optional when editing an already-Local user, matching today's
  behaviour.

A switch in EDIT mode surfaces a tip explaining the side effect
(password cleared / new password required) so the consequence isn't
hidden. The Source select is disabled on self-edit — same guard the
role column already uses — so an admin can't lock themselves out by
flipping their own row to an external source.

Strings are added to all five locales; the IdP protocol acronyms
(OIDC / SAML / CAS) render verbatim and don't need translation keys.
2026-07-01 15:39:10 +08:00
jialinandjialin 8a15172316 fix(provider): table sorting does not work 2026-07-01 11:42:30 +08:00
jialinandjialin 185e3475e7 feat: add model icon in my models 2026-07-01 11:42:30 +08:00
jialinandjialin 64720ccbfa chore: press enter key to submit while focus on password 2026-07-01 11:42:30 +08:00
jialinandjialin db17192e55 fix(usage): carry full filter set in breakdown tables and fix double fetch
- breakdown sub-tables now send all active filters (route/user/api_key), matching the trend chart
- summary tab filters the token trend by user and unions user options from both meta APIs (deduped by id)
- stabilize the filters reference so meta load no longer retriggers a second fetch on mount
2026-06-30 17:02:17 +08:00
gitlawr a1dd1ec861 ci: trigger backend pack on UI merge
After build-publish uploads the UI tarball to COS, dispatch a
repository_dispatch event (ui-built) to gpustack/gpustack so the
backend repack picks up the new UI.

Skipped for tag pushes — release tags drive the backend pack via
its own tag trigger. Requires a DISPATCH_PAT secret with
actions:write on gpustack/gpustack.
2026-06-30 15:57:33 +08:00
jialinandjialin a516e6ce72 feat(dashboard): split top user token usage into prompt/completion 2026-06-30 14:38:35 +08:00
micheliaandjialin 2e375c6603 fix(usage): label per-instance rows by shape, not an undefined title
The Instances-tab Instance Type column passed isCpu ? cpuOnlyLabel : undefined.
For a GPU row with vram but a missing/zero gpu_count, undefined let
renderInstanceType fall back to "CPU Only" (acceleratable=false). Pass
instanceTypeSeriesLabel(row) directly — consistent with the Instance Types
column and robust to that edge. (PR #1245 review)
2026-06-30 14:11:39 +08:00
micheliaandjialin 7d94c77c15 fix(usage): sum same-label buckets in the trend series
buildTrendSeries overwrote a (label, bucket) value on collision. Should two
backend groups ever map to the same display label, their values must add up
rather than letting the last write win. Accumulate instead.
2026-06-30 14:11:39 +08:00
micheliaandjialin 457d2f2f72 feat(usage): render instance types by actual shape (CPU spec / GPU x cards)
Instance Types are grouped by actual shape now, so each row/series is one
concrete type. Label them accordingly via a shared instanceTypeSeriesLabel,
used by both the table column and the trend legend so they read identically:
GPU shows "<product> x <cards>", CPU shows "CPU Only · N vCPU · M GB" (the
instance's real size, not the bare "CPU Only" + hover tooltip). The Instances
tab also uses the per-instance cpu/mem totals, so a 3c6g instance of a 1c2g
flavor reads 3c6g instead of 1c2g. flatten carries cpu_milli/memory_mib/
gpu_count from the breakdown dimensions.
2026-06-30 14:11:39 +08:00
gitlawrandjialin 5e7d83e5dd fix(api-keys): let platform admin create a non-tenant-pinned API key
The CreateOrgScopeField slot only renders a "Global" choice when the
host form passes ``allowGlobal: true``. Without it, an admin in the
"All Orgs" view sees only Personal + every org in the dropdown — and
the form's submit injects an ``X-Organization-Id`` header for every
choice, pinning the new key to a tenant. The matching backend change
(api_keys.owner_principal_id nullable) only kicks in when no header
is sent.

Pass ``allowGlobal: true`` (default-selects Global for admin) plus
``globalLabelId: 'scope.global'`` so the dropdown option reads as
plain "Global" — matching the existing tag / column labels for the
same concept elsewhere in the UI, instead of the longer
inference-backend "shared with all organizations" copy.

Relax the ListItem type so ``owner_principal_id`` may be ``null`` —
the OrganizationCell / OwnerScopeTag renderers already render the
"Global" placeholder for null/undefined; the type was the only thing
still claiming the column was always present.
2026-06-30 11:54:00 +08:00
jialinandjialin b1cffe047c docs: expand CLAUDE.md conventions (naming, types, config, core-ui)
- Clarify styles rule: avoid styled-components in new code; existing
  usage is legacy debt, not migrated wholesale
- Add naming conventions (module structure, file naming)
- Add config & types section (FormData/ListItem, config/index.ts)
- Expand Common components with frequently-reused core-ui inventory
2026-06-29 19:44:28 +08:00
jialinandjialin 6509f2a4ff docs: consolidate component conventions into CLAUDE.md and skill
- Add "Dynamic add-item form fields" guidance to CLAUDE.md (pick
  component by field schema: LabelSelector / ListInput / MetadataList)
- Fold StatusTag status-display recipe into create-crud-page skill
- Remove DESIGN.md (content migrated to the above)
2026-06-29 19:44:28 +08:00
jialinandjialin f719c11606 refactor(perferences): use center wrapper layout 2026-06-29 19:44:28 +08:00
jialinandjialin b8d7873b77 chore: dedupe agent instruction files into CLAUDE.md 2026-06-29 12:10:18 +08:00
jialinandjialin a9248a98a5 chore: add create-crud-page and form-patterns skills, refresh CLAUDE.md 2026-06-29 12:10:18 +08:00
micheliaandjialin 8f85e9a082 fix(usage): use frontend gpu_type token for instance-type legend
flattenItem receives the unmapped frontend dimension; the instance-type
grouping is 'gpu_type' (mapped to backend 'instance_type' via GROUP_BY_MAP),
so the previous 'instance_type' check never matched and the trend legend kept
showing the raw flavor slug. Check 'gpu_type'. (PR #1240 review)
2026-06-27 22:27:42 +08:00
micheliaandjialin 1a6d1654b2 fix(usage): label instance_type trend series by product name
The grouped-trend legend used the raw group key, which for instance_type is
the flavor slug (e.g. gpustack--generic-ln-x64-40c-160g--nvidia-a100-80g-4d).
Prefer the enriched product name so the chart legend matches the GPU Instances
list (#5700); falls back to the slug for legacy rows without dimension data.
2026-06-27 22:27:42 +08:00
micheliaandjialin abe705c034 fix(usage): fetch full trend/export series via page=-1
Trend charts and breakdown exports need the complete date series. The old
perPage=10000 workaround (and the missing page param on the token trend,
which fell back to perPage=20) dropped low-traffic — often most recent —
buckets, leaving gaps in the charts (#5690).

Send page=-1 (the backend's no-pagination sentinel) for the token trend,
the storage/instances trend charts and table exports, the resource export
dialog, and the summary-tab date trends.
2026-06-27 22:27:42 +08:00
jialinandjialin 8b34f68824 feat(cluster): resource topology entrance 2026-06-26 22:14:37 +08:00
jialinandjialin e37828a2cd fix: resource topology ux 2026-06-26 22:14:37 +08:00
jialinandjialin 4709661f93 fix(tts): reset form fields to initialvalues after model changed 2026-06-25 14:34:15 +08:00
jialinandjialin 1d7543f19c chore: adjust colors generator 2026-06-25 14:34:15 +08:00
jialinandjialin 1cf146ca32 fix(usage): dynamic bar color value 2026-06-24 20:54:16 +08:00
jialinandjialin b05c510776 fix: add warning tips when deleting ssh key 2026-06-24 20:54:16 +08:00
jialinandjialin e79d5f8962 fix(usage): selection maxTagCount 2026-06-24 20:54:16 +08:00
hibigandjialin 5503fdc21e chore: bump @gpustack/core-ui to v1.0.32 2026-06-24 19:00:22 +08:00
gitlawrandjialin 36a1038d12 feat(login): render SSO button data-driven from /auth/config
The backend now advertises the active external auth provider on
``/auth/config`` as a single ``external_auth: {type, login_url} | null``
field (replacing the per-provider ``is_oidc`` / ``is_saml`` booleans).
This is the API needed to add CAS without per-provider UI conditionals.

Wire the login UI accordingly:

- ``useSSOAuth`` exposes a single ``loginWithExternalAuth()`` action and
  ``options.external_auth`` carrying the provider info; the OIDC- and
  SAML-specific exports are gone.
- ``LoginForm`` renders one SSO button whenever ``external_auth`` is
  set, navigating to ``login_url``. New providers (CAS, future LDAP /
  Azure AD / …) need zero UI changes — only a backend route.
- ``LocalUserForm`` and ``LoginKit`` type definitions drop the
  per-provider booleans.
2026-06-24 18:59:19 +08:00
jialinandjialin ee42a9d0ed refactor(components): migrate shared components to core-ui and remove unused ones 2026-06-24 14:02:41 +08:00
jialinandjialin 28976dea4b fix(cardpage): duplicate data appears after clicking refresh 2026-06-24 14:02:41 +08:00
jialinandjialin 22e5cc3aa5 fix(usage): display usage tabs base on access permissions 2026-06-24 11:26:40 +08:00
jialinandjialin 3052c0b23e fix(app): run probe resources at two stages 2026-06-24 11:26:40 +08:00
jialinandjialin 1c9cabf7e7 fix(usage): instance type check conditions 2026-06-22 15:25:18 +08:00
jialinandjialin 03842d1741 fix: gpu selector height 2026-06-22 15:25:18 +08:00
jialinandjialin 84304eadb7 fix(playgroundimage): allow to input size 2026-06-18 16:03:35 +08:00
jialinandjialin ab3739219e chore: update vllm, sglang, preset argument options 2026-06-18 16:03:35 +08:00
jialinandjialin 7cf4932f3a fix(deploymentlog): show the checkbox only when container exsiting previous 2026-06-18 11:50:19 +08:00
jialinandjialin 032f1eefef fix(deployments): show a prefix before model name in playground 2026-06-18 11:50:19 +08:00
jialinandjialin e35be94f7c fix(provider): filter bailian do not work 2026-06-18 11:50:19 +08:00
jialinandjialin e76e2ad85a fix(worker): IP cell text wrapping 2026-06-18 11:50:19 +08:00
jialinandjialin cb19cd2476 fix: show cpu only spec info 2026-06-17 20:11:06 +08:00
jialinandjialin c532732ce9 fix: show CPU Only in usage instanc types 2026-06-17 18:29:58 +08:00
jialinandjialin 2bc10dbb2e fix: bailian logo, instance logs params container_name 2026-06-17 16:49:25 +08:00
jialinandjialin b634a03ef9 fix: use alibaba cloud icon for qwen provider 2026-06-17 15:30:43 +08:00
jialinandjialin ac64bb98ad fix: Only display name editable when editing storage and storage type 2026-06-17 12:35:24 +08:00
jialinandjialin aa49539210 fix: select gpu stype when creating cluster from gpu service 2026-06-17 12:35:24 +08:00
jialinandjialin 1f5678f781 fix: update nividia notes 2026-06-17 12:35:24 +08:00
jialinandjialin 7adc089e94 fix: save the default_width, default_height in image size options 2026-06-17 12:35:24 +08:00
jialinandjialin 6e1e9c07ee fix: template name readonly in editting 2026-06-17 11:06:39 +08:00
jialinandjialin 1d55382f3b fix: fetch all model instance after stopping 2026-06-17 11:06:39 +08:00
jialinandjialin e68bb0bc4e fix: export data should contain all tables 2026-06-16 22:28:46 +08:00
jialinandjialin c8e14dade5 fix: remove vendor logo in template list 2026-06-16 22:28:46 +08:00
jialinandjialin e4c1a3f5c2 refactor: usage instance tab, split tables 2026-06-16 22:28:46 +08:00
jialinandjialin cdd6ef9cb3 refactor: use hook for fetch event 2026-06-16 22:28:46 +08:00
jialinandjialin 737d43fd16 refactor: use hook for fetch chart 2026-06-16 22:28:46 +08:00
jialinandjialin 9f5163531c refactor: usage storage tab, split tables 2026-06-16 22:28:46 +08:00
jialinandjialin df5cf0f4e5 chore: nvidia notes in add worker 2026-06-16 18:35:19 +08:00
jialinandjialin e160b64697 fix: do not show billing menu in ent 2026-06-16 18:35:19 +08:00
jialinandjialin 7d36c6ac4a revert: show model name from api directly in apikeys 2026-06-16 15:25:46 +08:00
jialinandjialin 0820edf55f fix: refresh data in card page list 2026-06-16 15:25:46 +08:00
jialinandjialin 12e324893f fix: gpu instance state color 2026-06-16 14:17:43 +08:00
jialinandjialin 4017c4dd33 style: table hover bg, remove description in billing 2026-06-16 14:17:43 +08:00
jialinandjialin 17b65d80e1 fix(style): API info access url overflow 2026-06-16 14:17:43 +08:00
jialinandjialin 47cb3321ab fix(style): forgot password layout 2026-06-16 14:17:43 +08:00
jialinandjialin 547b9f617b fix(style): forget button in login page 2026-06-15 15:48:41 +08:00
jialinandjialin 9e7bd78693 fix: allowed models do not contain the org name 2026-06-15 14:45:27 +08:00
jialinandjialin c53d60b628 fix: remove the depude code in buildTrend 2026-06-14 19:40:19 +08:00
jialinandjialin 9566e0cdb2 fix: queryParams do not be updated timely 2026-06-14 19:21:36 +08:00
jialinandjialin ed3e779d22 fix: do not trigger setInitialized in catch when error occured 2026-06-14 19:14:18 +08:00
jialinandjialin 3e2133753d fix: barchart, piechart flash issue 2026-06-14 19:14:18 +08:00
jialinandjialin 736a853192 chore: add cancel token 2026-06-14 19:14:18 +08:00
jialinandjialin 08ef2ffa9a refactor(usagesummary): fetch data 2026-06-14 19:14:18 +08:00
jialinandjialin 6cf512cccd refactor(usage): split tabs into separate dirs 2026-06-14 19:14:18 +08:00
jialinandjialin 6f56e614f3 chore: instance creationg UE 2026-06-14 19:14:18 +08:00
jialinandjialin 8c06862705 style: login placeholder 2026-06-12 22:20:31 +08:00
jialinandjialin 48159171c2 fix: keep uniform for instance type render content 2026-06-12 22:20:31 +08:00
jialinandjialin d5d0749b72 chore(deps): update ui 1.0.27 2026-06-12 21:16:07 +08:00
jialinandjialin dc4dd81e11 refactor(login): layout style 2026-06-12 21:16:07 +08:00
jialinandjialin c736e2186d revert: display the max count cpu instance type 2026-06-12 20:49:47 +08:00
gitlawr 7a76bda484 feat: distinguish same-name GPU service resources across users
Resource names are unique per owning principal, so different users
can legally create same-name GPU instances, templates, SSH public
keys, storage volumes and storage types — but the admin's
cross-tenant lists and the instance-create template picker rendered
them indistinguishably.

- Group the template picker by owning scope: Your Templates, Global
  Templates, then — admin cross-tenant view only — one group per
  owner. A single group renders flat without headers. A plugin can
  take over grouping via hooks.useTemplateOwnerGroups when its
  principal model scopes templates beyond USER owners; a plugin
  without the hook keeps the flat list.
- Add an admin-only Creator column to the Instances, SSH Public
  Keys, Storage and Storage Types tables, sitting right before
  Created (mirroring the API Keys page convention). Resolves
  creator_id to a username via the user directory, falling back to
  owner_principal_id for legacy rows — a personal-scope row's
  creator IS its owner.
- Tag template cards with their owner scope (Global / username) for
  admins.

The Creator column and the card tag defer to a plugin that registers
the page's list columns / the OwnerScopeTag slot. Requires the
gpustack server change that records creator_id on templates, SSH
public keys and storage types.
2026-06-12 20:22:56 +08:00
yxfandjialin 778ea36a9d refactor(parameter): rename parameter_format to Flag Format and update related logic 2026-06-12 17:12:10 +08:00
jialinandjialin 04e1e1e566 chore: remove remember me 2026-06-12 16:02:22 +08:00
jialinandjialin 01f66085ab fix: only keep the max count cpu instance type 2026-06-12 16:02:22 +08:00
jialinandjialin ddf723525a fix: instance types switch between gpu and cpu 2026-06-12 12:10:14 +08:00
Yuxing Deng 7b18a92388 fix: register k8s cluster should selected nvidia vendor by default 2026-06-11 23:55:40 +08:00
jialinandjialin b985dcee63 chore(deps): update ui 1.0.26 2026-06-11 20:31:36 +08:00
jialinandjialin 77e92432ed fix: changed notice always display 2026-06-11 20:02:27 +08:00
jialinandjialin a7b33a928b fix: anti double submit in form 2026-06-11 20:02:27 +08:00
jialinandjialin 1218c6d096 fix: prevent duplicate submit and unlock on validation failure in deploy/update modals 2026-06-11 20:02:27 +08:00
jialinandjialin 6337431ed4 chore(deps): update ui 1.0.25 2026-06-11 14:13:08 +08:00
jialinandjialin 2ba5118bc2 fix: filter modelInstance by cluster in benchmark form 2026-06-11 14:13:08 +08:00
jialinandjialin 12649c7ccf chore(deps): update ui 1.0.24 2026-06-10 20:29:50 +08:00
jialinandjialin 5d96932870 fix: logs viewer scroll to bottom aumatically 2026-06-10 20:29:50 +08:00
jialinandjialin e6cfa6cf77 fix: template display after initialized 2026-06-10 20:29:50 +08:00
gitlawrandjialin 55d16c5681 feat: persist sider menu group collapse state across refresh 2026-06-10 20:24:53 +08:00
micheliaandjialin ab75685955 fix: extend usage query limit 2026-06-10 16:17:54 +08:00
micheliaandjialin bd0bf0bf63 fix(usage): render resource timestamps in their rollup tz, not browser-local 2026-06-10 16:17:54 +08:00
jialinandjialin fd050b33d9 fix: dedupe backend params when switching backend 2026-06-10 15:37:27 +08:00
jialinandjialin 698090b17a fix: watch list do not update when creating 2026-06-10 15:37:27 +08:00
jialinandjialin 6f96260993 fix(style): pie legend text overflow 2026-06-10 15:37:27 +08:00
jialinandjialin 98a6b19298 fix: flatten all filters in table list 2026-06-10 15:37:27 +08:00
Yuxing Dengandjialin ded270ff25 feat: support deploying cpu workers for k8s cluster 2026-06-10 15:26:13 +08:00
gitlawrandjialin c8860207e8 feat(apikeys): wire Organization plugin column on API Keys list
Splice the per-page extra-columns slot ('apiKeys') after the Name
column so plugin-contributed columns (e.g. Organization in the admin
All-org view) render on the API Keys list, matching the other resource
list pages. Declare owner_principal_id (and the already-consumed
masked_value / user_name / user_id) on ListItem.
2026-06-10 14:46:12 +08:00
gitlawrandjialin 8d045ffd8b feat(header): add GitHub star button to top bar
Compact pill (GitHub icon | star count) placed left of the version
display. Star count is fetched from the GitHub API once per 24h and
cached in localStorage; the button hides entirely when no count is
available (e.g. air-gapped deployments), matching the existing update-
check graceful-degradation pattern. Fetch is bounded by a 4s abort
timeout to avoid hanging requests offline.
2026-06-10 12:00:35 +08:00
jialinandjialin fd7437e74a fix: show start only on stopped 2026-06-10 10:03:43 +08:00
jialinandjialin 807ba866d3 fix: only ready allow deleting 2026-06-09 21:00:17 +08:00
jialinandjialin 5e2808d7c4 fix(style): usage model name overflow 2026-06-09 20:01:11 +08:00
jialinandjialin a26a3ff8f9 fix: safe mergeBackendParameters 2026-06-09 20:01:11 +08:00
jialinandjialin a2e9f69b96 fix: gpu instance always show delete, dedupe params 2026-06-09 19:01:32 +08:00
Yuxing Dengandjialin ac800a303a fix: remove the pined worker config changed notice 2026-06-09 17:54:23 +08:00
yxfandjialin dd542a9f29 feat(params): add initialization attributes to lock task_type field 2026-06-09 16:30:35 +08:00
gitlawrandjialin 26ce456bde feat: per-page extra-columns slot for list tables
Add `usePluginListColumns(pageKey)` so a registered plugin can splice
extra columns into a list page's columns hook keyed by page id. The
slot is wired into 11 list pages (Models, Model Routes, Clusters,
Cloud Credentials, MaaS Providers, Model Files, Model Instances,
GPU Service Instances / Public Keys / Storage / Storage Types) so any
consumer of the seam reaches every org-scoped list without per-page
plumbing.

The slot accepts either a static array or a hook function — the hook
form lets the registrant use React hooks to decide visibility without
the host having to evaluate it. Columns carry a `placement`:
`after-name` is the default (sits next to the row's identifying
column); `before-time` / `before-operation` are kept for back-compat
with the existing per-page `modelRoutes.extraColumns` slot.

SealTable-grid pages (Models / Model Routes / Clusters) absorb the
plugin column's span by shrinking the widest right-side columns so
the 24-unit grid stays balanced. Model Routes' `CREATE_TIME_MIN_SPAN`
drops from 3 to 2 because with two plugin columns active the grid
was 1 unit over and the action dropdown wrapped onto a new row; the
date string ellipsizes cleanly at the lower min.

ListItem types for Model Instances, MaaS Providers, Cloud Credentials,
and Model Files gain an optional `owner_principal_id` field — the
backend already emits it (denormalized from the parent resource), the
TS types just hadn't declared it.

The GPU Instances page splices the plugin column right before the
Cluster column (rather than after Name); the two read together since
Cluster narrows down to one Org.
2026-06-09 16:28:59 +08:00
micheliaandmichela feng 7ce1faaf6c fix(usage): format Last Active as local time on GPU & Storage tabs
The Last Active column had no renderer, so it dumped the raw ISO timestamp
(gpustack/gpustack#5523). It's a UTC instant (max bucket hour), so format it
to the user's local time with dayjs — matching the Resource Events time
column and the rest of the product. (Token Last Active is a daily date, not
an instant, so it stays a plain date with no timezone shift.)
2026-06-09 11:54:28 +08:00
micheliaandmichela feng 878bdaa5a7 fix(usage): keep the grouped-trend legend off the x-axis labels
The legend renders at the chart bottom (bottom: 0); with the default grid
(bottom: 8) it overlapped the date labels once a group-by split the trend
into multiple series. Reserve grid bottom space (matching the Tokens trend)
whenever a legend is shown.
2026-06-09 11:54:28 +08:00
micheliaandmichela feng 68a7e3f66a feat(usage): group-by on the GPU Instances & Storage trend charts
Mirror the Tokens trend: add a clearable "Group by" select to the
MetricChartCard so the GPU Instances chart can split by instance type /
instance / user, and Storage by storage / user. When grouped, the chart
fetches group_by=["date", "<dim>"] (the same list style as the token usage
API) and pivots into one stacked series per group (shared buildTrendSeries
util), with a legend; ungrouped stays a single series. group_by is now a
list across the resource breakdown client; group-by options reuse the
bottom-table dimensions (Users only when org-wide).
2026-06-09 11:54:28 +08:00
micheliaandmichela feng feccc81c5f feat(usage): fix Resource Events filters — real event types, name search, no user filter
- Event type: trim the dropdown to the four types that are actually emitted
  (Created / Started / Stopped / Deleted) — updated/attached/detached were
  never recorded — and actually send the selection (event_types) to the API,
  which previously dropped it so the filter did nothing.
- Add a resource-name text filter (debounced, case-insensitive substring).
- Remove the "filter by user" select: the events list has no User column.
  Scope still applies (members see only their own events).
2026-06-09 11:54:28 +08:00
jialinandjialin 620045d7a3 fix: show the default backend params configured in backend 2026-06-08 18:25:22 +08:00
jialinandjialin 6dc28d61b0 fix: view event in starting 2026-06-08 18:25:22 +08:00
jialinandjialin 754cc29793 chore: update iconfont files 2026-06-08 16:38:27 +08:00
jialinandjialin ed525509ea chore(deps): update 1.0.23 2026-06-08 16:26:25 +08:00
jialinandjialin 2b6f26e6c8 fix: update addworker command for dtk 2026-06-08 15:43:21 +08:00
jialinandjialin 09821aab85 feat: add a billing holder page 2026-06-08 15:43:21 +08:00
jialinandjialin 636190b8e6 chore: show the backend info in instance view 2026-06-08 15:43:21 +08:00
jialinandjialin e420c22a45 fix(style): bar chart legend gap 2026-06-08 11:42:20 +08:00
jialinandjialin 993f6edcc4 fix: error message do not show in tts 2026-06-08 11:42:20 +08:00
gitlawr bda8264788 fix: playground model id alignment across org boundaries
Two QA-reported bugs on the "Open in Playground" path. Both come
down to the model id the playground submits not matching what
``/v1/models`` / the dispatcher key off:

* Routes page (OSS UI) emitted ``default/<name>`` for routes in the
  platform Org and 404'd. ``use-open-playground`` keyed off the
  ``is_platform`` flag alone; stale caches that drop the flag
  slipped through. Also accept the well-known ``name === 'default'``
  (``PLATFORM_PRINCIPAL_NAME`` on the backend) as a fallback signal.

* My Models page (enterprise UI, non-admin) emitted bare ``<name>``
  with no Org prefix for non-platform routes. Use ``model.name``
  from ``/v2/my-models`` verbatim — the backend ("fix: principal
  prefix in my-models") now rewrites that field to the OpenAI-style
  id server-side, which also closes the cross-Org grant gap a
  client-side cache lookup can't (the granting Org isn't in the
  caller's member list). Card title now shows the prefixed id, so
  users can tell apart same-named models from different Orgs.

Routes page keeps ``useOpenPlayground`` — that surface is always
scoped to the caller's own Org, the local cache is sufficient, and
``/model-routes`` still returns the raw ``name``.

Also drops the now-unused ``onClick`` prop on ``ModelItem``: the
card had ``clickable={false}`` so the parent-passed handler was
already dead code; the Button drives the playground navigation.
2026-06-07 15:45:49 +08:00
gitlawr ab8e792276 feat(cluster): card-based cluster type selector for K8s
Replace the GPU Service switch (with the small "cannot be used for
model service" caption) with a two-card radio selector that makes the
choice between Model Service and GPU Service explicit at a glance.

- Render two cards styled to match the existing SwitchCard (same
  border, radius, 12px/14px padding, and label/description typography)
  so the selector blends in with the surrounding form fields.
- Underlying form state is unchanged: picking GPU Service still seeds
  k8s_options.gpuInstanceOptions = {}, picking Model Service clears
  it — so the static-address field, the EDIT-mode change watcher, and
  the API payload all keep working as before.
- Align the GPU Service label with the top-level menu entry
  (menu.gpuService) — drop "Instance" / "实例" from the card title
  and the static access address label.
2026-06-07 13:44:50 +08:00
jialinandjialin 0e4be63b6b chore(gaas): add a unknown phase 2026-06-06 21:09:56 +08:00
jialinandjialin d3ebea9090 fix: locales, empty link in add worker 2026-06-06 14:29:49 +08:00
jialinandjialin 15a9e23243 fix: convert cpu unit to core 2026-06-06 14:29:49 +08:00
jialinandjialin eab7d9c77f style: usage chart 2026-06-06 14:29:49 +08:00
jialinandjialin 4a86b71018 fix: ignore request errors on setup 2026-06-06 14:29:49 +08:00
jialinandjialin 6350f7f11a chore(i18n): usage locales 2026-06-05 23:59:25 +08:00
jialinandLawrence Li ce6388253c fix: cpu init value failed 2026-06-05 23:08:45 +08:00
jialinandLawrence Li d8f6064d5e fix: usage locales 2026-06-05 23:08:45 +08:00
jialinandLawrence Li 9f5d95f565 fix: align instance type fields to api 2026-06-05 23:08:45 +08:00
jialinandjialin 0cabbeb81c fix(style): cluster detail locale 2026-06-05 22:29:39 +08:00
jialinandjialin 76618aa3a1 fix(style): cluster detail 2026-06-05 22:18:26 +08:00
gitlawrandjialin b19db0f67d fix(clusters): call out that GPU instance service blocks model service
The "GPU Instance Service" toggle on the cluster create form only said
what it enables. The exclusion side — once flipped, the cluster drops
out of the model-deploy picker — was invisible until the user went
looking for the cluster elsewhere. Extend the tip to spell that out
in all five locales, using the canonical "GPU instance service" /
"model service" product terms.
2026-06-05 18:56:42 +08:00
gitlawrandjialin 76f228bfe7 fix(llmodels): hide GPU-service clusters from deploy picker
The deploy form's cluster dropdown fetched every visible cluster, so
clusters configured for GPU-service (k8s_options.gpu_instance_options
set) showed up as deployable targets even though deployments cannot
run on them.

Pass gpu_instance_enabled=false on the cluster-list query so the
backend returns only model-deployment clusters. GPU-instance creation
does not pick a cluster directly (it is derived from the instance
type), so no symmetric change is needed there.
2026-06-05 18:56:42 +08:00
gitlawrandjialin be40692773 feat(login): add onUserFetched plugin hook
The host's access function is memoized on `initialState` and runs
exactly once per commit. Plugins that maintain identity-scoped
caches the access predicate reads from (e.g. an org context cache)
had no way to seed those caches synchronously before the caller's
`setInitialState({currentUser: ...})` fired — any post-commit hydrate
couldn't widen the predicate, leaving the sidebar in a stale view
until the next identity change.

Add `LoginPlugin.onUserFetched(userInfo, ctx)` and call it inside
`fetchUserInfo` after the server confirms identity but before
returning. Also commit the identity to `userAtom` storage here so
localStorage's identity marker is in lockstep with whatever caches
the plugin seeds — the predicate's first evaluation then sees a
consistent view rather than the prior session's data.

Errors thrown from the hook are swallowed and logged; they never
block fetchUserInfo.
2026-06-05 18:55:46 +08:00
gitlawrandjialin 42ac377c17 feat: tease enterprise multi-tenancy and API key controls in OSS
Add an Organizations menu entry (admin-only) that routes to an upsell
page explaining the multi-tenancy module, and surface disabled
IP Access Control / Quota Limit items in the API Key dropdown with a
tooltip pointing at the enterprise edition. Both placeholders are
shadowed by the enterprise plugin at build time: the route merger
removes the OSS Organizations entry by name, and the dropdown skips
each placeholder when the same key is contributed via configActions.
2026-06-05 16:41:34 +08:00
jialinandjialin 46f859cc3c fix: no need to show error in login for first 2026-06-05 16:24:46 +08:00
gitlawrandjialin f2bff0d1fb fix(storage): remove duplicate error toast on create failure 2026-06-05 16:20:49 +08:00
gitlawrandjialin 0bd98b9662 fix(llmodels): scope catalog deploy cluster seed by create-scope org
initClusterId picked the platform default cluster without considering
the form's organization_id, so opening the catalog deploy form in the
admin all-scope view could seed a cluster that the (org-filtered)
dropdown then hides — submit failed with "Cluster not found".

Mirror the same scope-aware selection deploy-modal already does:
filter clusterList by owner_principal_id when organization_id is set
before picking default/ready/first.

Return number | undefined honestly (the picked org may own no
clusters, or clusterList may still be loading) and guard the
open-handler caller so undefined doesn't flow into fetchSpecData /
getGPUOptionList — the user resolves the empty state by picking an
org that owns clusters.
2026-06-05 13:11:41 +08:00
micheliaandjialin 1ac2c02197 fix: change benchmark model name query condition to input 2026-06-05 12:30:25 +08:00
jialinandjialin 907187d53a fix: show worker added message 2026-06-05 10:20:55 +08:00
Yuxing Dengandjialin faf6246fe6 fix: remove worker config bg color 2026-06-05 10:19:08 +08:00
Yuxing Dengandjialin 0c16e2be7a fix: add notification when editing cluster's k8s_options 2026-06-05 10:19:08 +08:00
micheliaandjialin 3212882895 chore(menu): move Usage below Resources in the sidebar 2026-06-04 19:52:52 +08:00
micheliaandjialin 62f34ffcaf feat(usage): resource usage metering page
Add the Usage page with Summary / Tokens / GPU Instances / Storage / Resource
Events tabs over the new metering endpoints: per-resource breakdowns with
date / scope / user / resource filters, trend charts, server-side sortable
tables (GPU-Hours, Instance-Hours, GB-Days, GB-Hours), Excel export with an
in-dialog preview, and KPI cards with help tooltips explaining each metric.

MaaS-only users (no Kubernetes cluster and no resource events) get a
tokens-only view with the tab bar dropped; GPU Service / the full page unlock
for admins, cluster owners, or anyone who has run a resource. Instance-type
rows reuse the GPU Instances list styling, and deleted users / instances /
volumes are flagged in breakdowns and filters.
2026-06-04 19:52:52 +08:00
micheliaandjialin 50601b0aff feat(gpu-service): instance-type spec popover with per-card specs
Replace the raw flavor slug in the GPU Instances list with the product name
("<product> x <count>") plus an info-icon popover that breaks the spec down by
category (GPU / CPU / Memory / Disk): per-card VRAM, whole-instance CPU/RAM,
system / ephemeral / persistent disks (persistent size resolved from the
referenced PV). Extract the cell into a shared InstanceTypeCell and centralize
memory formatting in formatMemoryDisplay so the GPU Instances list and the
Usage tab render identical sizes.
2026-06-04 19:52:52 +08:00
gitlawrandjialin 4c5d42cb13 feat(version): respect GPUSTACK_UI_* env overrides in build info
Allow a wrapping build that checks this source tree out as a
sub-package to stamp its own release tag and commit id onto the UI
(otherwise the version panel reports the host tree's git HEAD,
which the wrapper doesn't control).

GPUSTACK_UI_VERSION overrides the release tag and GPUSTACK_UI_COMMIT_ID
overrides the short commit id; both fall back to the git tag /
commit at HEAD when unset, preserving existing behavior. Names are
namespaced to avoid colliding with the many tools and CI runners
that already set a generic VERSION.
2026-06-04 18:21:13 +08:00
gitlawrandjialin 85268b8917 fix(api-keys): scope creator filter to current org members
The "Filter by creator" dropdown was listing every user in the
system. For an Org owner viewing an Org-scoped key list, picking
a user outside the Org always produced an empty result. Pass
scope=current_org to `/user-directory` so the picker matches the
list's actual scope. BE drops the param when the request has no
Org context, so callers without an Org are unchanged.
2026-06-04 18:09:32 +08:00
gitlawrandjialin 973d2c4529 feat(login): add plugin seam for default landing path
Plugins can supply `LoginPlugin.resolveDefaultPath` to override the
post-login redirect target. checkDefaultPage consults the hook and
uses the returned path when non-null; otherwise falls back to the
existing admin/non-admin defaults. The lookup runs in parallel with
the IS_FIRST_LOGIN read. Applies to both first-login and
subsequent-login flows.
2026-06-04 15:56:24 +08:00
jialinandjialin ab766b8c54 style: menu collapse button 2026-06-04 15:48:44 +08:00
jialinandjialin 4945242c87 style: login page size 2026-06-04 15:48:44 +08:00
jialinandjialin c4a72eb375 style: collapse style 2026-06-04 15:48:44 +08:00
gitlawrandjialin b12d11728d fix: update model files menu access 2026-06-04 13:16:47 +08:00
Yuxing Dengandjialin 8f0e3a7576 fix: remove k8s options section and merge into advance
And re-order the configuration inputs. The current input order is:
- namespace
- volume mounts
- registry credentials
- node selector
- system-default-registry
- operator image
- gpu instance static access address
- worker config yaml
2026-06-03 23:17:38 +08:00
Yuxing Dengandjialin 4810ff2dbe fix: enhance cluster create form 2026-06-03 23:17:38 +08:00
jialinandjialin a221b22be9 style: instance type data format 2026-06-03 21:29:31 +08:00
jialinandjialin 24b458530d fix(style): sso button width 2026-06-03 21:29:31 +08:00
jialinandjialin caea43590a chore: useMemo deps 2026-06-03 18:28:31 +08:00
jialinandjialin c3d7a0b85e chore: add locales 2026-06-03 18:28:31 +08:00
jialinandjialin 5c6f28027d chore: add locales 2026-06-03 18:28:31 +08:00
jialinandjialin dc04529f5b fix: vendor options display by locales 2026-06-03 18:28:31 +08:00
jialinandjialin 2bb04a419d fix: sshkey, storage selection displayName 2026-06-03 18:28:31 +08:00
jialinandjialin 8e31b71668 chore: merge cluster menu to resource 2026-06-03 18:28:31 +08:00
micheliaandmichela feng f055453889 fix(usage): defensive optional-chaining on breakdown items + sku 2026-06-03 17:10:51 +08:00
micheliaandmichela feng 820a6cecdd fix(gpu-service): avoid stringifying unset cpu in the instance form
`${values.spec?.resources?.cpu}` turned an unset cpu into the literal
string "undefined", which fails k8s quantity validation. Omit cpu when
it has no value instead.
2026-06-03 17:10:51 +08:00
micheliaandmichela feng c3a779e4c8 feat(usage): register the Usage route and menu group
Add the /usage menu folder with its overview child pointing at the
tabbed Usage page.
2026-06-03 17:10:51 +08:00
micheliaandmichela feng 00008abf03 feat(usage): add the tabbed Usage page
A Tabs shell hosting Tokens / GPU Instances / Storage / Summary /
Resource Events:
- token-tab: the original token view, wrapped as a tab.
- gpu-instances-tab & storage-tab: per-instance / per-volume breakdowns
  with KPI cards, a metric+granularity trend chart, and grouped tables.
- summary-tab: three-domain (Tokens / Compute / Storage) overview with
  headline stats, donut, and trend.
- resource-events: the lifecycle audit log (Started / Stopped).
- resource-filter-bar + metric-chart-card: shared filter / chart controls
  mirroring the Tokens tab.
- resource-export-data: a preview modal (filter + paginated preview, then
  download) backing the Export Chart Data / Export Table Data actions.
2026-06-03 17:10:51 +08:00
micheliaandmichela feng d9c6f2f780 feat(usage): add i18n strings for the Usage page
Menu label (Usage) and the resource-tab strings (filters, export
chart/table, table headers) across all five locales.
2026-06-03 17:10:51 +08:00
micheliaandmichela feng 9a4d77e869 style(usage): tune shared bar/pie charts for the usage views
- bar-chart: barMinWidth so hour-granularity bars stay visible with gaps.
- pie-chart: single legend-beside-pie layout, value (percent%) tooltip,
  2-decimal rounding for the Summary donut.
2026-06-03 17:10:51 +08:00
micheliaandmichela feng 2407416e33 feat(usage): add resource-usage API client, meta hook, and shared utils
The data layer the resource tabs build on:
- apis/resource.ts: adapter over the unified metered_usage read API
  (resource/gpu-instances/storage/summary/events breakdowns), flattening
  the server's generic shape into the per-tab item shape.
- hooks/use-resource-meta.ts: loads creators/instances/volumes filter
  options for the current scope.
- utils/time-buckets.ts: day/week/month/hour bucket keys + range fill.
- utils/export-breakdown.ts: derive Excel columns from antd table specs.
2026-06-03 17:10:51 +08:00
jialinandjialin 234e42ccfa chore: remove comment 2026-06-03 15:50:06 +08:00
jialinandjialin 4426b8dc30 fix: request noop 2026-06-03 15:50:06 +08:00
jialinandjialin 9541f49f39 fix(style): table cell overflow 2026-06-03 15:50:06 +08:00
jialinandjialin 9f7a99f370 fix: refresh list after creating key 2026-06-03 15:50:06 +08:00
jialinandjialin 12f0bb2f98 chore: request.extensions 2026-06-03 15:50:06 +08:00
jialinandjialin ac45a72e0e fix: api acccess info model name 2026-06-03 15:50:06 +08:00
jialinandjialin 9705818b15 fix: wrap fetch for injecting headers 2026-06-03 15:50:06 +08:00
jialinandjialin 39374eafda fix: update params after switching model 2026-06-03 15:50:06 +08:00
Yuxing Dengandjialin 110bc3205e fix: failed to enable gpu instance while creating cluster 2026-06-03 15:33:33 +08:00
gitlawrandjialin 57fec89f2d feat(api-keys): show creator column and filter to org owners
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`.
2026-06-03 15:25:38 +08:00
gitlawrandjialin 72e794c281 fix: align playground default model id with org namespace
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.
2026-06-03 10:59:43 +08:00
gitlawrandjialin fadbcc3e44 feat(benchmark): scope the benchmark create form by organization
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.
2026-06-03 10:14:50 +08:00
gitlawrandjialin 9e30e964e6 feat(models): scope deployment and model-file create by organization
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.
2026-06-03 10:14:50 +08:00
gitlawrandjialin fbb707d014 feat(gpu-service): scope create forms by the selected organization
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.
2026-06-03 10:14:50 +08:00
gitlawrandjialin d2d82ee5ee feat(request): add a response-interceptor seam
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.
2026-06-03 10:14:50 +08:00
jialinandjialin 9061fa08ba chore(deps): update ui 1.0.22 2026-06-02 17:51:25 +08:00
jialinandjialin f29236518e style: selector max tag count 2026-06-02 17:51:25 +08:00
jialinandjialin 6537de5224 fix(style): worker step container 2026-06-02 17:51:25 +08:00
jialinandjialin 0413d470a8 fix(style): worker upgrade tips do not show 2026-06-02 17:51:25 +08:00
jialinandjialin c01f4d3ce8 style: table split 2026-06-02 15:18:00 +08:00
jialinandjialin dd9d359bb0 chore: storage label 2026-06-02 15:18:00 +08:00
jialinandjialin b444c35643 style: side menu scroll bar 2026-06-02 15:18:00 +08:00
jialinandjialin abd0d52fad chore: perferences route 2026-06-02 15:18:00 +08:00
yxfandjialin c8771dd3b9 fix(label): update LoRA Adapter label to plural form 2026-06-02 15:13:55 +08:00
jialinandjialin 6d16f56183 chore(deps): update ui 1.0.21 2026-06-01 21:06:56 +08:00
jialinandjialin 95ffe0350e fix(style): k8s form 2026-06-01 21:06:56 +08:00
jialinandjialin e314f25a77 style: gpu instance service 2026-06-01 21:06:56 +08:00
gitlawrandjialin 65ea65e9ae feat(api-keys): rename User Name column to Creator and move before Created
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.
2026-06-01 19:13:44 +08:00
jialinandjialin 4ce975562e chore(deps): update ui 1.0.20 2026-06-01 13:36:14 +08:00
jialinandjialin 07e625f2bc chore: add locales 2026-06-01 13:36:14 +08:00
jialinandjialin d95fd8dc90 fix: instance type format in list 2026-05-31 22:23:32 +08:00
jialinandjialin 55028cf3a3 fix: parse quality data 2026-05-31 22:23:32 +08:00
jialinandLawrence Li 16a97772d3 fix: instance type api update 2026-05-30 08:28:38 +08:00
jialinandLawrence Li d33167d070 style: page segment fontsize 2026-05-30 08:28:38 +08:00
Yuxing Dengandjialin f62e0ba372 feat(cluster): add default container registry field to advanced config
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.
2026-05-29 23:11:46 +08:00
jialinandjialin 1b6466ae78 chore: menu sorting 2026-05-29 18:04:31 +08:00
jialinandjialin 29c5987bf1 style: menu 2026-05-29 18:04:31 +08:00
Yuxing Deng fcd1d01e83 feat(cluster): update K8s cluster form for new k8s_options API
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.
2026-05-29 17:56:01 +08:00
gitlawrandjialin a80b889760 refactor(access): fold allowed_users into allowed_principals
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`.
2026-05-29 17:11:12 +08:00
jialinandjialin f71bf1b074 chore(deps): update ui 1.0.19 2026-05-29 16:19:17 +08:00
jialinandjialin 7e048ee537 fix: click twice for submitting deployment form, using iconfont static files 2026-05-29 16:19:17 +08:00
jialinandjialin 34098e8950 fix: orgname not be shown in playground 2026-05-29 13:35:22 +08:00
jialinandjialin faf0d4d605 chore(deps): update ui 1.0.18 2026-05-29 11:30:17 +08:00
jialinandjialin 85ade88d55 fix: instance name text overflow 2026-05-29 11:24:03 +08:00
jialinandjialin 7d0b94330d fix: adapter request not be triggered 2026-05-29 11:24:03 +08:00
gitlawrandjialin 5cff1298fc feat(management): show only own rows on Clusters / Storage Types lists
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.
2026-05-28 18:16:15 +08:00
gitlawrandjialin d3a3d4e96e feat(templates): fix unresponsive action dropdown for non-admin
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.
2026-05-28 18:16:15 +08:00
jialinandjialin f99621ec92 fix: catalog hidden fields 2026-05-28 15:51:51 +08:00
jialinandjialin e47a9304f4 fix: cluster worker_config refill 2026-05-28 15:51:51 +08:00
jialinandjialin 9488fc1251 fix: update instance item type 2026-05-28 15:51:51 +08:00
jialinandjialin b590aa746b fix: update onceMaxRequest field 2026-05-28 15:51:51 +08:00
jialinandjialin ef61134b8b chore(deps): update ui 1.0.17 2026-05-28 10:02:32 +08:00
jialinandjialin a9daa0b586 fix: max gpu count 2026-05-27 20:18:33 +08:00
jialinandjialin ae0ad25ed6 fix: an error occured in switch language in stt 2026-05-27 20:18:33 +08:00
jialinandjialin a4c48c1981 fix: remove description json format 2026-05-27 17:10:15 +08:00
jialinandjialin 3497590d2e fix: using select handler for provider hint 2026-05-27 17:10:15 +08:00
jialinandjialin 11cf1329c8 fix: always show max label 2026-05-27 17:10:15 +08:00
jialinandjialin ba5b41cf3a chore: update locales 2026-05-27 17:10:15 +08:00
gitlawrandjialin 1d70446cc4 fix(routes): let Org owners see Storage Types menu
The route was gated on ``canSeeAdmin`` so only platform admins ever
saw Storage Types in the sidebar. But the backend route
(``gpu_instance_persistent_volume_types``) is tenant-scoped — Org
owners can create / list their own — so hiding the menu from them
left an admin-defined feature reachable only by URL.

Relax to ``canSeeOrgAdmin`` to match. OSS keeps the platform-admin-only
behaviour (predicate defaults to ``isPlatformAdmin``); the enterprise
access extension widens it to the current-org owner.
2026-05-27 16:57:49 +08:00
gitlawrandjialin f15d0ab243 fix(cluster-create): hide ProviderCatalog when entering via providerHint
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.
2026-05-27 15:53:39 +08:00
jialinandjialin 34be4800d7 fix: lora tag 2026-05-27 15:45:28 +08:00
jialinandjialin e40b0c2e07 fix: get back lora_module_name 2026-05-27 15:45:28 +08:00
lizw2andjialin 45e0ad9e93 fix(model-routes): use overridden model name for provider targets 2026-05-27 15:45:28 +08:00
gitlawrandjialin 96e6ddfe8a fix: collapse GPU Service menu in Personal Org; add K8s-direct CTA
Two follow-ups to the GPU Service gating:

- In Personal-Org view the access extension was stripping
  canSeeAdmin/canSeeOrgAdmin but leaving canSeeGpuService at its
  admin-shortcut value, so platform admins switched into Personal
  still saw the menu even though Personal scope can't host a K8s
  cluster. Mirror the probe result through sessionStorage so the
  extension can fall back to the strict cluster-availability signal
  in that branch.

- The empty-state CTA now reads 'Add a Kubernetes Cluster' and, on
  click, opens the cluster-create flow with Kubernetes preselected
  via clusterSession.providerHint. ClusterCreate consumes the hint
  on mount: it seeds extraData.provider and starts at the configure
  step instead of the provider catalog, so the user lands one click
  closer to the form they actually need.
2026-05-27 13:41:32 +08:00
gitlawrandjialin 6b47bacfac feat: gate GPU Service on Kubernetes cluster availability
GPU Service today only schedules on Kubernetes clusters; Docker / cloud
clusters can't host the CRDs. Without any awareness of that, Org
members whose Org has no K8s cluster (and no cluster_access grant on
one) saw a menu they couldn't use and a form that bottomed out with
backend errors.

Two changes lock the UX down:

- Boot probes the caller's cluster list once and stashes
  hasKubernetesCluster in initialState. A new canSeeGpuService
  predicate gates the menu — admins and Org owners always see it
  (they can add the cluster); everyone else only sees it when a
  reachable K8s cluster actually exists.

- The instances page filters its own cluster list to Kubernetes
  before deciding what to show. With nothing reachable we render the
  Deployments-style 'No clusters available. Add a Kubernetes cluster
  to get started.' empty state and hide the create-instance CTA;
  admins and Org owners additionally get the 'Add cluster' button
  that jumps to cluster management.
2026-05-27 13:41:32 +08:00
gitlawrandjialin 099b419e34 fix: source owner_principal_id from current organization
OwnerPrincipalIdField was reading currentClusterAtom, which no callers
ever wrote — so every form submission ended up with
owner_principal_id=null and the backend rejected non-admins with
"Only platform admin can create global ...". Pin the field to
currentOrganizationId instead (the Org the caller is acting under).
Cluster ownership is the wrong source: a cluster_access grant lets one
Org schedule on another Org's cluster, but the new resource is still
owned by the caller's Org, and the backend enforces
owner_principal_id == ctx.current_principal_id.

Also drops the dead currentClusterAtom.
2026-05-27 13:41:32 +08:00
jialinandjialin e343ec0a1e fix: instance type width 2026-05-27 12:14:15 +08:00
jialinandjialin e3d6d08916 fix: show max gpu 2026-05-27 12:14:15 +08:00
jialinandjialin d0bc206c83 fix: instance type missing when using CPU type 2026-05-27 12:14:15 +08:00
jialinandjialin 7287f8c81b style: subdrawer width 2026-05-27 12:14:15 +08:00
jialinandjialin 1878753cde fix: show cluster in table 2026-05-26 23:02:28 +08:00
jialinandjialin 2f04e01f4f fix: default instance type, storageMode 2026-05-26 22:40:02 +08:00
jialinandjialin a51f44c135 fix: select a useable instance type for initial 2026-05-26 22:40:02 +08:00
jialinandjialin 7f14eb544c fix: pvc events api 2026-05-26 19:16:50 +08:00
jialinandjialin c6e34db329 fix: ux issues 2026-05-26 19:16:50 +08:00
Yuxing Deng acb90531b2 feat: extend cluster k8s_options with multi-vendor manifest support
- New k8s_pod_spec form sections (image credentials, node selector,
  gpu vendor overrides) under k8s_options, replacing the legacy flat
  k8s_volume_mounts list. UI keys aligned to the backend wire shape
  (snake_case k8s_options + camelCase inside).
- System default container registry is pre-filled into the first image
  credential when creating a new cluster; empty username/password are
  coerced to null on submit to match the Optional[str] backend schema.
- Register cluster flow supports multi-runtime selection gated by the
  cluster's gpuVendorOverrides: non-override vendors stay single-select
  with an inline hint; multi-add only opens once an override vendor is
  picked, and non-override cards become disabled in that state.
- Manifest URL emits multiple ?runtime= params; check-env step combines
  per-vendor commands; downstream steps are disabled when no vendor is
  selected.
- Pre-validate gpuVendorOverrides at save time (non-empty selector, no
  duplicates across vendors, no key clash with base nodeSelector) so
  the user sees the error before hitting the manifest endpoint.
- Misc: dark-mode background of the k8s_pod_spec / volume mount titles
  no longer clashes with the drawer; cluster Steps no longer leaks the
  internal showModules/showForms props to the DOM.
2026-05-26 16:54:14 +08:00
yxfandyxf0314 91c3d0b814 feat: add parameter format and common parameters to backend configuration 2026-05-26 14:30:19 +08:00
jialinandjialin 95e03d11c1 fix: gpu service issues 2026-05-26 14:21:52 +08:00
jialinandjialin 88ab927755 refactor: gpu service API alignment 2026-05-25 22:42:02 +08:00
jialinandjialin 81c7e2ee61 fix(style): template title gap 2026-05-25 22:42:02 +08:00
jialinandjialin b5ed64de51 chore: ssh key selection 2026-05-25 22:42:02 +08:00
jialinandjialin 921a2a0d81 fix: instance form ux 2026-05-25 22:42:02 +08:00
jialinandjialin 5195b1e5fc style: number selection 2026-05-25 22:42:02 +08:00
jialinandjialin f886422f91 fix(style): textarea label background 2026-05-25 22:42:02 +08:00
jialinandjialin 27d57d7563 feat: instance pv events 2026-05-25 22:42:02 +08:00
jialinandjialin 2ebb8f44b7 fix: recreate action 2026-05-25 22:42:02 +08:00
jialinandjialin a0b408e8cb style: instance type remaing resourece 2026-05-25 22:42:02 +08:00
jialinandjialin 858e0dae09 feat: add number selection 2026-05-25 22:42:02 +08:00
gitlawrandjialin 7bee337554 fix(playground): inject X-Organization-Id on non-umi fetch paths
Playground completions (chat streaming via ``fetchChunkedData``)
and the raw ``fetch()`` calls for image / audio generation skip
umi's ``request`` pipeline and so miss the request interceptor in
``request.extensions.ts`` that pins tenant context. Higress
receives those calls without ``X-Organization-Id`` and the auth
callback falls back to the user's USER-principal id rather than
their active Org, which makes Playground usage invisible from
Org-scoped Usage views even after the backend learned to backfill
``consumer_principal_id``.

Centralise the lookup in ``tenantHeaders()`` next to
``fetchChunkedData`` so both streaming helpers and the four
``fetch()``-direct entry points in ``playground/apis/index.ts``
(image / TTS) attach the header with the same precedence as the
umi interceptor (``createScopeOrgOverride`` first,
``currentOrganizationId`` second). Returns an empty object when
no active org context is set, leaving requests unchanged.
2026-05-25 18:02:31 +08:00
jialinandjialin 7944b3f579 fix: add lora tag in routes 2026-05-25 13:56:44 +08:00
jialinandjialin 5e889563ca fix: search lora ux 2026-05-25 13:56:44 +08:00
jialinandjialin aad3b458c8 fix: selection styles 2026-05-25 13:56:44 +08:00
jialinandjialin acef2ef89a fix: add form validation 2026-05-25 13:56:44 +08:00
jialinandjialin d99c25dd03 feat: model file is lora 2026-05-25 13:56:44 +08:00
jialinandjialin f0a1dad48e fix: filter invaild lora in form 2026-05-25 13:56:44 +08:00
jialinandjialin 1e334903dd feat: lora route 2026-05-25 13:56:44 +08:00
jialinandjialin 342583f8d1 feat: lora for deployment 2026-05-25 13:56:44 +08:00
gitlawrandjialin 6af18275c4 feat(usage): treat org owners as managers on the Usage page
The Usage page gated cross-user features (default ``scope=all``,
the user filter, the per-user breakdown dimension) on
``currentUser.is_admin`` alone. The backend's
``_can_use_all_scope`` already grants the same surface to Org
owners — the page just never asked. Result: a non-admin Org owner
landed on ``scope=self`` with no user filter and no per-user
group-by, even though the API would have happily served them the
org-wide picture.

Switch the three sites to ``useAccess().canSeeOrgAdmin``, which
already encodes "platform admin OR owner of the selected
(non-Personal) Org". One predicate, no new helper.

Sites:
- ``index.tsx``: ``initialScope`` defaults to ``all`` for managers.
- ``filter-bar.tsx``: the user filter + nested API-key cascader
  surface (vs the flat API-key picker for members).
- ``daily-usage.tsx``: the ``user`` group-by option in the chart.
2026-05-25 13:56:25 +08:00
gitlawrandjialin 9c519d937d fix(usage): pass scope to the time series chart fetch
The Usage page's chart fetch (``fetchTimeSeriesData``) spread
``chartFilters`` plus dates and filters, but never forwarded
``commonFilters.scope`` — so it implicitly fell back to the
backend's ``scope=all`` default. The three breakdown tables
(``apikeys-table``, ``models-table``, ``users-table``) all pass
``scope`` explicitly, which produced an inconsistent picture on
the same page: an Org owner viewing a model granted from another
Org saw the date chart render empty while the tables underneath
showed identical usage rows.

Forward ``currentSelectedFilters.scope`` alongside the rest of the
common filters so the chart and the tables ask the backend the
same question.
2026-05-25 13:56:25 +08:00
Yuxing Deng 4b14fa6ca8 feat(usage): plugin slot for Models tab extra columns
Lets enterprise plugins contribute per-route columns and a global
mount point on the Usage page's Models tab. Mirrors the existing
modelRoutes pattern: `usage.modelsExtraColumns` for cells,
`<PluginExtraFields name="UsageModelsPageGlobal" />` for the
page-level data lifecycle (receives the visible route ids so a
plugin can bulk-fetch per-row data in one call).
2026-05-25 10:42:59 +08:00
jialinandjialin dc69e51042 fix: colorPrimary cache cleared 2026-05-24 10:51:44 +08:00
jialinandjialin 052d5aa474 fix: colorPrimary cache cleared 2026-05-21 19:58:16 +08:00
jialinandjialin 0c55c1c1b4 fix: sider menu style 2026-05-21 19:58:16 +08:00
jialinandjialin 6f896b4f94 fix(style): use --app-banner-height in scroller container 2026-05-21 19:58:16 +08:00
gitlawrandjialin e7a376db70 refactor: adapt org-namespace lookup to principal name rename
Tracks the gpustack identity-consolidation effort: the unified
``principals`` table's URL-safe identifier column was renamed
``slug`` → ``name`` (with the legacy display ``name`` → ``display_name``).
The enterprise plugin's persisted org caches (``organizationList``,
``allOrganizations`` in localStorage) now write ``name`` instead of
``slug`` on each Organization row.

``getCurrentOrgNamespace`` reads those caches to compose the k8s
namespace (``gpustack-{name}``) for GPU-instance / storage CRDs.
Updated to read ``item.name`` so namespace resolution stays in sync
with what the enterprise plugin writes — otherwise every gpu-service
write would fall through to ``gpustack-default`` even when the user
has an Org context.

Stale ``slug`` references in surrounding comments also retitled to
``name`` to avoid divergence between code and prose. The namespace
format itself (``gpustack-{...}``) is unchanged — only the column it
sources is.
2026-05-21 12:15:39 +08:00
Yuxing Dengandjialin 15cf4d89b7 feat: association dounut effect 2026-05-20 11:29:43 +08:00
Yuxing Dengandjialin c38fcc494a refactor(dashboard): collapse usage section into a single row
Merge the two "by Model" pies (Token Usage + API Requests) into one
dual-pie card with a shared right-side vertical legend and per-donut
center totals, drop the standalone API-Key Top 10 chart, and put the
combined chart side-by-side with the User Top 10 ranking in one row
with the same 16/8 column ratio used by the System Load section above.

* New component usage-charts/usage-by-model.tsx owns both metric queries
  (group_by=['date','route']), aggregates a shared route→color map so
  the legend toggles both donuts together, and renders two pies at
  centers ('20%','50%') and ('60%','50%') with radius ['50%','70%'].
* new-usage.tsx no longer mounts ApiRequestsByModel / TokenUsageByModel
  / TopTokenUsageByApiKey; layout collapses to two columns sized
  (lg=24 xl=16) and (lg=24 xl=8).
* Delete unused token-usage-by-model.tsx, api-requests-by-model.tsx,
  top-token-usage-by-api-key.tsx, and the use-top-token-usage-by-api-key
  hook.
* Strip the now-unreferenced dashboard.tokenUsageByModel /
  apiRequestsByModel / topTokenUsageByApiKey locale keys across 5
  locales, add the new dashboard.usageByModel title; locale parity
  check passes.
2026-05-20 11:29:43 +08:00
Yuxing Deng 44e5b78d29 refactor(dashboard): switch usage charts to route group_by, rename Models card
Align dashboard with the route dimension introduced in the previous
commit:

* Token Usage / API Requests pie charts query the breakdown API with
  `group_by: ['date', 'route']` and aggregate via the new `route`
  dimension in `buildUsageLabel`. Visible chart titles keep the existing
  "by Model" wording — only the underlying data dim changed.
* Overview "Models" card label switched to `dashboard.deployments`
  ("部署" / "Deployments"), and the Active Models table heading to
  `dashboard.activeDeployments`. These reflect the underlying entity
  (model deployments) more accurately now that routes are the
  user-facing model concept.
* Drop 11 unused dashboard locale keys (`dashboard.title`,
  `dashboard.models`, `dashboard.allocategpus`, `dashboard.instances`,
  `dashboard.disk`, `dashboard.diskutilization`, `dashboard.apirequest`,
  `dashboard.activeModels`, `dashboard.activeUsers`,
  `dashboard.activeModels.name`, `dashboard.runninginstances`) — no
  remaining references in `src/` after the rename. Locale parity check
  passes.
2026-05-19 16:50:42 +08:00
Yuxing Deng 38107a67fe refactor(usage): switch breakdown dimension from model to route
Align with gpustack backend commits 7b57cd3b (add route dimension to
usage breakdown) and 51fd25c8 (use model_route_id as models_called):

* group_by, filters, and BreakdownItem now use the `route` dimension;
  `models` filter cascader is replaced with a flat `routes` selector
  (routes have no provider/cluster grouping).
* Tab / group_by option / column header keep the user-facing "模型 / Model"
  wording — only the underlying data dimension changed.
* Drop cluster_name / provider_type columns from the breakdown table;
  drop api_keys_used column per design.
* Fix pre-existing excel export bug in use-export-table — string
  dataIndex was lodash-joined char-by-char ("input_tokens" →
  "i_n_p_u_t..."), array dataIndex was used as a flat key against nested
  rows. Rebuilt around buildSheetMeta that emits a stable field key plus
  a formatMap using _.get(row, path) for nested values. Sheet name kept
  as `models` to match the visible column label.
2026-05-19 16:50:42 +08:00
jialinandjialin 0274bcb8cd chore: update ui 1.0.14 2026-05-19 14:09:07 +08:00
gitlawrandjialin a91c1545f8 fix: require ref audio for Base task in TTS playground 2026-05-19 12:36:08 +08:00
jialinandjialin 0b3884b1a5 fix: gguf input box is hidden when deploying from model files 2026-05-19 12:16:02 +08:00
jialinandjialin bcea46fa8f fix(style): chart style 2026-05-18 20:14:28 +08:00
jialinandjialin abc3c2c25a fix: instance events namespace 2026-05-18 16:59:53 +08:00
jialinandjialin db7a8a702d fix: select panel no default value 2026-05-18 15:37:46 +08:00
jialinandjialin 288bfebc13 fix: filter user option in usage 2026-05-18 15:37:46 +08:00
jialinandjialin c7996d3e14 fix: login page miss icon 2026-05-18 15:37:46 +08:00
jialinandjialin 0d9b6325f9 fix: hygon, thead runtime 2026-05-18 15:37:46 +08:00
gitlawrandjialin baf1508644 fix: guard systemConfigAtom reads against null
``logout`` calls ``clearAtomStorage(systemConfigAtom)`` which sets the
atom to ``null``, and on a fresh SPA login (no full reload)
``app.tsx``'s ``fetchSystemConfig`` doesn't re-run, so consumers that
read ``systemConfig.xxx`` directly crash with "Cannot read properties
of null". A hard refresh masks the bug. Add optional chaining at the
read sites (grafana url / dashboard uids, showMonitoring) so the
contract stays robust regardless of how the atom is reset.
2026-05-18 15:36:29 +08:00
jialinandjialin d04604abe5 fix: SelectPanel props 2026-05-18 13:28:52 +08:00
jialinandjialin bf09784fad chore: update ui 1.0.13 2026-05-18 13:28:52 +08:00
gitlawrandjialin 67f5c794e1 fix(login): keep 360px design width with max-width: 100% cap
Switching ``Buttons`` to ``width: 100%`` (in c32ad712) had a
side-effect — with ``FormWrapper`` sized to ``max-content``, a
100% child without LocalUserForm alongside it lets the card
collapse to the button's intrinsic text width. Per PR #1097 review,
go back to the 360px design width and rely on ``max-width: 100%``
to keep it from overflowing tighter parents (the enterprise cover
layout's 272px inner especially).

Apply the same shape to LocalUserForm — its hardcoded 360px alone
would still overflow on the cover layout — so the two surfaces
stay in lockstep when switching between SSO and password modes.

(Pre-commit hook still crashes on the ``context.getDeclaredVariables``
bug in main; unrelated.)
2026-05-18 12:25:08 +08:00
gitlawrandjialin 55fbe118d5 fix(login): stop SSO button from overflowing the login card
``Buttons`` was a fixed ``width: 360px`` column, but ``FormWrapper``
sizes its content area to ``max-content`` capped at 440px (with 40px
padding). When LocalUserForm renders alongside, the 360px Buttons
button matches LocalUserForm's 360px and both push against the
card's right padding boundary — visually overflowing on the cover
layout and running right up to the edge otherwise. Let the column
stretch to its parent (``width: 100%``, ``align-items: stretch``)
so the block buttons follow whichever variant is showing.

(Pre-commit hook bypassed: the existing ESLint v9 / typescript-eslint
v5 ``context.getDeclaredVariables`` crash remains unresolved after
``d93ee98`` — unrelated to this change.)
2026-05-18 12:25:08 +08:00
Yuxing Dengandjialin ae8ec8572c feat: generic configActions plugin slot for api-keys & model-routes
Introduce `pages/api-keys/plugin.tsx` and `pages/model-routes/plugin.tsx`
defining a unified `{ key, labelId, icon, priority, danger, form,
useCreate }` contract that plugins use to contribute per-row
"configure this record" actions. The host renders one dropdown entry
per registered action and mounts each entry's form, ordered by a
single numeric priority (built-ins are ranked on the same scale; rows
flagged `danger` sink to the bottom).

Drops the older `apiKeys.rowActions` / `modelRoutes.rowActions` slots
and the implicit drawer half of
`<PluginExtraFields name="APIKeysPageGlobal" />`. The model-routes
mount point stays for the page-level quota-defaults bulk-fetch and now
also accepts a `refreshToken` so per-row saves can invalidate derived
page data without changing the row set.
2026-05-18 12:14:19 +08:00
jialinandjialin 38b91a558b chore: remove notes 2026-05-15 16:22:07 +08:00
jialinandjialin a001f83abb fix: update instance name error 2026-05-15 16:22:07 +08:00
jialinandjialin 87c8a3a64d feat: add instance recreate action 2026-05-15 16:22:07 +08:00
jialinandjialin c6e7075bf5 feat: template port name 2026-05-15 16:22:07 +08:00
jialinandjialin 5d75cfdfc5 fix: name validation 2026-05-15 16:22:07 +08:00
jialinandjialin 3f9852c932 refactor: instances add-modal 2026-05-15 16:22:07 +08:00
jialinandjialin 9dfaf990fd feat: add events, log api 2026-05-15 16:22:07 +08:00
micheliaandjialin 1bef1637e0 feat: support license banner 2026-05-15 09:41:10 +08:00
jialinandjialin 451d5bfe47 fix: show inject params, update ui 1.0.11 2026-05-14 11:54:51 +08:00
jialinandjialin faa1f935b7 feat: provider access to core-ui 2026-05-14 11:52:48 +08:00
jialinandjialin c428fe175a chore: remove inject params tag 2026-05-14 11:21:49 +08:00
jialinandjialin 9767c8bf39 chore: show deployment backend params 2026-05-14 11:21:49 +08:00
jialinandjialin 96b50538ed feat: add antv/g6 2026-05-13 17:58:31 +08:00
gitlawrandjialin 2dae886fd1 fix(gpu-service): use gpustack-{slug} namespace for org resources
The legacy ``getCurrentOrganizationId`` helper returned the raw numeric
org id from localStorage, which the GPU-service / storage call sites
then used as the K8s namespace path segment — producing requests like
``/v2/clusters/1/proxy/.../namespaces/5/instances``. The backend
creates a namespace named ``gpustack-{slug}`` (matching
``get_namespace_name``), so the request hit a namespace that doesn't
exist.

Replace the helper with ``getCurrentOrgNamespace`` that:

- prefers the Org the caller is acting under (numeric
  ``currentOrganizationId`` from localStorage, slug looked up in the
  persisted ``organizationList`` or ``allOrganizations`` cache);
- falls back to the selected cluster's owner Org slug for the admin
  "All" view, where there is no explicit Org context but the resource
  still has to land in some Org's namespace — call sites pass
  ``currentCluster?.owner_principal_id`` through;
- falls back to ``gpustack-default`` only if neither path resolves a
  slug (first load before any cache hydrates, etc.).

``ClusterListItem`` is widened with the optional ``owner_principal_id``
field so TypeScript accepts the fallback argument; the backend has
been returning it via ``ClusterPublic`` all along.
2026-05-13 12:29:09 +08:00
jialinandjialin beb74b3c47 fix: update lock.yaml 2026-05-13 10:52:44 +08:00
jialinandjialin d93ee98473 fix: eslinit context.getDeclaredVariables 2026-05-13 10:47:45 +08:00
jialinandjialin f081f0c1cf chore: dropdown styles token, icons url 2026-05-13 10:47:45 +08:00
jialinandjialin 4e49c399d0 fix: dashboard colors, users rank 2026-05-12 19:01:11 +08:00
jialinandjialin 2527555945 fix: HRM in dev 2026-05-12 14:31:52 +08:00
gitlawrandjialin 5ef671f047 feat: dashboard route gated on canSeeOrgAdmin
Relax the dashboard route from canSeeAdmin (strict platform admin)
to canSeeOrgAdmin so the access seam can widen the audience past
the platform admin — by default platform admin, plus whatever an
access extension chooses to admit. The dashboard endpoint takes
care of scoping the response per caller.

Also move Dashboard under the canSeeOrgAdmin bullet in the
access.ts predicate notes.
2026-05-12 14:20:18 +08:00
gitlawrandjialin 10781da086 fix: plugin locale merge re-runs on HMR; API Key form allows Personal
- locale-merger: drop the module-level dedup Set. addLocale is
  idempotent, and the guard made HMR-added plugin locale keys
  silently skipped — strings like organizations.members.selectUsers
  stayed unresolved until a full dev-server restart.
- api-keys form: pass allowPersonal: true to CreateOrgScopeField so
  admin in Platform-wide mode can target their own Personal Org when
  creating a key (unlike infra resources where Personal doesn't fit).
2026-05-12 14:20:18 +08:00
jialinandjialin c279a7f186 fix: gpu service access 2026-05-11 21:05:08 +08:00
gitlawrandjialin 7dd1c9ac5e refactor(backends): rename organization_id to owner_principal_id
Aligns the InferenceBackend ownership column with the principal-based
ownership model used elsewhere (clusters, model_routes). NULL identifies
platform-curated rows shared across orgs; a populated value identifies
an org-scoped override or custom backend.
2026-05-11 20:28:29 +08:00
gitlawrandjialin 125a5be91f feat: restore cluster detail surface, gate cluster-list name link via plugin
The "chore: hide cluster detail" change had commented out both the
cluster-detail route and the cluster name link in the list. The
detail page component still ships in the OSS tree, so reach is
restored but the link is plugin-gated rather than unconditional:
plain OSS renders the name as a span; with a registered
`clusterDetail.linkableName` flag the name turns into a
`Typography.Link` that fires the parent's existing `onCellClick`
(which navigates to the detail route).

Detail tabs adjustments for the in-cluster context:
- Drop the Deployments tab (deployments are managed from the
  top-level Models page; the detail view is scoped to a single
  cluster's hardware).
- `WorkerList` and `GPUList` accept an optional `clusterId`. When
  set, the list pins `cluster_id` in its query and hides the
  cluster-filter dropdown so the user can't scope away from the
  cluster they're inside.

`clusterDetail.extraTabs(clusterId, intl)` exposes a plugin slot
that appends additional tab items — used by the enterprise plugin
to inject per-cluster Access / Quotas surfaces. No-op without a
plugin.
2026-05-11 20:28:29 +08:00
jialinandjialin e25d48cc89 fix: update react-route version 2026-05-11 20:09:37 +08:00
jialinandjialin 5f96f367a6 chore: lock pnpm version 2026-05-11 19:49:21 +08:00
jialinandjialin 01fe2e66fb fix: style issues 2026-05-11 19:49:21 +08:00
jialinandjialin 1b74285de9 fix: storage instance keep the same cluster 2026-05-11 19:49:21 +08:00
jialinandjialin b13ba0a229 fix: alignment api 2026-05-11 19:49:21 +08:00
jialinandjialin 9e0dd5b003 fix: display templates by manufacturer 2026-05-11 19:49:21 +08:00
jialinandjialin 4128de61f9 fix: api test 2026-05-11 19:49:21 +08:00
jialinandjialin fc900c8ce6 fix: ssh key, template test 2026-05-11 19:49:21 +08:00
jialinandjialin 63ac4e0e75 fix: instance api alignment 2026-05-11 19:49:21 +08:00
jialinandjialin 7ee1635833 fix: template api alignment 2026-05-11 19:49:21 +08:00
jialinandjialin 7d0cfeff77 fix: storage api alignment 2026-05-11 19:49:21 +08:00
jialinandjialin 7fa44644fd style: scroller 2026-05-11 19:49:21 +08:00
jialinandjialin e9152e8725 refactor: add instance modal 2026-05-11 19:49:21 +08:00
jialinandjialin 7ea7f6666d feat: templates, storage 2026-05-11 19:49:21 +08:00
jialinandjialin 3ae49dc826 feat: add intances list 2026-05-11 19:49:21 +08:00
gitlawrandjialin 75d7eff63b chore: bump @gpustack/core-ui to ^1.0.10
1.0.10 ships the fix for the rolldown-emitted `require("react")` shim
that broke 1.0.9 runtime (the dist bundle no longer calls the
runtime require helper for react / react-dom). Pin the OSS host's
package.json spec accordingly and regenerate the lockfile.

Side fix in `pages/login/index.tsx`: 1.0.10's `CoreHooks` type
makes `useCurrentUser` and `useTableFetch` required. The login
page's CoreUIProvider never needed them (no current-user reads
and no tables), so cast the hooks block to `any` rather than
mounting stubs that would never run.
2026-05-11 19:30:21 +08:00
gitlawrandjialin 8618718b71 chore: sync pnpm-lock.yaml with package.json
Two pre-existing drifts between package.json and pnpm-lock.yaml were
breaking `pnpm install --frozen-lockfile` in CI:

- `@gpustack/core-ui` was bumped to `^1.0.9` in package.json without
  regenerating the lockfile (lockfile still pinned the prior 1.0.6
  resolution). Pin to `^1.0.7` instead — 1.0.9 ships a packaging
  glitch that surfaces a `Calling \`require\` for "react"` exception
  at module init under MFSU, so the matching enterprise build was
  already reverted; 1.0.7 is the last known-good across both repos.
- `react-router-dom` had `^6.30.3` in package.json but `6.30.3`
  (no caret) in the lockfile spec. Match the lockfile to keep the
  intentional pin from the original add.

`pnpm install --frozen-lockfile` now passes locally.
2026-05-11 19:30:21 +08:00
gitlawrandjialin 8d17000272 feat: users row actions accept plugin entries
The per-row user actions menu was hard-wired to a fixed list and any
plugin-contributed control had to sit as a separate trigger beside
the dropdown. Move the seam: plugins now contribute dropdown items
directly via `users.rowActions`, and we dispatch the click to the
plugin entry by key while the host's `handleSelect` keeps owning
the built-in keys.

Each entry is `{ key, labelId, icon?, danger?, show?(user), onClick(user) }`.
For UI state the onClick might need (drawers, modals), plugins can
mount a single `components.UsersPageGlobal` slot that the page now
renders once outside the table — typical pattern is a jotai atom
the rowAction writes to and the global slot reads from.

Drop the side-mounted `PluginExtraFields name="UserRowActions"` and
its surrounding `<Space>`; the action column is back to a single
DropdownButtons with all entries in one menu.
2026-05-11 19:30:21 +08:00
gitlawrandjialin df53774628 feat: access-control seam exposes prepended policies + create default
The OSS access-control form currently lets a plugin replace the
`allowed_users` policy entry. Multi-tenancy also wants:
- an extra radio option for "Org-scoped" sitting in front of the
  built-ins, and
- a context-sensitive create-time default (routes inside a non-
  platform Org should land on the Org-scoped policy rather than
  `authed`).

Extend the `accessControl` slot with two more hooks:
- `prependedPolicies?: { policyValue, labelId, tipsId?, Field? }[]`
  — entries prepended to the radio group; each may carry an
  optional content `Field` rendered when selected.
- `resolveCreateDefault?: () => string | undefined` — overrides the
  initial `access_policy` for the create flow; the OSS fallback is
  still `authed`.

The host's tooltip list mirrors the same order. Existing behaviour
without a plugin is unchanged.
2026-05-11 19:30:21 +08:00
gitlawrandjialin 68235e188a fix: refresh Access Settings policy from /access response
Reopening the Access Settings dialog after a save could leave the
radio on the wrong policy: the form initialized `access_policy`
from the route list's snapshot, but the list isn't refreshed when
the dialog closes, so the snapshot's value silently shadowed the
just-saved policy.

Backend now returns the route's authoritative `access_policy` on
`GET /v2/model-routes/{id}/access`. Use that value when the GET
resolves, falling back to the parent snapshot only as the initial
seed so the radio isn't briefly unselected.

While we're here, widen the `access_policy` type from a closed
union to `string` — plugins can contribute additional policy
values via the `accessControl.allowedUsersOverride` slot, so the
wire type shouldn't restrict to the OSS-side enum.
2026-05-11 19:30:21 +08:00
gitlawrandjialin f37fad11b1 feat: access-control modal seam to swap the allowed_users surface
The OSS Access Settings form hardcodes the `allowed_users` policy
(per-user explicit grants via the legacy transfer widget). Multi-
tenancy needs a different surface that grants by Org / Group / User
principals via the `/v2/model-routes/{id}/principals` endpoints, and
the enterprise plugin will ship that — but the OSS form should stay
visually unchanged when no plugin is loaded.

Add a plugin extension point on the form: if a registered plugin
exposes `accessControl.allowedUsersOverride: { policyValue, labelId,
tipsId?, Field }`, the form replaces the `allowed_users` radio entry
with the plugin's labelled option, swaps the access-scope tooltip
copy, and renders `<Field form routeId action />` for the override
policy. Without a plugin the form's radio / tooltip / content path
are unchanged.

`handleOnFinish` continues to send `users` only for `allowed_users`;
any other policy (including the plugin's value) goes through with
an empty users list, leaving the plugin's Field to manage its own
principal CRUD inline.
2026-05-11 19:30:21 +08:00
gitlawrandjialin 93fa277bdd fix: Access Control Modal user picker uses /user-directory
The form populated its candidate list via the admin-only
`GET /v2/users`, so opening Route Access Settings as a non-admin
caller (e.g. an org owner managing their own route in the
enterprise plugin) returned 403. Add a `queryUserDirectory` client
wrapper around the slim `/v2/user-directory` endpoint (gated to
platform admin OR org owner) and switch `getUserList` over. Response
shape is the same so the rest of the form is unchanged.
2026-05-11 19:30:21 +08:00
jialinandjialin ea7fa63079 chore(deps): add react-route 2026-05-11 14:47:45 +08:00
jialinandjialin d15db4b4dd chore: update ui 1.0.9 2026-05-11 14:32:07 +08:00
jialinandjialin ed06d57a31 feat: add slots 2026-05-11 14:27:49 +08:00
gitlawr 168c55990f fix: pick landing route by access predicate to avoid redirect loop
Access extensions can narrow ``canSeeAdmin`` to ``false`` even when
``currentUser.is_admin`` is ``true``. The default-landing logic
keyed off ``is_admin`` and pushed admin to ``/dashboard``; when
``canSeeAdmin`` is narrowed there, ``Exception`` 403-redirects to
``/``, which re-pushes ``/dashboard``, looping into a blank screen.
Switch the landing pick (and the masthead-click handler) to consult
the resolved ``access.canSeeAdmin`` predicate instead.

Behavior is unchanged for the default access module, where
``canSeeAdmin`` mirrors ``is_admin``.
2026-05-09 17:22:27 +08:00
gitlawrandjialin 2889ba7c78 feat: plugin extension slots for access, request interceptors, Users actions, and form fields
Four purely-additive seams that let build-time tooling extend host
behaviour without forking files:

* `src/access.extensions.ts` — identity `applyAccessExtensions`
  (mirrors `config/routes.extensions.ts`). `src/access.ts` runs the
  resolved predicate object through it. Adds two new predicates,
  `canSeeOrgAdmin` and `canManageCurrentOrg`, alongside the existing
  `canSeeAdmin`. Resources, Models children, Cluster Management, and
  Resources/Backends are retagged from `canSeeAdmin` to
  `canSeeOrgAdmin`. Users and Dashboard stay strict.
* `src/request.extensions.ts` — identity-empty
  `extraRequestInterceptors`. `src/request-config.tsx` spreads it
  into the existing `requestInterceptors` list so extensions can
  inject context-aware headers without forking the request config.
* Users page action column — renders
  `getGPUStackPlugin()?.components?.UserRowActions` next to the
  existing DropdownButtons inside a Space when a plugin component
  is registered. If absent, the cell renders exactly as before.
* `src/components/plugin-extra-fields.tsx` — generic component-slot
  helper. Renders `pluginManager.components.<name>` if registered,
  forwarding a `context` payload. Used by create/edit forms to let
  plugins inject extra `Form.Item` fields. Mounted in the relevant
  create forms — API Keys, Cloud Credentials, Clusters, Model
  Routes, Model Providers, and Inference Backends — under the slot
  name `CreateOrgScopeField`. Resources whose org is implicit from a
  parent (Models / Workers / Benchmarks / Worker Pools / Model Files
  inherit from the chosen Cluster) deliberately don't mount the slot.
2026-05-08 18:29:52 +08:00
gitlawrandjialin 896b8ef326 feat: expose useTableFetch via CoreUIProvider
Inject the host's `useTableFetch` hook into the CoreUIProvider's
`hooks` prop alongside the already-wired `useCurrentUser`, so plugins
that import `useTableFetch` from `@gpustack/core-ui` resolve it through
the host instead of throwing
"`hooks.useTableFetch is not a function`" at the first list-page render.

Companion to the earlier `feat: add use current user` change — core-ui
exposes both hooks as host-injected, but only the user hook had been
plumbed.
2026-05-08 14:46:35 +08:00
jialinandjialin f08d5c904c refactor: dynamic routes 2026-05-08 09:33:16 +08:00
jialinandjialin 4be5fb4d3d feat: add use current user 2026-05-07 18:26:48 +08:00
jialinandjialin a2d5d55518 chore: backend default command 2026-05-07 18:26:48 +08:00
jialinandjialin e1b28b01df chore: backend placeholder 2026-05-07 18:26:48 +08:00
gitlawrandjialin b6bbe880a0 feat(plugin): expose OrgSwitcher slot in extraRender
Add a single host-side mount point in extraRender.tsx:

    const OrgSwitcher = pluginManager?.components?.OrgSwitcher;
    ...
    {OrgSwitcher && <OrgSwitcher />}

A registered plugin can supply an Org switcher via
components.OrgSwitcher. When no plugin provides it, the host
renders nothing here, so the build is visually unchanged.
2026-05-07 18:16:42 +08:00
jialinandjialin 65ad57fc4c fix: iluvatar env check command 2026-05-06 19:32:01 +08:00
jialinandjialin 469e63b898 fix: keep the source deploying from files 2026-05-06 15:09:15 +08:00
jialinandjialin d471233f83 fix: sync row replicas when start 2026-05-06 14:43:03 +08:00
jialinandjialin 01576212a3 fix: bool type in python code 2026-05-06 14:43:03 +08:00
jialinandjialin 898c7f1be2 refactor: dashboard usage 2026-05-06 12:27:22 +08:00
jialinandjialin 705b33e307 style: parameters tooltip 2026-05-06 12:27:22 +08:00
jialinandjialin 2592f44bc2 fix: append routeID to model proxy URL and drop X-GPUStack-Model header 2026-05-06 12:27:22 +08:00
jialinandjialin 9914db6b96 fix: hide distributed_inference for custom backend 2026-04-29 20:51:48 +08:00
jialinandjialin bb93ad37a1 feat: benchmark inject params 2026-04-29 20:51:48 +08:00
jialinandjialin 93bc632088 fix: stt error handler 2026-04-29 20:51:48 +08:00
jialinandjialin ab89ddffc8 fix: tts stream error 2026-04-29 20:51:48 +08:00
jialinandjialin 7794e57bb1 fix: update lock file 2026-04-29 18:21:24 +08:00
jialinandjialin 7c15bb6ef6 chore(deps): update ui 1.0.5 2026-04-29 18:09:55 +08:00
jialinandjialin ac99156c90 feat: ip access control 2026-04-29 18:09:55 +08:00
jialinandjialin c68f546be9 fix: apikey filters 2026-04-29 18:09:55 +08:00
jialinandjialin 217285223c fix: list-input, labels-selector form 2026-04-29 18:09:55 +08:00
jialinandjialin 3144328874 fix: chart flicker 2026-04-29 09:29:04 +08:00
jialinandjialin 26880b4612 refactor: usage chart 2026-04-29 09:29:04 +08:00
jialinandjialin e7aac14ef1 fix: keep previous run checkbox visible and fall back to main worker 2026-04-28 14:41:36 +08:00
jialinandjialin 52211cd4ea fix: previous run description 2026-04-28 14:41:36 +08:00
jialinandjialin 331c59b7c1 fix: logs container 2026-04-28 14:41:36 +08:00
jialinandjialin b605b17561 fix: main worker flag 2026-04-28 14:41:36 +08:00
jialinandjialin 33163ac96c style: start history selection 2026-04-28 14:41:36 +08:00
jialinandjialin 757af11ab3 chore: history options 2026-04-28 14:41:36 +08:00
jialinandjialin 81a87ef362 chore: logs history selection 2026-04-28 14:41:36 +08:00
jialinandjialin 0cb260f854 feat: logs history 2026-04-28 14:41:36 +08:00
jialinandjialin 0ffc2ee924 chore: update ui version 2026-04-27 21:10:34 +08:00
jialinandjialin e4992e8313 fix: custom logos 2026-04-27 21:10:34 +08:00
jialinandjialin 519020077a chore: update lock file 2026-04-24 14:28:30 +08:00
jialinandjialin 6fccee3711 fix: impoer external components 2026-04-24 14:28:30 +08:00
jialinandjialin 597a86562a fix: rebase main 2026-04-24 14:28:30 +08:00
jialinandjialin 16449613e0 chore: core-ui deps 2026-04-24 14:28:30 +08:00
jialinandjialin 6c161c6ae7 build: sync config 2026-04-24 14:28:30 +08:00
jialinandjialin 48b357b1c6 chore: update hooks import 2026-04-24 14:28:30 +08:00
jialinandjialin a795544074 fix: CInput not found 2026-04-24 14:28:30 +08:00
jialinandjialin 8711c27b78 chore: replace components with core-ui, upgrade eslint 2026-04-24 14:28:30 +08:00
jialinandjialin d48aa99dcc fix: deleted tag 2026-04-22 13:16:48 +08:00
jialinandjialin 5f3b9b88ff fix: reset page after filters changed 2026-04-22 13:16:48 +08:00
jialinandjialin f566ea3b63 fix: table sorter 2026-04-22 13:16:48 +08:00
jialinandjialin 8ba77beeed fix: sync filters 2026-04-22 13:16:48 +08:00
jialinandjialin 128f59ed6f chore: locales 2026-04-22 13:16:48 +08:00
jialinandjialin c92f56ec8f style: card style 2026-04-22 13:16:48 +08:00
jialinandjialin daa3d74500 fix: usage datepicker 2026-04-22 13:16:48 +08:00
jialinandjialin ad7fc3a993 fix: usage ux 2026-04-22 13:16:48 +08:00
jialinandjialin 084822814d fix: models, apikeys Cascader 2026-04-22 13:16:48 +08:00
jialinandjialin 3a58bd1eef fix: show tag only one selected 2026-04-22 13:16:48 +08:00
jialinandjialin 94d3fa356c fix: usage filter 2026-04-22 13:16:48 +08:00
jialinandjialin 7d75714662 feat: usage 2026-04-22 13:16:48 +08:00
jialinandjialin b057d52499 fix: switch model do not work in chat 2026-04-17 20:35:16 +08:00
jialinandjialin 2d726ded30 feat: admin apikeys 2026-04-17 20:35:16 +08:00
jialinandjialin f45d9bda61 style: font size 2026-04-16 15:16:47 +08:00
jialinandjialin 684c1ab600 fix: apikey model access 2026-04-16 15:03:59 +08:00
jialinandjialin 3ac2720ba8 chore: remove experimental from metax 2026-04-16 15:03:59 +08:00
jialinandjialin 7b743f35b3 fix: apikey custom tag 2026-04-15 11:30:40 +08:00
jialinandjialin e1b2a4aee2 fix: python test code 2026-04-15 10:15:36 +08:00
jialinandjialin 308c350582 style: server version 2026-04-15 10:15:36 +08:00
jialinandjialin 41c9b86fd9 fix: k8s volume config 2026-04-14 20:42:01 +08:00
jialinandjialin 46b9855c87 fix: custom apikey 2026-04-14 20:42:01 +08:00
jialinandjialin c667de90ed style: worker versio info 2026-04-14 20:42:01 +08:00
jialinandjialin cf13e3bed0 fix: volume mounts default empty 2026-04-10 15:33:59 +08:00
jialinandjialin 0956538b79 fix: volume name update while typing 2026-04-10 15:17:58 +08:00
jialinandjialin f2a4ab2654 feat: k8s volume mounts 2026-04-10 15:17:58 +08:00
jialinandjialin aeabdab781 fix(style): all scope tag 2026-04-10 09:52:36 +08:00
jialinandjialin f04b5cdc27 feat: custom api key 2026-04-09 21:27:50 +08:00
jialinandjialin b947e3a8b9 fix: show gpu driver version 2026-04-09 21:27:50 +08:00
jialinandjialin ae3148a86f feat: validate tts form items 2026-04-09 12:14:36 +08:00
jialinandjialin 78bf76e965 fix: python code for eidting image 2026-04-09 12:14:36 +08:00
jialinandjialin 7beb5756f7 fix: chat content height 2026-04-09 12:14:36 +08:00
jialinandjialin a2b0236ea3 refactor: table filters ux 2026-04-08 18:54:06 +08:00
jialinandjialin 144914110a feat: more filters component 2026-04-08 18:54:06 +08:00
jialinandjialin c73609d155 fix: adjust border raduis 2026-04-08 18:54:06 +08:00
jialinandjialin 53cd8ba00c fix: page error while route changing 2026-04-08 18:54:06 +08:00
jialinandjialin 1c09cc6c8c refactor: ui/ux 2026-04-08 18:54:06 +08:00
jialinandjialin 06160546a6 feat: show the worker version 2026-03-31 15:05:38 +08:00
jialinandjialin 0b292829fa fix(style): select content align 2026-03-31 15:05:38 +08:00
jialinandjialin 392c51fced chore: instance state filter 2026-03-26 18:20:55 +08:00
jialinandjialin 144d5a6d5f style: instance cell style 2026-03-26 18:20:55 +08:00
jialinandjialin 43dfc7e1f0 fix: normalize parameter string by removing line-continuation backslashes 2026-03-26 18:20:55 +08:00
jialinandjialin adde7fba37 feat: add instance view 2026-03-26 18:20:55 +08:00
jialinandjialin 33fe180997 refactor: model instance item 2026-03-26 18:20:55 +08:00
jialinandjialin a95b7e9561 feat: add instance view 2026-03-26 18:20:55 +08:00
jialinandjialin 08330e0b82 fix: row-textarea upload img 2026-03-26 17:59:33 +08:00
jialinandjialin c74118e913 refactor: embedding messages format 2026-03-26 17:59:33 +08:00
jialinandjialin bb3cef89c1 fix(style): dashboard usage filters 2026-03-26 17:59:33 +08:00
jialinandjialin 589936199c chore: sharegpt description 2026-03-26 17:59:33 +08:00
jialinandjialin 710e2e1996 fix: a non-running instance is selected in creating benchmark 2026-03-26 17:59:33 +08:00
bakiburakogunandjialin aa794c7646 fix(i18n): add missing ShareGPT profile and fix untranslated model tips in tr-TR 2026-03-25 09:48:20 +08:00
jialinandjialin 48779aed29 fix: add ShareGPT profile 2026-03-24 16:29:41 +08:00
jialinandjialin 3907e1ac76 fix(style): hint-input minwidth 2026-03-24 16:29:41 +08:00
jialinandjialin 4d6af4fbf8 fix: copybutton do not work 2026-03-24 10:37:15 +08:00
bakiburakogunandjialin 9c3b0efb30 revert: restore fileName: any for consistency with other locale files 2026-03-23 15:55:07 +08:00
Baki Burak Öğünjialingemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
8bcb7ffe4d Update src/locales/tr-TR.ts
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-23 15:55:07 +08:00
bakiburakogunandjialin 77f0740ec9 feat(i18n): add Turkish (tr-TR) locale translation
Add complete Turkish localization for the GPUStack UI including all 18 translation files covering common UI, menus, dashboard, models, playground, resources, API keys, users, backends, benchmarks, clusters, providers, routes, shortcuts, usage, and vendor names.
2026-03-23 15:55:07 +08:00
jialinandjialin 8b5e407758 fix: alert info height 2026-03-20 17:23:55 +08:00
jialinandjialin 6c88a5554f fix: bedrock required fields 2026-03-20 17:23:55 +08:00
jialinandjialin d779ee06b1 fix: typos: position 2026-03-20 17:23:55 +08:00
jialinandjialin f873d4e245 fix: show required fields for provider 2026-03-20 17:23:55 +08:00
jialinandjialin 0a0e03dc68 fix: open the advanced when configured 2026-03-20 17:23:55 +08:00
jialinandjialin f0e19c8934 fix: spin size 2026-03-19 12:28:18 +08:00
jialinandjialin a6af51dc32 fix: delay querying backends 2026-03-19 12:28:18 +08:00
jialinandjialin fd1a079271 refactor: deployments dir tree 2026-03-19 12:28:18 +08:00
jialinandjialin 9840a48301 fix: depercated props 2026-03-19 12:28:18 +08:00
jialinandjialin 2dc7c9adf1 feat: show the apikey mask value 2026-03-17 16:35:55 +08:00
jialinandjialin a86bfe7fd3 chore: upgrade antd 6.3.3 2026-03-17 16:35:55 +08:00
jialinandjialin d99475bb80 style: table overflow scroller style 2026-03-17 16:35:55 +08:00
jialinandjialin 194b7aff2e fix: autofilled in add user form 2026-03-17 16:35:55 +08:00
jialinandjialin f6d5733183 fix: video gen 2026-03-17 16:35:55 +08:00
jialinandjialin 0ca055b8e8 fix: parser multiple backend pramas on paste 2026-03-17 16:35:55 +08:00
jialinandjialin 9aae2c7f50 fix: use html audio to load stream 2026-03-17 16:35:55 +08:00
jialinandjialin c29ad1270b fix: stt streaming response handler 2026-03-17 16:35:55 +08:00
jialinandjialin 49a60c25af feat: add streaming for stt 2026-03-17 16:35:55 +08:00
jialinandjialin 39bcc158bb feat: add stream mode for tts 2026-03-17 16:35:55 +08:00
jialinandjialin 4f02bcd40c feat: paste multiple lines in parameters input 2026-03-17 16:35:55 +08:00
jialinandjialin 556c4a434d chore: benchmark profile filter 2026-03-17 16:35:55 +08:00
jialinandjialin d389b035b7 refactor: playground params form 2026-03-17 16:35:55 +08:00
jialinandjialin 9447088f89 refactor: adjust playground pages dir 2026-03-17 16:35:55 +08:00
jialinandjialin 64955c26a7 refactor: playground params container 2026-03-17 16:35:55 +08:00
jialinandjialin 68519223fa fix: pagination cache for table list 2026-03-17 16:35:55 +08:00
jialinandjialin e72cacc4b2 chore: autocomplete user info 2026-03-17 16:35:55 +08:00
jialinandjialin ed4be2328f chore: store pagination for list 2026-03-17 16:35:55 +08:00
jialinandjialin e241c6cb6f refactor: embeddding input list 2026-03-17 16:35:55 +08:00
jialinandjialin 6e98fb4274 fix: reset filter in export modal 2026-03-16 14:48:46 +08:00
jialinandjialin 83f6f41787 fix: show backend warning hint after submitting in editing mode 2026-03-16 14:48:46 +08:00
jialinandjialin 18c10e5839 chore: update model cols 2026-03-16 14:48:46 +08:00
jialinandjialin 11de47c813 style: same key tooltip 2026-03-16 14:48:46 +08:00
jialinandjialin 85ea619b18 fix: title for editing apikey 2026-03-16 14:48:46 +08:00
jialinandjialin 3f57cf21f7 feat: add profile filter in benchmark 2026-03-16 14:48:46 +08:00
jialinandjialin b75c773879 chore: add nvidia notes 2026-03-16 14:48:46 +08:00
jialinandjialin 95070ae382 feat: display provider models 2026-03-16 14:48:46 +08:00
jialin 353ee3a9ae fix: route target can be set 0 2026-03-10 19:06:49 +08:00
jialin 972e147fdf fix: yaml edior height 2026-03-10 19:06:49 +08:00
jialin 73e52fe334 chore: add hint for creating backend 2026-03-09 15:48:16 +08:00
jialin cf521ad79e fix: default_env field not display in backend editing yaml 2026-03-02 10:21:21 +08:00
jialin 76a3e5cfcf fix: custom config overwrite input value 2026-02-25 10:42:06 +08:00
jialin 200d63e5fc fix: add custom config in get-models, test-model 2026-02-25 10:28:31 +08:00
jialin 0aeefb1217 fix: allow deleting version for community backend 2026-02-24 17:23:42 +08:00
jialin 7dfeb859e2 refactor: query dashboard data 2026-02-24 16:33:28 +08:00
jialin 5151f26b9e fix: miss openaiCustomUrl in get-models payload 2026-02-24 15:36:41 +08:00
jialin 053e5cca86 chore: markdown for community backend detail 2026-02-24 11:37:22 +08:00
jialin 89765c19c4 fix: copy multi lines when == 2026-02-24 11:07:53 +08:00
jialin 0824f0c13b fix: provider proxy_url set null when disabled 2026-02-24 11:07:53 +08:00
jialin 04092c3fa5 fix: openaicustomurl do not display 2026-02-24 11:07:53 +08:00
jialin d8258cf676 fix: worker list params do not update 2026-02-14 12:06:45 +08:00
jialin 97d6eee5ba chore: icons static files 2026-02-13 20:36:46 +08:00
jialin 511297ed11 fix: do not cache while watching during manual updates 2026-02-13 19:32:59 +08:00
jialin 88aafbaf7b refactor: list watch trigger 2026-02-13 16:12:56 +08:00
jialin af9890b16c style: community backend detail title 2026-02-13 12:26:43 +08:00
jialin c67a8c4120 fix: add other category 2026-02-12 19:38:47 +08:00
jialin 16c0e00b4f chore: diff envs in editing deployment 2026-02-12 19:38:47 +08:00
jialin d1c1c5acf9 chore: corex metax notes 2026-02-12 14:56:25 +08:00
jialin 9cb7145b4e fix: provider yaml inital value 2026-02-12 12:20:05 +08:00
jialin 978d327bcd chore: form tabs active change 2026-02-12 12:20:05 +08:00
jialin 07664b5f22 chore: quick create route 2026-02-12 12:20:05 +08:00
jialin b21fe0ac4c style: error message toast 2026-02-12 12:20:05 +08:00
jialin 932c30cd7f fix: enable_model_route set true in cloning 2026-02-11 20:35:19 +08:00
jialin 59c19f3fae fix: update add worker command 2026-02-11 17:54:34 +08:00
jialin c9245f957e fix: enable_model_route value in cloning 2026-02-11 17:54:34 +08:00
jialin 228cd4e664 fix: benchmark detail envs gpus list 2026-02-11 16:50:49 +08:00
jialin 289a651d52 fix: default language value 2026-02-11 16:14:06 +08:00
jialin 1b8c17ce92 fix: tts params filter 2026-02-11 15:30:35 +08:00
jialin e66de732ac fix: copy button do not work 2026-02-11 15:30:35 +08:00
jialin 2ba94f7524 fix: miss model file path in cloning 2026-02-11 11:05:43 +08:00
jialin 3f90642726 chore: request result in list 2026-02-10 17:29:57 +08:00
jialin 35ae7b9643 fix(style): adjust ui 2026-02-10 17:29:57 +08:00
jialin 15d635322b fix: target model text overflow 2026-02-10 17:29:57 +08:00
jialin c3d5234710 fix: new added model item is cleared 2026-02-10 17:29:57 +08:00
jialin 9b498d10d5 fix: dashboard model data format 2026-02-10 11:33:24 +08:00
jialin 9418e5b635 fix: get models in cloning 2026-02-10 11:33:24 +08:00
jialin 8779b79bbf fix: get models in cloning 2026-02-10 10:25:02 +08:00
jialin ba11689029 feat: filter form for table 2026-02-10 10:25:02 +08:00
jialin 90e212a1b2 fix: reset default columns do not work 2026-02-09 18:02:30 +08:00
jialin 198a52e230 chore: add runtime in register k8s command 2026-02-09 15:58:29 +08:00
jialin e7890dda3d chore: show fallback change warning 2026-02-09 13:26:44 +08:00
jialin 83086c46e6 fix: target options miss deployment models 2026-02-09 11:51:26 +08:00
jialin 1314123ad7 fix: api_key format 2026-02-09 11:51:26 +08:00
jialin cb13873098 fix: deduplicate models before submitting 2026-02-09 11:51:26 +08:00
jialin fba430999d fix(style): error message toast 2026-02-09 09:35:42 +08:00
jialin 293c53d8dd chore: keep error message visiable on hover 2026-02-08 19:44:43 +08:00
jialin 33ce334bd7 fix: verify same model selected 2026-02-08 19:44:43 +08:00
jialin 379eeedde2 fix: deduplicate in select provider models and target models 2026-02-08 17:20:05 +08:00
jialin ec6f5d1ad3 chore: add copy button for run command 2026-02-08 10:27:49 +08:00
jialin f35ba8f8e5 fix: success message in creation cluster 2026-02-08 10:27:49 +08:00
jialin 21ffd2a564 fix: env params incorrect after switching between vendors 2026-02-07 16:51:09 +08:00
jialin 3c5b095510 fix: benchmark enviroment table 2026-02-06 20:59:47 +08:00
jialin 096227e3da fix: benchmark tooltip issues 2026-02-06 18:34:20 +08:00
jialin 4a582ec8b1 fix: benchmark tooltip 2026-02-06 18:34:20 +08:00
jialin 4132494a49 chore: provider masked api-key 2026-02-06 17:33:06 +08:00
jialin 1bd31d2936 fix: stt languages sorting 2026-02-06 17:33:06 +08:00
jialin 18ffeb82a2 fix: benchmark form data error in editing 2026-02-06 15:36:24 +08:00
jialin da985871cc fix: benchmark issues 2026-02-06 14:27:05 +08:00
jialin 4ff5b3e05a chore: grafana entry 2026-02-06 14:27:05 +08:00
jialin 7f3d6bb55f fix: ui issues 2026-02-06 11:13:49 +08:00
jialin d54722609c fix: remove duplicate selected models 2026-02-06 11:13:49 +08:00
jialin 2765d5edeb chore: tts, stt meta 2026-02-05 20:35:07 +08:00
jialin a1a7979fb3 fix: update open playground btn 2026-02-05 11:55:05 +08:00
jialin 4e8b5e26ae feat: monitor button 2026-02-04 20:14:25 +08:00
jialin 47596fdb1a fix: copy button do not work 2026-02-04 20:14:25 +08:00
jialin 332521a7f3 feat: grafana link 2026-02-04 20:14:25 +08:00
jialin 8690de00a9 feat: add grafana link 2026-02-04 20:14:25 +08:00
jialin 7fd2b43704 fix: refresh enable backend state 2026-02-04 14:58:48 +08:00
jialin 05c05419fc feat: duplicate deployment 2026-02-04 14:58:48 +08:00
jialin 5ad9ac277c style: benchmark progress 2026-02-03 20:50:40 +08:00
jialin 42f24245b5 fix: maas provider custom config 2026-02-03 20:26:00 +08:00
jialin 7548836b67 fix: reranker for sglang 2026-02-03 19:12:34 +08:00
jialin a2c33affcd fix: file import error 2026-02-03 18:27:49 +08:00
jialin 11039a5c82 fix: enable_model_route 2026-02-03 18:21:08 +08:00
jialin 46969702a8 fix: open model in playground 2026-02-03 18:21:08 +08:00
jialin 134827a0f7 chore: resort menu 2026-02-03 18:21:08 +08:00
jialin f0390682ce chore: rebase main 2026-02-03 18:21:08 +08:00
jialin 2a6846d2c0 chore: remove invalid file 2026-02-03 18:21:08 +08:00
jialin dc8b3b9210 fix: routes locales 2026-02-03 18:21:08 +08:00
jialin dcda9be50b style: display target model name 2026-02-03 18:21:08 +08:00
jialin 04c25ab0f7 chore: fallback selection 2026-02-03 18:21:08 +08:00
jialin 051d9421b0 chore: access, endpoints to route targets 2026-02-03 18:21:08 +08:00
jialin 6d65797b4f fix: access source error 2026-02-03 18:21:08 +08:00
jialin 497813eced fix(styles): providers access token 2026-02-03 18:21:08 +08:00
jialin 26e3a1944b chore: access form test 2026-02-03 18:21:08 +08:00
jialin e188d788b9 chore: provider form test 2026-02-03 18:21:08 +08:00
jialin d86d061f97 style: access list 2026-02-03 18:21:08 +08:00
jialin 35918d2a00 chore: access meta 2026-02-03 18:21:08 +08:00
jialin 51bab71291 style: adjust providers style 2026-02-03 18:21:08 +08:00
jialin 6fd886b27a style: metadata list styles 2026-02-03 18:21:08 +08:00
jialin 2211009cce chore: providers logo 2026-02-03 18:21:08 +08:00
jialin eb65cee280 style: fallback ui 2026-02-03 18:21:08 +08:00
jialin a3dd126931 chore: fallback checkbox 2026-02-03 18:21:08 +08:00
jialin 0b2be1e54b chore: model access control 2026-02-03 18:21:08 +08:00
jialin 337d93ff51 style: endpoint selection 2026-02-03 18:21:08 +08:00
jialin df5c2be7fa feat: metadata list 2026-02-03 18:21:08 +08:00
jialin 15cd9795d5 style: providers selection, remove fallback form 2026-02-03 18:21:08 +08:00
jialin 5fcdf4ea00 chore: access form 2026-02-03 18:21:08 +08:00
jialin e8e982559d chore: providers ux 2026-02-03 18:21:08 +08:00
jialin b2a8d27178 feat: provider form 2026-02-03 18:21:08 +08:00
jialin 679e10abd6 feat: add access list 2026-02-03 18:21:08 +08:00
jialin 34331063eb feat: provider lsit 2026-02-03 18:21:08 +08:00
jialin 2dfdd4a0d9 feat: add provider menu 2026-02-03 18:21:08 +08:00
jialin cb5413c2ce fix: backend version list 2026-02-03 16:05:43 +08:00
jialin 9e7f2261f3 fix: max token not a int number 2026-02-03 16:05:43 +08:00
jialin 5efed8f193 fix: do not set default deployment name 2026-02-03 16:05:43 +08:00
jialin 0af7e95af2 fix: do not display as group when has one 2026-02-03 15:41:16 +08:00
jialin cf3b225d3e fix: edit community backend 2026-02-03 15:41:16 +08:00
jialin 32f95f5d7d fix: revert backend selection 2026-02-03 15:41:16 +08:00
jialin 9a5a1bf555 chore: add mutiple additional volume 2026-02-02 15:58:01 +08:00
jialin b7dfb0af0b chore: export action for benchmark item 2026-02-02 15:58:01 +08:00
jialin 4a71387098 fix: do not show evalute result when no claims 2026-02-02 15:58:01 +08:00
jialin f651ab7fd9 fix: default benchmark form values 2026-02-02 14:10:46 +08:00
jialin fc599a0637 style: column setting sub title 2026-02-02 14:10:46 +08:00
jialin 9796233897 feat: benchmark detail actions 2026-02-01 22:56:36 +08:00
jialin 0849a2f114 chore: column settings 2026-02-01 20:22:25 +08:00
jialin 043218937f chore: benchmark detail locales 2026-02-01 20:22:25 +08:00
jialin 7b4d60b576 feat: add ref_text, x_vector_only_mode 2026-01-31 16:53:35 +08:00
jialin f803b910b5 fix: tts default params 2026-01-31 14:39:44 +08:00
jialin 8d660b20aa fix: miss default name in deploy 2026-01-31 14:39:44 +08:00
jialin 1e298bbbc0 fix: tts viewcode params 2026-01-31 14:06:50 +08:00
jialin 14eb54481d chore: remove advanced toggle in image 2026-01-31 14:06:50 +08:00
jialin 7a5c28228b chore: upload a ref audio 2026-01-31 14:06:50 +08:00
jialin f278716b7e fix: hidden tokens input 2026-01-30 23:14:23 +08:00
jialin b3e186fea4 feat: add tts params 2026-01-30 22:58:11 +08:00
jialinandLawrence Li f0cdefad90 chore: update icons 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 507b0e44d5 fix: init envs 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 56985be26b chore: default_env, env 2026-01-30 20:48:55 +08:00
jialinandLawrence Li d3a0c6a330 style: arrow bivider 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 0d5cd28d38 feat: community backends marketplace 2026-01-30 20:48:55 +08:00
jialinandLawrence Li b6bd9cd7dd feat: community backends page 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 2dab5093dc fix: parse yaml error 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 4a4a02eb0d chore: update env vars from backend version 2026-01-30 20:48:55 +08:00
jialinandLawrence Li 4000aacb2c fix: backend selection 2026-01-30 20:48:55 +08:00
jialinandLawrence Li a2be7f6b36 chore: backend api 2026-01-30 20:48:55 +08:00
jialin af6f90c228 fix: benchmark state 2026-01-30 18:48:55 +08:00
jialin 3df2d23bf5 fix: benchmark detail styles 2026-01-30 18:48:55 +08:00
jialin d173e2d49b fix: details layout 2026-01-30 18:48:55 +08:00
jialin 5747cae87a style: detail layout 2026-01-30 18:48:55 +08:00
jialin 667751dc5f refactor: env detail 2026-01-30 18:48:55 +08:00
jialin 577996f507 refactor: summary 2026-01-30 18:48:55 +08:00
jialin 22e42e279c chore: logs 2026-01-30 18:48:55 +08:00
jialin 6c42b87c57 chore: update creation form 2026-01-30 18:48:55 +08:00
jialin 3fe63f836f chore: remove labels 2026-01-30 18:48:55 +08:00
jialin 1a2e82f079 chore: profile config 2026-01-30 18:48:55 +08:00
jialin 8f2eea70f6 chore: detail api 2026-01-30 18:48:55 +08:00
jialin 358df925e7 chore: list actions 2026-01-30 18:48:55 +08:00
jialin 9ab8f233a2 feat: benchmark detail page 2026-01-30 18:48:55 +08:00
jialin fb0e45ebed feat: benchmark form 2026-01-30 18:48:55 +08:00
jialin 37b814b70a feat: benchmark list 2026-01-30 18:48:55 +08:00
jialin 8b282afb03 fix: vllm run command 2026-01-22 17:39:01 +08:00
jialin ec9a4b2607 fix: duplicate watch created 2026-01-20 14:51:45 +08:00
jialin d5f3158638 fix: recreate watch request after cluster changed 2026-01-20 13:40:15 +08:00
jialin eb5e344b41 style: gpu vendor logo 2026-01-20 10:36:38 +08:00
jialin 9d13b27c9e fix: add worker message trigger 2026-01-20 10:36:38 +08:00
jialin b4f69a4e2e perf: cancel the request when leaving page 2026-01-16 16:53:38 +08:00
jialin 3e359a69c7 fix: set loadingWatch state when the loading begins 2026-01-16 11:37:47 +08:00
jialin 9de4054851 style: t-head logo 2026-01-15 19:54:20 +08:00
jialin 9676e63b5e feat: add t-head 2026-01-15 19:54:20 +08:00
jialin 337cb67cd9 fix: fetch list axios token incorrect 2026-01-15 17:48:45 +08:00
jialin fe79258766 fix: remove updateManually on model files 2026-01-15 16:43:44 +08:00
jialin 0a9cc1e17a fix: cache current list query params 2026-01-15 16:20:05 +08:00
jialin 6445061836 fix: page changing do not trigger watch 2026-01-15 15:07:55 +08:00
jialin 19df710a0d fix(style): autocomplete width out of range 2026-01-15 11:17:05 +08:00
jialin 0d8333eac6 style: textarea max rows 2026-01-15 11:17:05 +08:00
jialin b1cb4bbe32 fix: minimatch error 2026-01-15 09:28:19 +08:00
jialin 11b9430b15 fix: worker list update by watch 2026-01-14 20:12:39 +08:00
jialin 1230c3bd28 fix: worker list update by watch 2026-01-14 19:58:09 +08:00
jialin 2ee645b1ee fix: too many requests after query change 2026-01-14 18:02:08 +08:00
jialin 84ce308d9d fix: hint input width 2026-01-14 18:02:08 +08:00
jialin be631d021e feat: get initial password command 2026-01-14 17:19:43 +08:00
jialin a37458fd9e fix: update worker by fetchData 2026-01-14 16:01:47 +08:00
jialin 095b9478cc fix: form submit on video 2026-01-14 16:01:47 +08:00
jialin 94fec52329 chore: scrollable tabs 2026-01-14 16:01:47 +08:00
jialin fc4d71139a style: playground right side 2026-01-14 16:01:47 +08:00
jialin c813344c7c fix: the max embedding value exceeds the limit. 2026-01-13 20:57:50 +08:00
jialin f0bfbfe693 fix: re-create watch request after filtering 2026-01-13 18:43:11 +08:00
jialin 741cf07f2e fix(style): model file list text overflow 2026-01-13 18:43:11 +08:00
jialin 5ff26e1ce8 chore: video test code 2026-01-13 18:43:11 +08:00
jialin 342d977fe4 fix: -custom error message on creating backend 2026-01-13 18:43:11 +08:00
niu_heandjialin 9c1011fbd8 Add JSX as a global variable in ESLint config 2026-01-13 17:22:03 +08:00
jialin daaf77a89f fix: do not fixed node version in package.json 2026-01-13 09:29:51 +08:00
jialin 420600510d fix(style): remove dot symbol when no size 2026-01-12 20:42:05 +08:00
jialin 0ae9a7f42f fix: gpuindex sort 2026-01-12 20:36:38 +08:00
jialin a1cddfa18b chore: hide cluster detail 2026-01-12 20:30:24 +08:00
jialin 89fe8e68f4 feat: playground video 2026-01-12 20:30:24 +08:00
jialin cd3b0607a5 style: modal drawer blur 2026-01-12 20:30:24 +08:00
jialin 0cde6f7dbc chore: add models and gpus in cluster detail page 2026-01-12 20:30:24 +08:00
jialin 42943a33fe chore: display allocated vram on instance 2026-01-12 14:52:59 +08:00
jialin fc76822bdf fix: catalog size 2026-01-12 12:24:34 +08:00
jialin 05ad296b43 chore: cluster system load service 2026-01-12 12:24:34 +08:00
jialin 2e5b075d1b fix: header bg color 2026-01-12 12:24:34 +08:00
EVGENY Mandjialin f47348106e Update backends.ts 2026-01-10 17:14:17 +08:00
jialin f351447f09 chore: remove editor static files 2026-01-09 21:13:41 +08:00
jialin 10b2a8b4f9 fix: editor incompatibility with firefox79 2026-01-09 20:51:48 +08:00
jialin afcd3e5693 feat: add cluster detail 2026-01-09 20:51:48 +08:00
jialin 7b5ce83cbd fix: add worker default selection is incorrect 2026-01-08 11:01:06 +08:00
jialin 0cff804cd7 fix: add worker title 2026-01-08 09:26:55 +08:00
jialin f4e6bf7fd5 fix: gpuindexs sort 2026-01-08 09:26:55 +08:00
jialin ba4f277863 fix: add worker ux when only DO cluster 2026-01-08 09:26:55 +08:00
jialin d35bd16543 fix: cluster pending status 2026-01-08 09:26:55 +08:00
jialin 065841530d fix: slider marker label 2026-01-07 15:55:26 +08:00
jialin 28a73d2a6e chore: use drawer for creation 2026-01-07 15:55:26 +08:00
jialin bbf09c2ea9 style: command viewer scroller 2026-01-07 15:55:26 +08:00
jialin 8f9fae9210 style: use drawer for adding worker 2026-01-06 17:21:32 +08:00
jialin 0eba1da6c4 style: cluster creation steps 2026-01-06 17:21:32 +08:00
jialin 0ba8300959 fix: no clusters tips in adding worker modal 2026-01-06 17:21:32 +08:00
jialin f8ae7680ad fix: updating model name should show the recreation message 2026-01-06 17:21:32 +08:00
jialin d51f00d35d style: cluster creation modal 2026-01-06 17:21:32 +08:00
jialin 0f7a475ec9 feat: add context length 2026-01-06 17:21:32 +08:00
jialin d8b3331648 style: slider handle color 2026-01-06 17:21:32 +08:00
jialin 2816fedc36 refactor: catalog fetch data 2026-01-06 17:21:32 +08:00
jialin 17de9d98f4 chore(deps): upgrade antd to v6 2026-01-06 17:21:32 +08:00
jialin b2f5eb8850 style: user usage rank 2026-01-06 17:21:32 +08:00
jialin c821a2e463 refactor: create cluster ux 2026-01-06 17:21:32 +08:00
jialin 66e8f83395 fix: did not cache workers and clusters in model files 2025-12-31 10:23:15 +08:00
jialin 41ebceca8f chore: static iconfont files 2025-12-29 15:40:36 +08:00
jialin 632685672c fix: wheel event passive 2025-12-29 15:28:40 +08:00
jialin bd2e4bb633 docs: hygon notes 2025-12-29 09:36:48 +08:00
jialin 835633d44b fix: help menu item clickable area 2025-12-26 18:18:32 +08:00
EVGENY Mandjialin 75773e6b00 Update models.ts 2025-12-26 18:16:33 +08:00
EVGENY Mandjialin a21b9b5244 Update users.ts 2025-12-26 18:16:02 +08:00
EVGENY Mandjialin 38bf76dc14 Update common.ts 2025-12-26 18:15:29 +08:00
EVGENY Mandjialin 9461341104 Update clusters.ts 2025-12-26 18:15:05 +08:00
jialin 5b5b2c6364 fix: chat compare switch model meta 2025-12-26 17:29:36 +08:00
jialin a006263e51 fix: add worker to default cluster 2025-12-26 12:08:30 +08:00
jialin febdd38f7c fix: set submitloading as false in catch 2025-12-26 09:48:10 +08:00
jialin 1b4d335b09 fix: store hideModel value in global session 2025-12-25 20:36:48 +08:00
jialin 725409a576 fix: image seed do no clear 2025-12-25 17:57:11 +08:00
jialin 9b25cc51e4 style: user rank chart 2025-12-25 17:57:11 +08:00
jialin 9dcdb6b6e7 fix: reset userSettings first login 2025-12-25 13:14:59 +08:00
jialin 73b987358f fix(style): cluster options 2025-12-24 20:52:48 +08:00
jialin 84c2bf130c style: cluster selector 2025-12-24 20:37:20 +08:00
jialin b25189feea fix: cancel sorting 2025-12-24 15:40:19 +08:00
jialin a9b58a5564 fix: cluster filter do not work in dashboard 2025-12-24 12:20:56 +08:00
jialin 530b20c633 chore: add resource modal button text 2025-12-24 10:53:53 +08:00
jialin fa0aeac10b fix: remove default sorting status on UI 2025-12-23 20:54:16 +08:00
jialin 1b661bb8cd feat: set default cluster 2025-12-23 20:54:16 +08:00
jialin 530e8f7537 fix: replicas, ready_replicas sorting 2025-12-23 18:35:33 +08:00
jialin 9e019a98f6 fix: click create hide modal 2025-12-23 10:51:58 +08:00
jialin 10c6cf9f7e fix: init image seed 2025-12-23 10:51:58 +08:00
jialin 480b238e44 fix: expand all model clear instances cache 2025-12-23 10:51:58 +08:00
jialin 32bdbc693a fix: onTableChange only handles sorting 2025-12-22 19:41:52 +08:00
jialin ab146b6b6b fix: allow to enter seed 2025-12-22 18:11:46 +08:00
jialin 763929eafc fix: instance backend data ui compatability with old data 2025-12-22 18:11:46 +08:00
jialin 711b3fee98 fix: user guidance when no resource 2025-12-22 18:11:46 +08:00
jialin 0d0592eee9 fix: sorter tooltip 2025-12-22 16:41:44 +08:00
jialin cc7948ba45 fix: update Server as GPUStack Server 2025-12-22 16:19:47 +08:00
jialin 7e25b64689 fix: remove instance from cache after deleting 2025-12-22 16:19:47 +08:00
Yuxing Dengandjialin d844cdadb5 fix: update advertise address title and description 2025-12-22 15:14:29 +08:00
jialin e3b3f51e87 fix: ignore the sort_by when no data 2025-12-22 11:57:57 +08:00
jialin 45efda7f1d fix: single sorted field, remove token 2025-12-22 10:10:35 +08:00
jialin db45c861e4 feat: models filter by state 2025-12-21 21:37:05 +08:00
jialin 42f8e6a6d9 chore: set col order 2025-12-21 21:37:05 +08:00
jialin 23a761226a feat: table list sorter 2025-12-21 21:37:05 +08:00
jialin 6738dfe0d3 fix: anyway submit disabled when no catalog specs 2025-12-19 20:33:25 +08:00
jialin 6e16b2eb0c fix: submit pool data from a validate failed form 2025-12-19 20:33:25 +08:00
jialin 707eb15465 fix: update command of adding worker 2025-12-19 20:33:25 +08:00
jialin 8008099e71 fix: image default seed 2025-12-19 20:33:25 +08:00
jialin 79ccb96c95 fix: edit worker_config 2025-12-19 16:44:52 +08:00
jialin bd76934e40 fix: delete worker pool 2025-12-19 15:19:27 +08:00
jialin bb793a41fc fix: entrypoint do not display in edit 2025-12-19 15:00:10 +08:00
jialin ad048a92ed feat: backend entrypoint 2025-12-19 14:40:20 +08:00
jialin 14d944ad2f style: cluster advanced scroller 2025-12-18 20:03:00 +08:00
jialin e1b8b5696c fix: update instance backend field 2025-12-18 17:29:07 +08:00
jialin 78368e9979 fix: cluster worker config issues 2025-12-18 17:29:07 +08:00
jialin e83bff6923 chore: worker ip 2025-12-18 14:06:32 +08:00
jialin e6c12d81ea fix: edit worker pool miss data 2025-12-18 14:06:32 +08:00
jialin 3a73d0c1b2 style: worker added info status 2025-12-18 12:19:31 +08:00
jialin b524587a9a fix: add worker button do not work 2025-12-18 10:56:45 +08:00
jialin e4bd753355 style: no resource modal 2025-12-17 19:58:28 +08:00
jialin 30536cb04a fix: remove advanced agrs in image 2025-12-17 16:15:34 +08:00
jialin da1efa9270 fix: password length 64 2025-12-17 16:15:34 +08:00
jialin bd6f4b3f76 fix: add worker, create cluster 2025-12-17 14:59:07 +08:00
jialin 5e48d75ead fix: add worker message 2025-12-17 14:59:07 +08:00
jialin bd46fcdc75 fix: container name in add worker 2025-12-17 14:59:07 +08:00
jialin 6968f9fa6b feat: worker_config 2025-12-16 18:43:28 +08:00
jialin 5225fd328d fix: backend framework options 2025-12-16 11:11:24 +08:00
jialin fa42474612 chore: worker added message 2025-12-15 15:13:57 +08:00
jialin d2eda78af0 fix: add worker command container_name 2025-12-15 10:18:16 +08:00
jialin 7a0ee6aac4 style: column wrapper srollable style 2025-12-13 19:47:48 +08:00
jialin b327eedb45 fix: logout error 2025-12-12 17:26:06 +08:00
jialin a68e3ff9da fix: no spec list for catalog 2025-12-12 17:22:28 +08:00
jialin 0500dd2e35 fix: query catalog spec, default_version check 2025-12-12 17:08:30 +08:00
jialin cb43131ccb fix: add worker command 2025-12-12 11:14:57 +08:00
jialin 883e0ab115 feat: get initial password 2025-12-12 11:14:57 +08:00
jialin b189160888 fix: docs for gpu link 404 2025-12-11 18:14:25 +08:00
jialin 3dbb0de683 feat: add resource for first login 2025-12-11 17:50:39 +08:00
jialin b38bc8a207 fix: table sorter 2025-12-11 13:33:41 +08:00
jialin 3328828da6 refactor: card list resize container 2025-12-11 13:33:41 +08:00
jialin 7fdabd90a5 fix: update add worker command 2025-12-10 15:50:53 +08:00
jialin d8452e98f5 style: drawer styles 2025-12-10 15:50:53 +08:00
jialin dda2213593 style: workers page no page container 2025-12-10 15:50:53 +08:00
jialin 04f995b80c style: resize terminal height 2025-12-10 15:50:53 +08:00
jialin a08f1e2512 chore: rename ground-left as ground-llm 2025-12-10 15:50:53 +08:00
jialin af4849d6cc style: terminal panel 2025-12-10 15:50:53 +08:00
jialin 087675889f feat: model cache volume mount, clear gpu selector 2025-12-09 19:07:40 +08:00
1180 changed files with 83936 additions and 43053 deletions
+143
View File
@@ -0,0 +1,143 @@
---
name: create-crud-page
description: Scaffold a CRUD list/table page module in the gpustack-ui monorepo. Use when creating a new page module, building a list/table page, adding a create/edit drawer, or setting up the components/config/forms/hooks/services structure for a feature.
---
# Create a CRUD Table Page
## Inputs (do this first)
- The argument passed to this skill is the **module name** (e.g. `/create-crud-page api-keys` → module `api-keys`). If no name was given, ask for it.
- **Always ask the user for the API documentation before generating any code**, even if a module name was provided:
> Where is the API documentation for this module? (OpenAPI/Swagger URL, schema file path, or an interface description)
- Wait for the answer, then read/fetch it. Derive `config/types.ts` (`FormData`, `ListItem`), the `services` request hooks, and form fields from that schema. Do not guess field names or endpoints — if the doc is missing details, ask.
- **Also ask which form layout to scaffold:**
> Should the form use tabs? (1) a plain form without tabs, or (2) a tabbed form
Choose the form structure in section 3 accordingly. Default to **no tabs** unless the user picks tabs or the schema clearly has many grouped sections.
---
Before anything: **reuse common `components`, `hooks`, and `utils` from `@gpustack/core-ui` whenever possible.**
Reference implementation for sections below: `src/pages/model-routes`.
## Module structure
Create the module under `src/pages/{module}`:
```text
{module}
├── components
├── config
├── forms
├── hooks
├── index.tsx
└── services
```
## 1. components
Module-specific components.
- The create/edit form component is named `add-xxx-modal.tsx` (repo convention — keep the `-modal` suffix even though it is built with `FormDrawer`).
- Use `FormDrawer` from `@gpustack/core-ui`.
- If a table cell's render logic/structure is complex, extract it into `xxx-cell.tsx`.
## 2. config
```text
config
├── index.ts # static configs & constants
└── types.ts # TypeScript types
```
Naming: form types → `FormData`; table list item types → `ListItem`.
## 3. forms
Main form component goes in `forms/index.tsx`.
- **Complex interactions** (Form.Item split across components): create a dedicated Form Context and wrap with `FormContext.Provider`.
- **Tab-based forms**: use `ScrollSpyTabs` from `@gpustack/core-ui`, wrapping the `Form` or `FormContext.Provider`. Do not use tabs unless necessary.
- **Required-field validation**: use `getRuleMessage` for standard `input`/`select`.
- For cascading selectors and async race protection, follow the **form-patterns** skill.
## 4. hooks
- Table columns → `use-xxx-columns.tsx`.
- Open/close hooks for `add-xxx-modal.tsx``use-create-xxx.ts`.
## 5. index.tsx (list page entry)
- **Data fetching**: `useTableFetch` from `@gpustack/core-ui`.
- **Data display**:
- Standard table → Ant Design `Table`. Ref: `src/pages/users/index.tsx`.
- Expandable/collapsible rows → `Table` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/index.tsx`.
- Card-style lists → use `InfiniteScrollerProvider`. Ref: `src/pages/backends/index.tsx`.
## 6. services
`request` is injected via a provider — do **not** create a centralized `apis` directory like in `gpustack-ui`. Define request hooks directly in `services`.
- Use `useRequest` from `@gpustack/core-ui`, or `useQueryData` (same underlying method).
- Ref: `src/pages/gpu-service/storage-types/services/use-create-storage-type.ts`.
## 7. Empty data
- Page table lists → `NoResult`.
- Simple (non-page) tables → `Empty` with `image={Empty.PRESENTED_IMAGE_SIMPLE}`.
## Common UI conventions
- **Drawer/Modal open/close**: use `useBodyScroll` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/hooks/use-create-route.ts`.
- **Status display** (success/failed/processing/warning): use `StatusTag`, never `Tag` from `antd` directly. See **Status display** below. Ref: `src/pages/llmodels/components/table-list.tsx`.
- **Permission-gated visibility**: use `Access` / `useAccess`. Ref: `src/pages/access/index.tsx`.
- **Styles**: avoid `styled-components` for complex/large styling. Prefer `createStyles` for component-scoped dynamic styles, CSS Modules (`xxx.module.less`) for static structured styles.
## Status display
Use `StatusTag` from `@gpustack/core-ui` for status display. Do not use `Tag` from `antd` directly.
1. Define the mapping from business status values to UI status values in the module's `config/index.ts`:
```ts
import { StatusMaps } from '@/config';
import { StatusType } from '@/config/types';
export const XxxStatusValueMap = {
Running: 'running',
Pending: 'pending',
Failed: 'failed'
};
export const XxxStatusLabelMap: Record<string, string> = {
[XxxStatusValueMap.Running]: 'Running',
[XxxStatusValueMap.Pending]: 'Pending',
[XxxStatusValueMap.Failed]: 'Failed'
};
export const status: Record<string, StatusType> = {
[XxxStatusValueMap.Running]: StatusMaps.success,
[XxxStatusValueMap.Pending]: StatusMaps.transitioning,
[XxxStatusValueMap.Failed]: StatusMaps.error
};
```
2. In table columns, pass only the UI status value and display text required by `StatusTag`:
```tsx
<StatusTag
statusValue={{
status: status[value],
text: XxxStatusLabelMap[value] || value,
message: record.state_message
}}
/>
```
`statusValue.status` must be a value mapped from `StatusMaps` (`success`, `transitioning`, `warning`, `error`, `inactive`). Do not pass business status values such as `running` or `pending` directly.
+103
View File
@@ -0,0 +1,103 @@
---
name: form-patterns
description: Patterns for forms with cascading/dependent selections in the gpustack-ui monorepo. Use when building a form where picking one field derives another (pick A → auto-pick B → write form), handling async option loading on modal open, or protecting against stale async results.
---
# Form Patterns
Theme: keep cascading selections (pick A → derive B → write form) on a single, predictable path.
## Accessing the form: `form` vs `form.current`
This is **not** absolute — it depends on the call site:
- **Inside the form component** (`forms/index.tsx`), or anywhere holding a `Form.useForm()` instance → call it directly: `form.setFieldsValue(...)`.
- **In the outer Drawer/Modal wrapper** that opens the form and holds it via `ref={form}` (`const form = useRef(null)`), driven by an `open` prop → go through the ref: `form.current?.setFieldsValue(...)`.
The reference template below is written for the **Drawer-wrapper scenario** (it reacts to `open` and owns the shared `selection` state), so it uses `form.current?` throughout. If you lift this logic into the form body with a `useForm()` instance, drop the `.current`.
## 1. No fallback for derived selection
When "pick A then auto-pick B", match by rule and return `undefined` if no match — let the form field stay empty. Do **not** silently fall back to `list[0]`; a fallback hides data issues and fakes a valid selection.
```ts
const findB = (key, list) =>
key ? list.find((x) => x.key === key) : undefined;
```
For form fields, clear with `undefined`, not `''`. In Ant Design `undefined` restores the placeholder; `''` is treated as a real value.
## 2. Async race protection
For fetches triggered by a lifecycle entry (e.g. modal open), tag each invocation with a session ref. Discard stale results if the session rotated (modal closed and re-opened) before the response arrives.
```ts
const sessionRef = useRef(0);
useEffect(() => {
if (!open) {
sessionRef.current += 1;
return;
}
const session = ++sessionRef.current;
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
if (sessionRef.current !== session) return;
applySelection(as.items[0], findB(as.items[0].key, bs.items));
});
}, [open]);
```
## 3. Reference template
Two cascading selectors backed by a single shared state, with a single atomic write (state + form together):
```ts
type Selection = { a?: string; b?: number };
const [selection, setSelection] = useState<Selection>({});
const sessionRef = useRef(0);
const form = useRef<any>(null); // wrapper holds the form via <Form ref={form} /> — see "Accessing the form" above
const findB = (key, list) =>
key ? list.find((x) => x.key === key) : undefined;
// Single atomic write: state + form together.
const applySelection = (a, b) => {
setSelection({ a: a.name, b: b?.id });
form.current?.setFieldsValue({
field: b?.field,
spec: { ...currentSpec, ...b?.spec }
});
};
// Trigger 1: modal opened
useEffect(() => {
if (!open) {
sessionRef.current++;
setSelection({});
return;
}
const session = ++sessionRef.current;
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
if (sessionRef.current !== session) return;
const first = as.items[0];
applySelection(first, findB(first.key, bs.items));
});
}, [open]);
// Trigger 2: user picks A
const handleAChange = (a) => {
applySelection(a, findB(a.key, listB));
};
// Trigger 3: user picks B
const handleBChange = (b) => {
setSelection((prev) => ({ ...prev, b: b.id }));
form.current?.setFieldsValue({ ...b.fields });
};
```
## Related
- Module/file structure for forms lives in the **create-crud-page** skill (section 3).
- Required-field validation: use `getRuleMessage`.
-1
View File
@@ -1 +0,0 @@
src/components/icon-font/iconfont/iconfont.js
-15
View File
@@ -1,15 +0,0 @@
module.exports = {
extends: require.resolve('@umijs/max/eslint'),
rules: {
'react/no-unstable-nested-components': 1,
'no-unused-vars': 'off',
'no-undef': 'error',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/class-name-casing': 'off'
},
globals: {
Global: 'readonly',
React: 'readonly'
},
ignorePatterns: ['public/static/']
};
+20 -2
View File
@@ -1,6 +1,7 @@
name: CI
on:
workflow_dispatch: {}
push:
branches:
- 'main'
@@ -13,7 +14,7 @@ on:
- 'v*-dev'
env:
NODE_VERSION: '21'
NODE_VERSION: '22'
jobs:
deps:
@@ -32,7 +33,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '${{ env.NODE_VERSION }}'
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Deps
run: scripts/deps
@@ -117,3 +118,20 @@ jobs:
remote_path: releases/${{ steps.version.outputs.version }}.tar.gz
accelerate: true
clean: false
trigger-backend:
needs: build-publish
if: (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-22.04
steps:
- name: Dispatch backend build
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DISPATCH_PAT }}
repository: gpustack/gpustack
event-type: ui-built
client-payload: |
{
"ref": "${{ github.ref }}",
"sha": "${{ github.sha }}"
}
+3
View File
@@ -13,3 +13,6 @@
.swc
.DS_Store
.idea
.claude/settings.local.json
/dist.zip
.cache
+3 -5
View File
@@ -1,13 +1,11 @@
{
"*.{md,json}": ["prettier --cache --write"],
"*.{js,jsx}": ["max lint --fix --eslint-only", "prettier --cache --write"],
"*.{css,less}": [
"max lint --fix --stylelint-only",
"prettier --cache --write"
],
"*.{css,less}": ["prettier --cache --write"],
"!public/vs/**": [],
"*.ts?(x)": [
"max lint --fix --eslint-only",
"prettier --cache --parser=typescript --write"
],
"src/locales/**/*.ts": ["npx tsx src/locales/check.ts"]
"src/locales/**/*.ts": ["node --import tsx src/locales/check.ts"]
}
+1
View File
@@ -0,0 +1 @@
22
+1 -2
View File
@@ -3,7 +3,6 @@ node_modules
.umi-production
public/static/*.js
public/static/*.css
src/components/icon-font/iconfont/iconfont.js
src/components/icon-font/iconfont/*.css
src/components/iconfont/
+1 -1
View File
@@ -3,5 +3,5 @@ module.exports = {
rules: {
'selector-class-pattern': null
},
ignoreFiles: ['public/static/*.css']
ignoreFiles: ['public/static/*.css', 'src/components/iconfont/iconfont.css']
};
+5
View File
@@ -0,0 +1,5 @@
# Agent Instructions
This project keeps a single source of truth for agent/contributor conventions in [`CLAUDE.md`](./CLAUDE.md). **Read [`CLAUDE.md`](./CLAUDE.md) and follow it.**
@CLAUDE.md
+151
View File
@@ -0,0 +1,151 @@
# Repo
This is the **open source UI** (`gpustack-ui`). Common `components`, `hooks`, and `utils` are published as `@gpustack/core-ui` and consumed throughout `src`.
**Always prioritize reusing common `components`, `hooks`, and `utils` from `@gpustack/core-ui`.**
Task-specific conventions live in skills: use **create-crud-page** when building a page module, **form-patterns** when building cascading/dependent forms.
# React State and Request Patterns
Keep data flow explicit, predictable, and performant. The triggering **action** is the source of truth for UI updates — not effect-driven synchronization.
## 1. Avoid effect-driven requests
Do not use request functions as `useEffect` dependencies. Trigger requests explicitly from user actions or lifecycle entry points.
```ts
// Avoid
useEffect(() => {
fetchData();
}, [fetchData]);
```
## 2. Form requests should be action-driven
- Fetch form data (e.g. `Select` options) when the form first opens.
- If later requests depend on interactions, trigger them inside the interaction handler.
- Do not rely on `useEffect` dependency changes.
```ts
// Recommended
const handleOnChange = (value) => {
fetchData(value);
};
```
## 3. Update related states together
When one action updates multiple related states, update them all directly in the handler. Do not sync via `useEffect` or derive indirectly via `useMemo`.
```ts
const handleOnChange = (value) => {
setState1(...);
setState2(...);
buildState(...);
};
```
## 4. Group strongly related state
If multiple states always update together, use a single state object instead of multiple `useState` calls — fewer rerenders, more predictable transitions.
```ts
const [state, setState] = useState({ state1: ..., state2: ..., state3: ... });
```
## 5. Prefer explicit state flow
Keep request execution, state updates, and derived calculations close to the triggering action. Avoid chaining business logic through multiple `useEffect` hooks.
```ts
// Prefer
const handleAction = () => {
fetchData();
setTableData(...);
setSelectedRow(...);
};
```
## 6. Avoid premature memoization
Do not use `useMemo` / `useCallback` unless there is a confirmed bottleneck. Overuse adds complexity, obscures state flow, and risks stale dependencies. Optimize only when necessary.
## 7. Keep request logic predictable
A user interaction should clearly show: what request fires, which states update, how the UI changes. Avoid indirect update chains from dependency-driven effects.
## 8. Prefer action-driven architecture
Prefer action-driven updates, explicit handlers, and localized state transitions over effect-driven synchronization, cross-hook implicit updates, and reactive chains between states.
# Styles
**Future direction (apply to all new code):** avoid `styled-components`. Prefer:
1. `createStyles` for component-scoped dynamic styles
2. CSS Modules (`xxx.module.less`) for structured static styles
Existing `styled-components` usage is legacy tech debt — do not migrate it wholesale, but do not add new `styled-components` either. Theme tokens (`var(--ant-color-*)`) work in all three approaches.
## Layout
Compose layout with Ant components, not hand-written `display: flex`.
- **1D flex** (row/column with `gap`, `align`, `justify`) → `Flex`. Do not write raw `display: flex` in new code.
- **Inline sequence** of a few elements with uniform spacing → `Space`.
- **Page/grid columns** → `Row` / `Col`.
Drive spacing with the theme scale (`Flex`/`Space` `gap`, or `var(--ant-*)` spacing tokens), not scattered `px` literals.
# Naming conventions
A page module lives under `src/pages/{module}` with this sub-structure: `components/`, `config/`, `forms/`, `hooks/`, `services/`, `index.tsx`. File naming:
- **Create/edit modal**: `add-{feature}-modal.tsx` (keep the `-modal` suffix even when built with `FormDrawer`).
- **Table columns hook**: `use-{feature}-columns.tsx`.
- **Open/close & request hooks**: `use-{verb}-{noun}.ts` (e.g. `use-create-user.ts`, `use-query-user-list.ts`).
- **Complex table cell**: extract into `{feature}-cell.tsx`.
# Config & types
- `config/types.ts` — TypeScript types. Form shape → `FormData`; table/list row → `ListItem`.
- `config/index.ts` — static constants, enums, and value/label maps (e.g. `XxxStatusValueMap`, `XxxStatusLabelMap`). Keep constants out of `types.ts`.
- **`Select` options that need i18n**: set `label` to the message key and add `locale: true` on the option — the field translates it at render. Omit `locale` for options whose label is already final text. Ref `src/pages/benchmark/config/index.ts`.
# Common components
Always check `@gpustack/core-ui` first. Frequently reused:
- **Drawer/Modal open/close**: `useBodyScroll`.
- **Form drawer / footer**: `FormDrawer`, `ModalFooter`.
- **Delete confirmation**: `DeleteModal`.
- **Search + bulk actions bar**: `FilterBar`.
- **Form fields**: `BaseSelect`, `Input` (labeled).
- **Text overflow**: `AutoTooltip`.
- **Icons**: `IconFont`.
- **Tags & status** (4 variants): see the section below.
- **Permission-gated visibility**: `Access` / `useAccess`.
- **Request hooks**: `useRequest` / `useQueryData` / `useQueryDataList`.
- **Table data fetching**: `useTableFetch`.
- **Submit guard** (prevent double-submit): `useSubmitLock`.
- **Tabbed forms**: `ScrollSpyTabs`.
# Tags & status indicators
Four core-ui components cover tag/status display in tables and lists. Pick by **what the value means**, not by how it looks — don't reach for a generic antd `Tag`:
- **`StatusTag`** — semantic status with a **dynamic message/detail** (tooltip, download, extra content). Use when a row's status carries variable text, e.g. a failed job with an error message. Colors come from `StatusColorMap` (error/warning/transitioning/success/inactive).
- **`StatusDot`** — colored dot + short label, **no message**. Use for a plain status/type cell where the value is a fixed enum (e.g. an event-type or log column). Same `StatusColorMap` palette; `inactive` dot is quaternary. If the status needs dynamic text, use `StatusTag` instead.
- **`ThemeTag`** — a **standalone category label** (independent content, e.g. a permission scope or a model name). Default neutral; wraps antd `Tag`.
- **`TextAttribute`** — a small neutral pill that is a **subordinate annotation following a primary text** (e.g. `key-name [custom]`), not a standalone tag. Manages its own leading margin. Two variants: `filled` (default) and `outlined`. Ref the name column in `src/pages/api-keys/hooks/use-keys-columns.tsx`.
Rule of thumb: semantic + dynamic text → `StatusTag`; semantic + fixed enum → `StatusDot`; independent category → `ThemeTag`; annotation of nearby text → `TextAttribute`.
# Dynamic add-item form fields
When building a form, select the add-item component from the **shape of the field's data** (its schema). Match the schema, don't hand-roll a list UI:
- **Plain object** (key→value map) → `LabelSelector`.
- **String array** → `ListInput`. Ref `src/pages/llmodels/forms/backend-parameters-list.tsx`.
- **Object array** → `MetadataList` with a custom item renderer per entry. Ref `src/pages/llmodels/forms/model-lora-list.tsx`.
+6 -1
View File
@@ -1,5 +1,6 @@
import { defineConfig } from '@umijs/max';
import keepAlive from './keep-alive';
import { extraMfsuExclude } from './mfsu.extensions';
import { compressionPluginConfig, monacoPluginConfig } from './plugins';
import proxy from './proxy';
import routes from './routes';
@@ -19,6 +20,9 @@ export default defineConfig({
history: {
type: 'hash'
},
define: {
'process.env.ENABLE_ENTERPRISE': process.env.ENABLE_ENTERPRISE
},
analyze: {
analyzerMode: 'server',
analyzerPort: 8888,
@@ -29,7 +33,7 @@ export default defineConfig({
defaultSizes: 'parsed' // stat // gzip
},
mfsu: {
exclude: ['lodash', 'ml-pca', 'monaco-editor']
exclude: ['lodash', 'ml-pca', ...extraMfsuExclude]
},
base: process.env.npm_config_base || '/',
...(isProduction
@@ -73,6 +77,7 @@ export default defineConfig({
antd: {
style: 'less'
},
title: 'GPUStack',
hash: true,
access: {},
model: {},
+9
View File
@@ -0,0 +1,9 @@
// Identity hook for build-time mfsu.exclude extensions. Tooling may
// overwrite this file to add package names that must skip MFSU's
// pre-bundling; the original is restored on cleanup. Mirrors
// `src/request.extensions.ts` / `src/access.extensions.ts`.
//
// MFSU bundles node_modules into immutable chunks at dev startup, so
// workspace-linked packages whose source you edit during dev must be
// excluded here or HMR won't pick up changes.
export const extraMfsuExclude: string[] = [];
+1 -1
View File
@@ -13,7 +13,7 @@ export function monacoPluginConfig(config: any) {
entry: 'monaco-yaml',
worker: {
id: 'monaco-yaml/yamlWorker',
entry: 'monaco-yaml/yaml.worker'
entry: 'monaco-yaml/yaml.worker.js'
}
}
]
+3 -1
View File
@@ -6,7 +6,8 @@ const proxyTableList = [
'v1-openai',
'version',
'proxy',
'update'
'update',
'grafana'
];
// @ts-ingore
@@ -30,5 +31,6 @@ export default function createProxyTable(target?: string) {
},
{}
);
return proxyTable;
}
+3
View File
@@ -0,0 +1,3 @@
// Identity hook for build-time route extensions. Tooling may overwrite
// this file to inject additional routes; the original is restored on cleanup.
export const applyRouteExtensions = <T>(base: T): T => base;
+235 -86
View File
@@ -1,6 +1,7 @@
import { keepAliveRoutes } from './keep-alive';
import { applyRouteExtensions } from './routes.extensions';
export default [
const baseRoutes = [
{
name: 'dashboard',
path: '/dashboard',
@@ -8,7 +9,10 @@ export default [
icon: 'icon-dashboard',
selectedIcon: 'icon-dashboard-filled',
defaultIcon: 'icon-dashboard',
access: 'canSeeAdmin',
// `canSeeOrgAdmin` widens to anyone the access seam grants
// admin-ish visibility — by default platform admin, plus
// whatever the routes extension chooses to allow.
access: 'canSeeOrgAdmin',
component: './dashboard',
routes: []
},
@@ -32,7 +36,7 @@ export default [
icon: 'icon-chat',
selectedIcon: 'icon-chat-filled',
defaultIcon: 'icon-chat',
component: './playground/index'
component: './playground/chat/index'
},
{
name: 'embedding',
@@ -42,7 +46,7 @@ export default [
icon: 'icon-embedding',
selectedIcon: 'icon-embedding-filled',
defaultIcon: 'icon-embedding',
component: './playground/embedding'
component: './playground/embedding/index'
},
{
name: 'rerank',
@@ -52,7 +56,7 @@ export default [
icon: 'icon-reranker',
selectedIcon: 'icon-reranker-filled',
defaultIcon: 'icon-reranker',
component: './playground/rerank'
component: './playground/rerank/index'
},
{
name: 'text2images',
@@ -62,7 +66,7 @@ export default [
icon: 'icon-image1',
selectedIcon: 'icon-image-filled',
defaultIcon: 'icon-image1',
component: './playground/images'
component: './playground/images/index'
},
{
name: 'speech',
@@ -72,8 +76,19 @@ export default [
icon: 'icon-audio1',
selectedIcon: 'icon-audio-filled',
defaultIcon: 'icon-audio1',
component: './playground/speech'
component: './playground/speech/index'
}
// {
// name: 'video',
// title: 'Video',
// path: '/playground/video',
// key: 'video',
// icon: 'icon-video-outline',
// hideInMenu: false,
// selectedIcon: 'icon-video-filled02',
// defaultIcon: 'icon-video-outline',
// component: './playground/video'
// }
]
},
{
@@ -85,6 +100,15 @@ export default [
path: '/models',
redirect: '/models/deployments'
},
{
name: 'userModels',
path: '/models/user-models',
key: 'userModels',
icon: 'icon-models',
selectedIcon: 'icon-models-filled',
defaultIcon: 'icon-models',
component: './llmodels/user-models'
},
{
name: 'modelCatalog',
path: '/models/catalog',
@@ -92,7 +116,7 @@ export default [
icon: 'icon-layers',
selectedIcon: 'icon-layers-filled',
defaultIcon: 'icon-layers',
access: 'canSeeAdmin',
access: 'canSeeOrgAdmin',
component: './llmodels/catalog'
},
{
@@ -102,18 +126,132 @@ export default [
icon: 'icon-rocket-launch1',
selectedIcon: 'icon-rocket-launch-fill',
defaultIcon: 'icon-rocket-launch1',
access: 'canSeeAdmin',
access: 'canSeeOrgAdmin',
component: './llmodels/index'
},
{
name: 'userModels',
path: '/models/user-models',
key: 'userModels',
icon: 'icon-models',
selectedIcon: 'icon-models-filled',
defaultIcon: 'icon-models',
access: 'canSeeUser',
component: './llmodels/user-models'
name: 'routes',
path: '/models/routes',
key: 'routes',
icon: 'icon-captive_portal',
selectedIcon: 'icon-captive_portal',
defaultIcon: 'icon-captive_portal',
access: 'canSeeOrgAdmin',
component: './model-routes/index'
},
{
name: 'providers',
path: '/models/providers',
key: 'modelProviders',
icon: 'icon-extension-outline',
selectedIcon: 'icon-extension-filled',
defaultIcon: 'icon-extension-outline',
access: 'canSeeOrgAdmin',
component: './maas-provider/index'
},
{
name: 'benchmark',
path: '/models/benchmark',
key: 'benchmark',
icon: 'icon-speed',
selectedIcon: 'icon-speed-filled',
defaultIcon: 'icon-speed',
access: 'canSeeOrgAdmin',
component: './benchmark/index'
},
{
name: 'benchmarkDetail',
path: '/models/benchmark/detail',
key: 'benchmarkDetail',
icon: 'icon-speed',
selectedIcon: 'icon-speed-filled',
defaultIcon: 'icon-speed',
access: 'canSeeOrgAdmin',
hideInMenu: true,
component: './benchmark/details'
},
{
name: 'backendsList',
path: '/models/backends',
key: 'backendsList',
icon: 'icon-backend',
selectedIcon: 'icon-backend-filled',
defaultIcon: 'icon-backend',
access: 'canSeeOrgAdmin',
component: './backends/index'
},
{
name: 'modelfiles',
path: '/models/modelfiles',
key: 'modelfiles',
icon: 'icon-files',
selectedIcon: 'icon-files-filled',
defaultIcon: 'icon-files',
access: 'canSeeOrgAdmin',
component: './resources/components/model-files'
}
]
},
{
name: 'gpuService',
path: '/gpu-service',
key: 'gpuService',
access: 'canSeeGpuService',
routes: [
{
path: '/gpu-service',
redirect: '/gpu-service/instances'
},
{
name: 'instances',
path: '/gpu-service/instances',
key: 'gpuServiceList',
icon: 'icon-cloud-outlined',
selectedIcon: 'icon-cloud-filled',
defaultIcon: 'icon-cloud-outlined',
component: './gpu-service/instances'
},
{
name: 'templates',
path: '/gpu-service/templates',
key: 'gpuServiceTemplates',
icon: 'icon-instance-template-outlined',
selectedIcon: 'icon-instance-template-filled',
defaultIcon: 'icon-instance-template-outlined',
component: './gpu-service/templates'
},
{
name: 'storage',
path: '/gpu-service/storage',
key: 'gpuServiceStorage',
icon: 'icon-database-outlined',
selectedIcon: 'icon-database-filled',
defaultIcon: 'icon-database-outlined',
component: './gpu-service/storage'
},
{
name: 'storageTypes',
path: '/gpu-service/storage-types',
key: 'gpuServiceStorageTypes',
icon: 'icon-storage-outlined',
// Storage types are tenant-scoped on the backend (Org owners
// can create/list their own), so the menu shouldn't be
// platform-admin-only. ``canSeeOrgAdmin`` keeps the gate at
// "admin or current-org owner" — Org members still don't see
// it, which matches the read/write model in the route.
access: 'canSeeOrgAdmin',
selectedIcon: 'icon-storage-filled',
defaultIcon: 'icon-storage-outlined',
component: './gpu-service/storage-types'
},
{
name: 'publicKeys',
path: '/gpu-service/public-keys',
key: 'gpuServicePublicKeys',
icon: 'icon-ssh-outlined',
selectedIcon: 'icon-ssh-filled',
defaultIcon: 'icon-ssh-outlined',
component: './gpu-service/public-keys'
}
]
},
@@ -121,12 +259,22 @@ export default [
name: 'resources',
path: '/resources',
key: 'resources',
access: 'canSeeAdmin',
access: 'canSeeOrgAdmin',
routes: [
{
path: '/resources',
redirect: '/resources/workers'
},
{
name: 'clusters',
path: '/resources/clusters/list',
key: 'clusters',
icon: 'icon-cluster2-outline',
selectedIcon: 'icon-cluster2-filled',
defaultIcon: 'icon-cluster2-outline',
component: './cluster-management/clusters',
subMenu: ['/resources/clusters/create']
},
{
name: 'workers',
path: '/resources/workers',
@@ -145,73 +293,9 @@ export default [
defaultIcon: 'icon-gpu1',
component: './resources/components/gpus'
},
{
name: 'backendsList',
path: '/resources/backends',
key: 'backendsList',
icon: 'icon-backend',
selectedIcon: 'icon-backend-filled',
defaultIcon: 'icon-backend',
access: 'canSeeAdmin',
component: './backends/index'
},
{
name: 'modelfiles',
path: '/resources/modelfiles',
key: 'modelfiles',
icon: 'icon-files',
selectedIcon: 'icon-files-filled',
defaultIcon: 'icon-files',
component: './resources/components/model-files'
}
]
},
{
name: 'clusterManagement',
path: '/cluster-management',
key: 'clusterManagement',
access: 'canSeeAdmin',
routes: [
{
path: '/cluster-management',
redirect: '/cluster-management/clusters/list'
},
{
name: 'clusters',
path: '/cluster-management/clusters/list',
key: 'clusters',
icon: 'icon-cluster2-outline',
selectedIcon: 'icon-cluster2-filled',
defaultIcon: 'icon-cluster2-outline',
component: './cluster-management/clusters',
subMenu: [
'/cluster-management/clusters/detail',
'/cluster-management/clusters/create'
]
},
{
name: 'clusterDetail',
path: '/cluster-management/clusters/detail',
key: 'clusterDetail',
icon: 'icon-cluster2-outline',
selectedIcon: 'icon-cluster2-filled',
defaultIcon: 'icon-cluster2-outline',
hideInMenu: true,
component: './cluster-management/cluster-detail'
},
{
name: 'clusterCreate',
path: '/cluster-management/clusters/create',
key: 'clusterCreate',
icon: 'icon-cluster2-outline',
selectedIcon: 'icon-cluster2-filled',
defaultIcon: 'icon-cluster2-outline',
hideInMenu: true,
component: './cluster-management/cluster-create'
},
{
name: 'credentials',
path: '/cluster-management/credentials',
path: '/resources/credentials',
key: 'credentials',
icon: 'icon-credential-outline',
selectedIcon: 'icon-credential-filled',
@@ -220,16 +304,69 @@ export default [
}
]
},
{
// Cross-resource consumption (tokens + GPU/CPU instances + storage).
// A folder so it matches the other top-level groups; more usage views can
// graduate in here later.
name: 'billingAndUsage',
path: '/usage',
key: 'usageGroup',
icon: 'icon-usage-outlined',
selectedIcon: 'icon-usage-filled',
defaultIcon: 'icon-usage-outlined',
routes: [
{
path: '/usage',
redirect: '/usage/overview'
},
{
name: 'usage',
path: '/usage/overview',
key: 'usage',
icon: 'icon-usage-outlined',
selectedIcon: 'icon-usage-filled',
defaultIcon: 'icon-usage-outlined',
component: './usage/index'
},
{
name: 'billing',
path: '/usage/billing',
key: 'billing',
icon: 'icon-billing-outlined',
selectedIcon: 'icon-billing-filled',
defaultIcon: 'icon-billing-outlined',
hideInMenu: process.env.ENABLE_ENTERPRISE === 'true',
// OSS exposes the menu as a teaser for the enterprise billing
// module. The page itself just renders an upsell notice — the real
// billing UI lives in the enterprise plugin and shadows this route
// via `routes.extensions.ts`.
component: './billing'
}
]
},
{
name: 'accessControl',
path: '/access-control',
key: 'accessControl',
access: 'canSeeAdmin',
routes: [
{
path: '/access-control',
redirect: '/access-control/users'
},
{
name: 'organizations',
path: '/access-control/organizations',
key: 'organizations',
icon: 'icon-org-outlined',
selectedIcon: 'icon-org-filled',
defaultIcon: 'icon-org-outlined',
// OSS exposes the menu to platform admins as a teaser for the
// enterprise multi-tenancy module. The page itself just renders
// an upsell notice — the real CRUD UI lives in the enterprise
// plugin and shadows this route via `routes.extensions.ts`.
access: 'canSeeAdmin',
component: './organizations'
},
{
name: 'users',
path: '/access-control/users',
@@ -237,7 +374,17 @@ export default [
icon: 'icon-users',
selectedIcon: 'icon-users-filled',
defaultIcon: 'icon-users',
access: 'canSeeAdmin',
component: './users'
},
{
name: 'apikeys',
path: '/access-control/api-keys',
key: 'apikeys',
selectedIcon: 'icon-key-filled',
icon: 'icon-key',
defaultIcon: 'icon-key',
component: './api-keys'
}
]
},
@@ -253,8 +400,8 @@ export default [
},
{
name: 'profile',
path: '/profile',
key: 'profile',
path: '/preferences',
key: 'preferences',
hideInMenu: true,
component: './profile',
icon: 'User'
@@ -275,3 +422,5 @@ export default [
component: './404'
}
];
export default applyRouteExtensions(baseRoutes);
+24 -10
View File
@@ -1,13 +1,27 @@
const child_process = require('child_process');
import { execSync } from 'child_process';
export const getBranchInfo = () => {
const latestCommit = child_process
.execSync('git rev-parse HEAD')
.toString()
.trim();
const versionTag = child_process
.execSync(`git tag --contains ${latestCommit}`)
.toString()
.trim();
return { version: versionTag || '', commitId: latestCommit.slice(0, 7) };
// git may be absent (source archive, bare container) or this tree may
// not be a git checkout. Swallow the failure and fall back to the env
// overrides below — losing build info shouldn't fail the build.
let latestCommit = '';
let versionTag = '';
try {
latestCommit = execSync('git rev-parse HEAD').toString().trim();
versionTag = execSync(`git tag --contains ${latestCommit}`)
.toString()
.trim();
} catch {
// Not a git checkout / git unavailable; rely on env overrides.
}
// Respect explicit GPUSTACK_UI_* overrides so a wrapping build that
// checks this source tree out as a sub-package can stamp its own
// release tag and commit id onto the UI (otherwise the panel reports
// the host tree's git HEAD, which the wrapper doesn't control).
const overrideVersion = process.env.GPUSTACK_UI_VERSION?.trim();
const overrideCommitId = process.env.GPUSTACK_UI_COMMIT_ID?.trim();
return {
version: overrideVersion || versionTag || '',
commitId: overrideCommitId || latestCommit.slice(0, 7)
};
};
+79
View File
@@ -0,0 +1,79 @@
import js from '@eslint/js';
import prettier from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import reactPlugin from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import unusedImports from 'eslint-plugin-unused-imports';
import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import tseslint from 'typescript-eslint';
export default defineConfig([
globalIgnores([
'public/static/',
'dist',
'src/.umi/',
'src/.umi-production/',
'src/.umi-test/',
'src/components/iconfont/'
]),
{
files: ['**/*.{ts,tsx,js,jsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
prettier
],
plugins: {
react: reactPlugin,
import: importPlugin,
'unused-imports': unusedImports
},
settings: {
react: {
version: 'detect'
},
'import/resolver': {
node: true,
typescript: true
}
},
languageOptions: {
ecmaVersion: 2020,
globals: {
...globals.browser,
...globals.node,
Global: 'readonly',
React: 'readonly',
JSX: 'readonly'
}
},
rules: {
'react/no-unstable-nested-components': 'warn',
'no-unused-vars': 'off',
'no-undef': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unnecessary-type-constraint': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': 'off',
'import/no-unresolved': 'off',
'import/no-duplicates': 'error',
'react-hooks/exhaustive-deps': 'off',
'react-hooks/preserve-manual-memoization': 'off',
'react-hooks/set-state-in-effect': 'off',
'react-hooks/refs': 'off',
'react-hooks/use-memo': 'off',
'react-hooks/immutability': 'off',
'no-unsafe-optional-chaining': 'off',
'no-empty': 'off',
'no-constant-condition': 'off',
'no-prototype-builtins': 'off',
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }]
}
}
]);
+31 -23
View File
@@ -1,9 +1,9 @@
{
"private": true,
"author": "jialin",
"author": "gpustack",
"scripts": {
"build": "max build",
"check:locales": "npx tsx ./src/locales/check.ts",
"check:locales": "node --import tsx ./src/locales/check.ts",
"dev": "max dev",
"format": "prettier --cache --write .",
"postinstall": "max setup",
@@ -12,13 +12,12 @@
"setup": "max setup",
"start": "npm run dev"
},
"resolutions": {
"immer": "^9.0.6"
},
"dependencies": {
"@ant-design/icons": "^5.5.1",
"@ant-design/pro-components": "^2.7.19",
"@ant-design/icons": "^6.1.0",
"@ant-design/pro-components": "3.1.0-0",
"@antv/g6": "^5.0.51",
"@braintree/sanitize-url": "^7.1.1",
"@gpustack/core-ui": "^1.0.42",
"@huggingface/gguf": "^0.1.7",
"@huggingface/hub": "^0.15.1",
"@huggingface/tasks": "^0.11.6",
@@ -26,45 +25,40 @@
"@orcid/bibtex-parse-js": "^0.0.25",
"@react-hook/resize-observer": "^2.0.2",
"@types/lodash": "^4.17.4",
"@umijs/max": "^4.2.11",
"@umijs/max": "^4.6.15",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"ahooks": "^3.8.5",
"ansi-to-html": "^0.7.2",
"antd": "^5.25.4",
"antd": "^6.3.3",
"antd-style": "^3.6.2",
"axios": "^1.8.2",
"classnames": "^2.5.1",
"clipboard": "^2.0.11",
"crypto-js": "^4.2.0",
"culori": "^4.0.2",
"dayjs": "^1.11.11",
"dompurify": "^3.2.6",
"driver.js": "^1.3.1",
"echarts": "^5.5.1",
"epubjs": "^0.3.93",
"file-saver": "^2.0.5",
"has-ansi": "^5.0.1",
"highlight.js": "^11.10.0",
"jdenticon": "^3.3.0",
"jotai": "^2.8.4",
"js-yaml": "^4.1.0",
"jszip": "^3.10.1",
"katex": "^0.16.21",
"lamejs": "github:zhuker/lamejs",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"mammoth": "^1.8.0",
"marked": "^14.1.0",
"minimatch": "^10.0.3",
"minimatch": "^3.1.2",
"ml-dataset-iris": "^1.2.1",
"ml-pca": "^4.1.1",
"monaco-editor": "^0.53.0",
"monaco-editor-webpack-plugin": "^7.1.0",
"monaco-yaml": "^5.4.0",
"monaco-editor": "^0.30.1",
"monaco-yaml": "^4.0.0",
"numeral": "^2.0.6",
"overlayscrollbars": "^2.10.0",
"overlayscrollbars-react": "^0.5.6",
"pdfjs-dist": "^4.7.76",
"query-string": "^9.0.0",
"rc-resize-observer": "^1.4.3",
"rc-virtual-list": "^3.14.8",
@@ -74,44 +68,58 @@
"react-hotkeys-hook": "^4.5.0",
"react-intersection-observer": "^9.16.0",
"react-markdown": "^9.0.3",
"react-router-dom": "^6.30.3",
"rehype-katex": "^7.0.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"remark-math": "^6.0.0",
"semver": "^7.7.3",
"simplebar-react": "^3.2.6",
"styled-components": "^6.1.15",
"tinycolor2": "^1.6.0",
"umi-presets-pro": "^2.0.3",
"wavesurfer.js": "^7.8.8",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
"wavesurfer.js": "^7.8.8"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^25.0.3",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/semver": "^7.7.1",
"@typescript-eslint/eslint-plugin": "^8.58.1",
"@umijs/case-sensitive-paths-webpack-plugin": "^1.0.1",
"@umijs/plugins": "^4.4.11",
"babel-plugin-named-asset-import": "^0.3.8",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"compression-webpack-plugin": "^11.1.0",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"eslint": "^8.56.0",
"eslint-plugin-unused-imports": "^3.2.0",
"eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-unused-imports": "^4.4.1",
"extract-css-loader": "^0.0.1",
"file-loader": "^6.2.0",
"globals": "^17.4.0",
"husky": "^9.0.11",
"less-loader": "^12.2.0",
"lint-staged": "^15.2.2",
"mini-css-extract-plugin": "^2.9.0",
"monaco-editor-webpack-plugin": "^4.2.0",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-packagejson": "^2.5.0",
"prettier-plugin-two-style-order": "^1.0.1",
"tsx": "^4.19.3",
"typescript": "^5.4.5",
"typescript-eslint": "^8.58.0",
"url-loader": "^4.1.1",
"webpack-bundle-analyzer": "^4.10.2",
"worker-loader": "^3.0.8"
},
"packageManager": "pnpm@9.3.0",
"pnpm": {
"overrides": {
"elliptic": "^6.6.1"
+2
View File
@@ -5,6 +5,8 @@ export default (api: IApi) => {
const info = JSON.parse(process.env.VERSION || '{}');
const env = process.env.NODE_ENV;
$('html').attr('lang', 'en');
$('html').attr('data-env', env);
$('html').attr(
+14392 -10619
View File
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const pnpmDir = path.resolve(__dirname, '../../../node_modules/.pnpm');
function findDir(prefix) {
return fs.readdirSync(pnpmDir).find((name) => name.startsWith(prefix));
}
const stylelintDir = findDir('stylelint@14.8.2');
if (!stylelintDir) {
console.error('Compatible stylelint@14.8.2 not found in workspace node_modules/.pnpm');
process.exit(1);
}
const stylelintBin = path.join(
pnpmDir,
stylelintDir,
'node_modules/stylelint/bin/stylelint.js'
);
const result = spawnSync(process.execPath, [stylelintBin, ...process.argv.slice(2)], {
stdio: 'inherit',
cwd: process.cwd(),
});
if (result.error) {
console.error(result.error);
}
process.exit(result.status ?? 1);
+14
View File
@@ -0,0 +1,14 @@
// Identity hook for build-time access-predicate extensions. Tooling may
// overwrite this file to widen predicates; the original is restored on
// cleanup. Mirrors `config/routes.extensions.ts`.
export type AccessPredicates = {
canSeeAdmin: boolean;
canSeeOrgAdmin: boolean;
canManageCurrentOrg: boolean;
canSeeUser: boolean;
canDelete: boolean;
canLogin: boolean;
};
export const applyAccessExtensions = <T extends AccessPredicates>(base: T): T =>
base;
+42 -5
View File
@@ -1,5 +1,11 @@
export default (initialState: { currentUser?: Global.UserInfo }) => {
const canSeeAdmin = !!(
import { applyAccessExtensions } from './access.extensions';
export default (initialState: {
currentUser?: Global.UserInfo;
hasKubernetesCluster?: boolean;
hasResourceEvents?: boolean;
}) => {
const isPlatformAdmin = !!(
initialState &&
initialState.currentUser &&
initialState.currentUser.is_admin
@@ -9,11 +15,42 @@ export default (initialState: { currentUser?: Global.UserInfo }) => {
initialState.currentUser &&
!initialState.currentUser.is_admin
);
// GPU Service is Kubernetes-only. We only gate visibility down when
// the probe in `getInitialState` came back with a definitive answer;
// `undefined` (probe failed / not yet ready) collapses to the
// role-based default so a transient network blip can't lock anyone
// out of the menu.
const hasKubernetesCluster = initialState?.hasKubernetesCluster;
// Having run GPU/CPU instances or storage (any resource_events) also unlocks
// GPU Service / the full Usage page — a user who used it keeps seeing it even
// without a current cluster. MaaS-only users (no cluster, no events) don't.
const hasResourceEvents = !!initialState?.hasResourceEvents;
return {
canSeeAdmin,
// Predicate roles, top-down by strictness:
// * `canSeeAdmin` — strictly platform admin (`users.is_admin`).
// Gates Users.
// * `canSeeOrgAdmin` — admin-style menus that work cross-org
// (Dashboard, Resources, Models, Cluster Management). Defaults
// to platform admin; extensions widen to include org admins.
// * `canSeeGpuService` — GPU Service menu. Anyone allowed to
// manage clusters (admins, Org owners) sees it; non-admins fall
// through to "show only if a Kubernetes cluster is actually
// reachable" so Org members without scheduling access don't see
// a dead-end menu item.
// * `canManageCurrentOrg` — pages that only make sense inside a
// specific org context (member / group management). Defaults to
// `false`; extensions widen when both an org is selected AND
// the caller is admin of it.
// Pass through `applyAccessExtensions` so build-time tooling can
// widen these without editing this file. Default is a no-op.
return applyAccessExtensions({
canSeeAdmin: isPlatformAdmin,
canSeeOrgAdmin: isPlatformAdmin,
canSeeGpuService:
isPlatformAdmin || hasKubernetesCluster !== false || hasResourceEvents,
canManageCurrentOrg: false,
canSeeUser,
canDelete: true,
canLogin: true
};
});
};
+84 -5
View File
@@ -1,21 +1,35 @@
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
import { userSettingsHelperAtom } from '@/atoms/settings';
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
import { setAtomStorage } from '@/atoms/utils';
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
import { COLOR_PRIMARY } from '@/config/theme/constants';
import { getGPUStackPlugin } from '@/plugins';
import { enterprisePluginReady } from '@/plugins/enterprise-ready';
import { GPUStackPluginManager } from '@/plugins/manager';
import { requestConfig } from '@/request-config';
import {
queryCurrentUserState,
queryVersionInfo,
updateCheck
} from '@/services/profile/apis';
import { fetchSystemConfig } from '@/services/system/query-system-config';
import { isOnline } from '@/utils';
import {
markInitialStateProbed,
probeAccessFlags
} from '@/utils/access-probes';
import { installTenantFetch } from '@/utils/install-fetch';
import {
IS_FIRST_LOGIN,
readState,
writeState
} from '@/utils/localstore/index';
import { RequestConfig, history } from '@umijs/max';
import '@gpustack/core-ui/style.css';
import { RequestConfig, history, request as umiRequest } from '@umijs/max';
import { message } from 'antd';
installTenantFetch();
// only for the first login and access from http://localhost
const checkDefaultPage = async (userInfo: any) => {
@@ -32,9 +46,29 @@ const checkDefaultPage = async (userInfo: any) => {
export async function getInitialState(): Promise<{
fetchUserInfo: () => Promise<Global.UserInfo>;
currentUser?: Global.UserInfo;
pluginData?: Record<string, any>;
hasKubernetesCluster?: boolean;
hasResourceEvents?: boolean;
}> {
const { location } = history;
// In open-source builds the promise resolves immediately.
await enterprisePluginReady;
// initialize plugins and merge enterprise locales
let pluginData = {};
try {
pluginData = await GPUStackPluginManager.initialize({
request: umiRequest,
setUserSettings: (value) => setAtomStorage(userSettingsHelperAtom, value),
setStorageUserSettings: (value) =>
setAtomStorage(userSettingsHelperAtom, value),
defaultColorPrimary: COLOR_PRIMARY
});
} catch (error) {
console.error('Failed to initialize plugins:', error);
}
const getUpdateCheck = async () => {
try {
const data = await updateCheck();
@@ -57,6 +91,37 @@ export async function getInitialState(): Promise<{
});
if (data.is_admin) {
getUpdateCheck();
fetchSystemConfig();
}
// Only commit a substantive user object. A truthy-but-empty
// `data` (e.g. server responded 200 with an empty body) would
// otherwise look like "logged in" to every `currentUser`
// reader and the access seam — break out instead and let the
// caller treat the request as failed.
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
// Commit the identity to atom storage (and so to localStorage)
// before returning. The access function — memoized on
// `initialState` and run once per commit — reads identity from
// localStorage; without this preemptive write the predicate
// sees the prior session's identity on its first evaluation
// after login, and stays stale until the next identity change
// (which usually doesn't come without a manual refresh).
try {
setAtomStorage(userAtom, data);
} catch (err) {
console.error('userAtom commit error:', err);
}
// Fire `onUserFetched` so plugins maintaining identity-scoped
// caches can seed them under the new identity before any
// caller commits this user to `initialState`. Errors here are
// swallowed and logged — fetchUserInfo must still return.
try {
await getGPUStackPlugin()?.login?.onUserFetched?.(data, {
request: umiRequest
});
} catch (err) {
console.error('onUserFetched plugin hook error:', err);
}
}
return data;
} catch (error: any) {
@@ -97,15 +162,29 @@ export async function getInitialState(): Promise<{
getAppVersionInfo();
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
const userInfo = await fetchUserInfo();
const [userInfo, accessFlags] = await Promise.all([
fetchUserInfo(),
probeAccessFlags()
]);
// Record that the probes ran for an authenticated user this page load
// (the refresh path) so the layout doesn't re-probe. A failed
// fetch (empty user — e.g. unauthenticated deep link that bounces to
// login) is NOT marked: the user will log in via SPA afterwards and
// the layout becomes responsible for probing.
if (userInfo?.username) {
markInitialStateProbed();
}
checkDefaultPage(userInfo);
return {
fetchUserInfo,
currentUser: userInfo
currentUser: userInfo,
pluginData,
...accessFlags
};
}
return {
fetchUserInfo
fetchUserInfo,
pluginData
};
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 286 64"><g><g><defs><path id="SVGID_1_" d="M47.5 17.6L25 4.8v52.6l9-5.2V37.4l6.8 3.9-.1-10.1-6.7-3.9v-5.9l13.5 7.9z"/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath><g clip-path="url(#SVGID_2_)"><linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-1.6" y1="335.05" x2="53.6" y2="335.05" gradientTransform="translate(0 -304)"><stop offset="0" stop-color="#ff6f00"/><stop offset="1" stop-color="#ffa800"/></linearGradient><path d="M-1.6 4.6h55.2v52.9H-1.6V4.6z" fill="url(#SVGID_3_)"/></g></g></g><g><g><defs><path id="SVGID_4_" d="M.5 17.6L23 4.8v52.6l-9-5.2V21.4L.5 29.3z"/></defs><clipPath id="SVGID_5_"><use xlink:href="#SVGID_4_" overflow="visible"/></clipPath><g clip-path="url(#SVGID_5_)"><linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-1.9" y1="335.05" x2="53.3" y2="335.05" gradientTransform="translate(0 -304)"><stop offset="0" stop-color="#ff6f00"/><stop offset="1" stop-color="#ffa800"/></linearGradient><path d="M-1.9 4.6h55.2v52.9H-1.9V4.6z" fill="url(#SVGID_6_)"/></g></g></g><path style="fill:#425066" d="M88.2 21.1h-10v27.7h-5.6V21.1h-10v-4.5h25.6v4.5z"/><path style="fill:#425066" d="M94.9 49.2c-3.4 0-6.2-1.1-8.3-3.2-2.1-2.1-3.2-5-3.2-8.6v-.7c0-2.2.4-4.4 1.4-6.4.9-1.8 2.2-3.3 3.9-4.4 1.7-1.1 3.6-1.6 5.6-1.6 3.3 0 5.8 1 7.6 3.1s2.7 5 2.7 8.8v2.2H88.9c.1 1.8.8 3.4 2 4.7 1.2 1.2 2.7 1.8 4.4 1.7 2.4.1 4.6-1.1 6-3l2.9 2.8c-1 1.4-2.3 2.6-3.8 3.3-1.8 1-3.6 1.4-5.5 1.3zm-.6-20.5c-1.4-.1-2.7.5-3.6 1.5-1 1.2-1.6 2.7-1.7 4.3h10.3v-.4c-.1-1.8-.6-3.2-1.4-4.1-1-.8-2.3-1.4-3.6-1.3zm19.4-3.9l.2 2.8c1.7-2.1 4.3-3.3 7-3.2 5 0 7.5 2.9 7.6 8.6v15.8h-5.4V33.3c0-1.5-.3-2.6-1-3.4-.7-.7-1.7-1.1-3.2-1.1-2.1-.1-4 1.1-4.9 2.9v17h-5.4v-24l5.1.1zm32.2 17.5c0-.9-.4-1.7-1.2-2.2-1.2-.7-2.6-1.1-3.9-1.3-1.6-.3-3.1-.8-4.6-1.5-2.7-1.3-4-3.2-4-5.6 0-2 1-4 2.6-5.2 1.7-1.4 4-2.1 6.6-2.1 2.9 0 5.2.7 6.9 2.1 1.7 1.3 2.7 3.4 2.6 5.5h-5.4c0-1-.4-1.9-1.2-2.6-.9-.7-1.9-1.1-3.1-1-1 0-2 .2-2.9.8-.7.5-1.1 1.3-1.1 2.2 0 .8.4 1.5 1 1.9.7.5 2.1.9 4.2 1.4 1.7.3 3.4.9 5 1.7 1.1.5 2 1.3 2.7 2.3.6 1 .9 2.1.9 3.3 0 2.1-1 4-2.7 5.2-1.8 1.3-4.1 2-7 2-1.8 0-3.6-.3-5.2-1.1-1.4-.6-2.7-1.6-3.6-2.9-.8-1.2-1.3-2.6-1.3-4h5.2c0 1.1.5 2.2 1.4 2.9 1 .7 2.3 1.1 3.5 1 1.4 0 2.5-.3 3.2-.8 1-.4 1.4-1.2 1.4-2zm8.1-5.7c0-2.2.4-4.4 1.4-6.3.9-1.8 2.2-3.3 3.9-4.3 1.8-1 3.8-1.6 5.8-1.5 3.2 0 5.9 1 7.9 3.1s3.1 4.8 3.3 8.3v1.3c0 2.2-.4 4.3-1.4 6.3-.8 1.8-2.2 3.3-3.9 4.3-1.8 1-3.8 1.6-5.9 1.5-3.4 0-6.1-1.1-8.1-3.4-2-2.2-3-5.2-3.1-9l.1-.3zm5.3.5c0 2.5.5 4.4 1.5 5.8 1.8 2.3 5.1 2.8 7.5 1 .4-.3.7-.6 1-1 1-1.4 1.5-3.5 1.5-6.2 0-2.4-.5-4.3-1.6-5.8-1.7-2.3-5-2.8-7.4-1.1-.4.3-.8.7-1.1 1-.8 1.4-1.4 3.5-1.4 6.3zm33.1-7.3c-.7-.1-1.5-.2-2.2-.2-2.5 0-4.1.9-5 2.8v16.4h-5.4v-24h5.1l.1 2.7c1.3-2.1 3.1-3.1 5.4-3.1.6 0 1.3.1 1.9.3l.1 5.1zm22.5 5.3h-13v13.7h-5.6V16.6h20.5v4.5h-14.9v9.6h13v4.4zm10.7 13.7h-5.4V16.5h5.4v32.3zm3.9-12.2c0-2.2.4-4.4 1.4-6.3.9-1.8 2.2-3.3 3.9-4.3 1.8-1 3.8-1.6 5.8-1.5 3.2 0 5.9 1 7.9 3.1s3.1 4.8 3.3 8.3v1.3c0 2.2-.4 4.3-1.3 6.3-.8 1.8-2.2 3.3-3.9 4.3-1.8 1-3.8 1.6-5.9 1.5-3.4 0-6.1-1.1-8.1-3.4-2-2.2-3.1-5.2-3.1-9v-.3zm5.4.5c0 2.5.5 4.4 1.5 5.8 1 1.4 2.6 2.2 4.3 2.1 1.7.1 3.3-.7 4.2-2.1 1-1.4 1.5-3.5 1.5-6.2 0-2.4-.5-4.3-1.6-5.8-1.7-2.3-5-2.8-7.4-1.1-.4.3-.8.7-1.1 1-.9 1.4-1.4 3.5-1.4 6.3zm41.2 4.3l3.8-16.5h5.2l-6.5 24h-4.4l-5.1-16.5-5.1 16.5h-4.4l-6.6-24h5.3l3.9 16.4 4.9-16.4h4.1l4.9 16.5z"/></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<!-- 顶面填充(亮面,高透明度) -->
<linearGradient id="cube-top-grad" x1="12" y1="2" x2="12" y2="12" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#f759ab" stop-opacity="0.18"/>
<stop offset="100%" stop-color="#c41d7f" stop-opacity="0.1"/>
</linearGradient>
<!-- 左侧面填充(暗面,低透明度) -->
<linearGradient id="cube-left-grad" x1="2" y1="7" x2="12" y2="17" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#c41d7f" stop-opacity="0.12"/>
<stop offset="100%" stop-color="#c41d7f" stop-opacity="0.04"/>
</linearGradient>
<linearGradient id="cube-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#f759ab"/>
<stop offset="100%" stop-color="#c41d7f"/>
</linearGradient>
</defs>
<!-- 顶面填充 -->
<polygon points="12,2 21.5,6.7 12,11.5 2.5,6.7" fill="url(#cube-top-grad)" />
<!-- 左侧面填充 -->
<polygon points="2.5,6.7 12,11.5 12,21.3 2.5,16.5" fill="url(#cube-left-grad)" />
<!-- 立方体全纯线外骨架(细化为圆角衔接) -->
<path d="M12 2L2.5 6.7M12 2l9.5 4.7M21.5 6.7L12 11.5M2.5 6.7L12 11.5M2.5 6.7v9.8l9.5 4.8M21.5 6.7v9.8l-9.5 4.8M12 11.5v9.8" stroke="url(#cube-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

+22
View File
@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<!-- 1. 定义专属微通透渐变填充 -->
<linearGradient id="img-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#d46b08" stop-opacity="0.12"/>
<stop offset="100%" stop-color="#d46b08" stop-opacity="0.04"/>
</linearGradient>
<!-- 2. 定义边框高精度渐变(亮橙到深橙,拉开层次) -->
<linearGradient id="img-stroke-grad" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#fa8c16"/>
<stop offset="100%" stop-color="#d46b08"/>
</linearGradient>
</defs>
<!-- 3. 精装底色充填层 -->
<rect x="3" y="3" width="18" height="18" rx="4" fill="url(#img-fill-grad)" />
<!-- 4. 高级柔和微圆角边框层 -->
<rect x="3" y="3" width="18" height="18" rx="4" stroke="url(#img-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 内部几何现代山脉线条 -->
<path d="M3 16l4-4a2 2 0 0 1 2.8 0l5.2 5.2M13 15l2.5-2.5a2 2 0 0 1 2.8 0l2.7 2.7" stroke="url(#img-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 标志性通透小钻石 -->
<rect x="14" y="6" width="4" height="4" rx="1.5" transform="rotate(45 16 8)" fill="#fa8c16" fill-opacity="0.3" stroke="url(#img-stroke-grad)" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+17
View File
@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<linearGradient id="chat-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#389e0d" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#389e0d" stop-opacity="0.02"/>
</linearGradient>
<linearGradient id="chat-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#73d13d"/>
<stop offset="100%" stop-color="#389e0d"/>
</linearGradient>
</defs>
<!-- 主现代对话框体(全部改为圆润R角,精装填充) -->
<path d="M18 4H6a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h7.5l3.5 3.5a1 1 0 0 0 1.5-.5V17a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3z" fill="url(#chat-fill-grad)" stroke="url(#chat-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 内部通透对话线条(细化为圆角代码采样块感) -->
<rect x="7" y="8" width="8" height="1.5" rx="0.75" fill="#73d13d" fill-opacity="0.3"/>
<rect x="7" y="11.5" width="10" height="1.5" rx="0.75" fill="#73d13d" fill-opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<linearGradient id="rank-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#08979c" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#08979c" stop-opacity="0.01"/>
</linearGradient>
<linearGradient id="rank-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#36cfc9"/>
<stop offset="100%" stop-color="#08979c"/>
</linearGradient>
</defs>
<!-- 数据权重条(全部改为高级圆角) -->
<rect x="11" y="4" width="10" height="2.5" rx="1.25" fill="#36cfc9" fill-opacity="0.1" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
<rect x="11" y="9" width="7.5" height="2.5" rx="1.25" fill="#36cfc9" fill-opacity="0.05" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
<rect x="11" y="14" width="5" height="2.5" rx="1.25" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
<!-- 基准线与立体双向指引箭头(优化为圆角) -->
<path d="M3 17l3 3 3-3M6 4v16" stroke="url(#rank-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.5 5.5L6 4l1.5 1.5" stroke="url(#rank-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<linearGradient id="stt-fill-grad" x1="12" y1="3" x2="12" y2="14" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#40a9ff" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#1677ff" stop-opacity="0.05"/>
</linearGradient>
<linearGradient id="stt-stroke-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#40a9ff"/>
<stop offset="100%" stop-color="#1677ff"/>
</linearGradient>
</defs>
<!-- 麦克风核心体:精装通透填充 -->
<rect x="8.5" y="3" width="7" height="11" rx="3.5" fill="url(#stt-fill-grad)" stroke="url(#stt-stroke-grad)" stroke-width="1.8" />
<!-- 内部音膜立体结构线(细化为点状) -->
<line x1="10" y1="8" x2="14" y2="8" stroke="url(#stt-stroke-grad)" stroke-width="1" stroke-dasharray="1 2"/>
<!-- 悬挂外托架与底座(全部改为高级圆角) -->
<path d="M5 10a7 7 0 0 0 14 0M12 17v4M8 21h8" stroke="url(#stt-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<defs>
<!-- 1. 核心高通透深蓝渐变充填 -->
<linearGradient id="tts-v2-fill" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#2f54eb" stop-opacity="0.15"/>
<stop offset="100%" stop-color="#1d39c4" stop-opacity="0.03"/>
</linearGradient>
<!-- 2. 精准调校的专属蓝色渐变边框 -->
<linearGradient id="tts-v2-stroke" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#2f54eb"/>
<stop offset="100%" stop-color="#1d39c4"/>
</linearGradient>
</defs>
<!-- 左侧:低频辅助声波(大间距,带现代圆角) -->
<rect x="4" y="8" width="2.2" height="8" rx="1.1" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round"/>
<!-- 中央:核心高频声波主体(拉大宽度,注入通透水晶质感) -->
<rect x="10.4" y="2" width="3.2" height="20" rx="1.6" fill="url(#tts-v2-fill)" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 右侧:高频衰减声波(保持几何对称与呼吸感) -->
<rect x="17.8" y="5" width="2.2" height="14" rx="1.1" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>AI360</title><path clip-rule="evenodd" d="M12 0h.018c1.473-.002 2.88.261 4.179.754C20.755 2.456 24 6.85 24 12c0 6.627-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0zm8.604 18.967A11.024 11.024 0 0023.07 12c0-1.717-.39-3.344-1.089-4.794a2.59 2.59 0 01-3.214.62 6.278 6.278 0 01-1.333-.992C16.283 5.73 15.109 4.66 13.696 3.9c-3.211-1.729-6.825-1.501-9.695.447A11.033 11.033 0 00.93 12c0 1.663.367 3.241 1.024 4.657.75-.973 2.131-1.346 3.232-.71.667.384 1.257.92 1.837 1.447l.176.16c1.365 1.234 2.794 2.355 4.558 2.965 3.053 1.053 6.356.437 8.847-1.552z" fill="url(#lobe-icons-ai-360-fill-0)" fill-rule="evenodd"></path><path d="M5.643 10.312c-.83.11-1.401.766-1.408 1.618a1.715 1.715 0 001.45 1.72c.805.128 1.64-.426 1.87-1.26.046-.167.076-.338.106-.51.025-.14.05-.282.084-.42.318-1.317 1.237-1.95 2.788-1.93 1.086.013 1.318.271 1.68 1.855.017.076.043.151.07.226.26.714.976 1.17 1.67 1.065a1.647 1.647 0 001.38-1.438c.083-.729-.348-1.264-1.122-1.575-.34-.136-.664-.158-.995-.141-.726.037-1.121-.36-1.339-.977a3.359 3.359 0 01-.134-.65c-.014-.093-.027-.186-.043-.278-.156-.887-.835-1.51-1.669-1.532-.791-.02-1.464.551-1.665 1.418l-.06.27-.025.117c-.355 1.636-.974 2.205-2.638 2.422z" fill="url(#lobe-icons-ai-360-fill-1)"></path><path d="M18.059 13.644c.989-.206 1.577-.838 1.592-1.697.015-.83-.624-1.582-1.46-1.724-.77-.13-1.599.383-1.844 1.18-.069.22-.117.448-.165.676-.06.29-.122.58-.225.854-.367.986-1.593 1.546-2.926 1.394-.824-.095-1.106-.446-1.342-1.674-.18-.938-.864-1.535-1.681-1.467-.85.07-1.515.829-1.468 1.673.05.892.678 1.44 1.705 1.489 1.375.064 1.75.396 1.926 1.787.067.531.267.967.685 1.288 1.02.783 2.407.208 2.66-1.108l.022-.114c.152-.796.3-1.577 1.04-2.101.36-.255.761-.326 1.166-.397.105-.019.21-.037.315-.06z" fill="url(#lobe-icons-ai-360-fill-2)"></path><path d="M13.83 7.961a.755.755 0 11-1.51 0 .755.755 0 011.51 0z" fill="url(#lobe-icons-ai-360-fill-3)"></path><path d="M10.809 16.678a.755.755 0 100-1.511.755.755 0 000 1.51z" fill="url(#lobe-icons-ai-360-fill-4)"></path><defs><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-ai-360-fill-0" x1="12" x2="12" y1="0" y2="24"><stop stop-color="#12B7FA"></stop><stop offset="1" stop-color="#006ffb"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-ai-360-fill-1" x1="11.943" x2="11.943" y1="6.085" y2="17.778"><stop stop-color="#006ffb"></stop><stop offset="1" stop-color="#12B7FA"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-ai-360-fill-2" x1="11.943" x2="11.943" y1="6.085" y2="17.778"><stop stop-color="#006ffb"></stop><stop offset="1" stop-color="#12B7FA"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-ai-360-fill-3" x1="11.943" x2="11.943" y1="6.085" y2="17.778"><stop stop-color="#006ffb"></stop><stop offset="1" stop-color="#12B7FA"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-ai-360-fill-4" x1="11.943" x2="11.943" y1="6.085" y2="17.778"><stop stop-color="#006ffb"></stop><stop offset="1" stop-color="#12B7FA"></stop></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>AlibabaCloud</title><path d="M14.752 4.64h5.274C22.242 4.64 24 6.475 24 8.691V15.8a3.947 3.947 0 01-3.974 3.975h-5.274l1.299-1.835 3.822-1.222c.688-.23 1.146-.918 1.146-1.605v-5.81c0-.687-.458-1.375-1.146-1.605L16.05 6.475l-1.3-1.835zM2.98 15.111c0 .688.46 1.376 1.147 1.606l3.822 1.146 1.3 1.835H3.974A3.947 3.947 0 010 15.723V8.69c0-2.216 1.758-4.05 3.975-4.05h5.273L7.95 6.474 4.127 7.697c-.688.23-1.146.918-1.146 1.606v5.808z" fill="#FF6A00"></path><path d="M16.051 11.213H8.025v1.835h8.026v-1.835z" fill="#FF6A00"></path></svg>

After

Width:  |  Height:  |  Size: 656 B

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>AzureAI</title><path clip-rule="evenodd" d="M16.233 0c.713 0 1.345.551 1.572 1.329.227.778 1.555 5.59 1.555 5.59v9.562h-4.813L14.645 0h1.588z" fill="url(#lobe-icons-azure-ai-fill-0)" fill-rule="evenodd"></path><path d="M23.298 7.47c0-.34-.275-.6-.6-.6h-2.835a3.617 3.617 0 00-3.614 3.615v5.996h3.436a3.617 3.617 0 003.613-3.614V7.47z" fill="url(#lobe-icons-azure-ai-fill-1)"></path><path clip-rule="evenodd" d="M16.233 0a.982.982 0 00-.989.989l-.097 18.198A4.814 4.814 0 0110.334 24H1.6a.597.597 0 01-.567-.794l7-19.981A4.819 4.819 0 0112.57 0h3.679-.016z" fill="url(#lobe-icons-azure-ai-fill-2)" fill-rule="evenodd"></path><defs><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-ai-fill-0" x1="18.242" x2="14.191" y1="16.837" y2=".616"><stop stop-color="#712575"></stop><stop offset=".09" stop-color="#9A2884"></stop><stop offset=".18" stop-color="#BF2C92"></stop><stop offset=".27" stop-color="#DA2E9C"></stop><stop offset=".34" stop-color="#EB30A2"></stop><stop offset=".4" stop-color="#F131A5"></stop><stop offset=".5" stop-color="#EC30A3"></stop><stop offset=".61" stop-color="#DF2F9E"></stop><stop offset=".72" stop-color="#C92D96"></stop><stop offset=".83" stop-color="#AA2A8A"></stop><stop offset=".95" stop-color="#83267C"></stop><stop offset="1" stop-color="#712575"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-ai-fill-1" x1="19.782" x2="19.782" y1=".34" y2="23.222"><stop stop-color="#DA7ED0"></stop><stop offset=".08" stop-color="#B17BD5"></stop><stop offset=".19" stop-color="#8778DB"></stop><stop offset=".3" stop-color="#6276E1"></stop><stop offset=".41" stop-color="#4574E5"></stop><stop offset=".54" stop-color="#2E72E8"></stop><stop offset=".67" stop-color="#1D71EB"></stop><stop offset=".81" stop-color="#1471EC"></stop><stop offset="1" stop-color="#1171ED"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-ai-fill-2" x1="18.404" x2="3.236" y1=".859" y2="25.183"><stop stop-color="#DA7ED0"></stop><stop offset=".05" stop-color="#B77BD4"></stop><stop offset=".11" stop-color="#9079DA"></stop><stop offset=".18" stop-color="#6E77DF"></stop><stop offset=".25" stop-color="#5175E3"></stop><stop offset=".33" stop-color="#3973E7"></stop><stop offset=".42" stop-color="#2772E9"></stop><stop offset=".54" stop-color="#1A71EB"></stop><stop offset=".68" stop-color="#1371EC"></stop><stop offset="1" stop-color="#1171ED"></stop></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Baichuan</title><defs><linearGradient id="lobe-icons-baichuan-fill" x1="17.764%" x2="100%" y1="8.678%" y2="91.322%"><stop offset="0%" stop-color="#FEC13E"></stop><stop offset="100%" stop-color="#FF6933"></stop></linearGradient></defs><path d="M7.333 2h-3.2l-2 4.333V17.8L0 22h5.2l2.028-4.2L7.333 2zm7.334 0h-5.2v20h5.2V2zM16.8 7.733H22V22h-5.2V7.733zM22 2h-5.2v4.133H22V2z" fill="url(#lobe-icons-baichuan-fill)" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 569 B

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>BaiduCloud</title><path d="M21.715 5.61l-3.983 2.31a.903.903 0 01-.896 0L12.44 5.384a.903.903 0 00-.897 0L7.156 7.92a.903.903 0 01-.896 0L2.276 5.617 12.002 0l9.713 5.61z" fill="#5BCA87"></path><path d="M18.641 9.467a.89.89 0 00-.438.77v5.072a.896.896 0 01-.445.77l-4.428 2.51a.884.884 0 00-.445.777v4.607l4.429-2.536 5.31-3.047V7.157l-3.983 2.31z" fill="#EC5D3E"></path><path d="M10.98 18.941a.936.936 0 00-.305-.352l-4.429-2.516a.903.903 0 01-.431-.764v-5.078a.89.89 0 00-.452-.757l-.451-.26L1.38 7.158V18.39l5.311 3.047L11.126 24v-4.608a.881.881 0 00-.146-.45z" fill="#2464F5"></path></svg>

After

Width:  |  Height:  |  Size: 717 B

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>BaiLian</title><path d="M6.336 8.919v6.162l5.335-3.083L6.337 8.92z" fill="#1C54E3"></path><path d="M21.394 5.288s-.006-.006-.01-.006L17.01 2.754 6.336 8.92l5.335 3.082 9.701-5.6.016-.01a.635.635 0 00.006-1.1v-.003z" fill="#AA9AFF"></path><path d="M21.71 12.465a.62.62 0 00-.316.085s-.006 0-.009.003l-4.375 2.528 5.05 2.915h.006a2.06 2.06 0 00.28-1.04v-3.855a.637.637 0 00-.636-.636z" fill="#00EAD1"></path><path d="M22.06 17.996l-5.05-2.915L6.34 21.242l4.27 2.465s.016.006.022.012a2.102 2.102 0 002.093 0c.006-.003.016-.006.022-.012l8.538-4.93c.003 0 .006-.003.01-.006.321-.183.589-.45.775-.772h-.006l-.004-.003z" fill="#00CEC9"></path><path d="M11.672 11.998l-5.336 3.083-1.444.832-3.605 2.083H1.28c.173.303.416.555.709.738l.078.044.016.01.02.012 4.232 2.442 10.671-6.161-5.335-3.082z" fill="#00EAD1"></path><path d="M12.74.29c-.1-.06-.208-.107-.315-.148-.02-.006-.038-.016-.057-.022a2.121 2.121 0 00-.7-.12c-.233 0-.457.038-.668.11l-.031.01a2.196 2.196 0 00-.372.17L2.068 5.222s-.003 0-.006.003c-.324.183-.592.451-.781.773h.006l5.049 2.918L17.01 2.758 12.74.29z" fill="#7347FF"></path><path d="M1.287 6.001H1.28A2.06 2.06 0 001 7.041v9.915c0 .378.1.735.28 1.043h.007l5.049-2.918V8.919l-5.05-2.918z" fill="#0423DA"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Bedrock</title><defs><linearGradient id="lobe-icons-bedrock-fill" x1="80%" x2="20%" y1="20%" y2="80%"><stop offset="0%" stop-color="#6350FB"></stop><stop offset="50%" stop-color="#3D8FFF"></stop><stop offset="100%" stop-color="#9AD8F8"></stop></linearGradient></defs><path d="M13.05 15.513h3.08c.214 0 .389.177.389.394v1.82a1.704 1.704 0 011.296 1.661c0 .943-.755 1.708-1.685 1.708-.931 0-1.686-.765-1.686-1.708 0-.807.554-1.484 1.297-1.662v-1.425h-2.69v4.663a.395.395 0 01-.188.338l-2.69 1.641a.385.385 0 01-.405-.002l-4.926-3.086a.395.395 0 01-.185-.336V16.3L2.196 14.87A.395.395 0 012 14.555L2 14.528V9.406c0-.14.073-.27.192-.34l2.465-1.462V4.448c0-.129.062-.249.165-.322l.021-.014L9.77 1.058a.385.385 0 01.407 0l2.69 1.675a.395.395 0 01.185.336V7.6h3.856V5.683a1.704 1.704 0 01-1.296-1.662c0-.943.755-1.708 1.685-1.708.931 0 1.685.765 1.685 1.708 0 .807-.553 1.484-1.296 1.662v2.311a.391.391 0 01-.389.394h-4.245v1.806h6.624a1.69 1.69 0 011.64-1.313c.93 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708a1.69 1.69 0 01-1.64-1.314H13.05v1.937h4.953l.915 1.18a1.66 1.66 0 01.84-.227c.931 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708-.93 0-1.685-.765-1.685-1.708 0-.346.102-.668.276-.937l-.724-.935H13.05v1.806zM9.973 1.856L7.93 3.122V6.09h-.778V3.604L5.435 4.669v2.945l2.11 1.36L9.712 7.61V5.334h.778V7.83c0 .136-.07.263-.184.335L7.963 9.638v2.081l1.422 1.009-.446.646-1.406-.998-1.53 1.005-.423-.66 1.605-1.055v-1.99L5.038 8.29l-2.26 1.34v1.676l1.972-1.189.398.677-2.37 1.429V14.3l2.166 1.258 2.27-1.368.397.677-2.176 1.311V19.3l1.876 1.175 2.365-1.426.398.678-2.017 1.216 1.918 1.201 2.298-1.403v-5.78l-4.758 2.893-.4-.675 5.158-3.136V3.289L9.972 1.856zM16.13 18.47a.913.913 0 00-.908.92c0 .507.406.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zm3.63-3.81a.913.913 0 00-.908.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92zm1.555-4.99a.913.913 0 00-.908.92c0 .507.407.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zM17.296 3.1a.913.913 0 00-.907.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92z" fill="url(#lobe-icons-bedrock-fill)" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Claude</title><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="#D97757" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cloudflare</title><path d="M16.493 17.4c.135-.52.08-.983-.161-1.338-.215-.328-.592-.519-1.05-.519l-8.663-.109a.148.148 0 01-.135-.082c-.027-.054-.027-.109-.027-.163.027-.082.108-.164.189-.164l8.744-.11c1.05-.054 2.153-.9 2.556-1.937l.511-1.31c.027-.055.027-.11.027-.164C17.92 8.91 15.66 7 12.942 7c-2.503 0-4.628 1.638-5.381 3.903a2.432 2.432 0 00-1.803-.491c-1.21.109-2.153 1.092-2.287 2.32-.027.328 0 .628.054.9C1.56 13.688 0 15.326 0 17.319c0 .19.027.355.027.545 0 .082.08.137.161.137h15.983c.08 0 .188-.055.215-.164l.107-.437" fill="#F38020"></path><path d="M19.238 11.75h-.242c-.054 0-.108.054-.135.109l-.35 1.2c-.134.52-.08.983.162 1.338.215.328.592.518 1.05.518l1.855.11c.054 0 .108.027.135.082.027.054.027.109.027.163-.027.082-.108.164-.188.164l-1.91.11c-1.05.054-2.153.9-2.557 1.937l-.134.355c-.027.055.026.137.107.137h6.592c.081 0 .162-.055.162-.137.107-.41.188-.846.188-1.31-.027-2.62-2.153-4.777-4.762-4.777" fill="#FCAD32"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cohere</title><path clip-rule="evenodd" d="M8.128 14.099c.592 0 1.77-.033 3.398-.703 1.897-.781 5.672-2.2 8.395-3.656 1.905-1.018 2.74-2.366 2.74-4.18A4.56 4.56 0 0018.1 1H7.549A6.55 6.55 0 001 7.55c0 3.617 2.745 6.549 7.128 6.549z" fill="#39594D" fill-rule="evenodd"></path><path clip-rule="evenodd" d="M9.912 18.61a4.387 4.387 0 012.705-4.052l3.323-1.38c3.361-1.394 7.06 1.076 7.06 4.715a5.104 5.104 0 01-5.105 5.104l-3.597-.001a4.386 4.386 0 01-4.386-4.387z" fill="#D18EE2" fill-rule="evenodd"></path><path d="M4.776 14.962A3.775 3.775 0 001 18.738v.489a3.776 3.776 0 007.551 0v-.49a3.775 3.775 0 00-3.775-3.775z" fill="#FF7759"></path></svg>

After

Width:  |  Height:  |  Size: 769 B

+1
View File
@@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Coze</title><path clip-rule="evenodd" d="M3.908 9.096A8.092 8.092 0 0111.998 1h.006c4.468 0 8.09 3.628 8.09 8.096v3.392h1.942c2.23 0 2.732 3.126.615 3.828l-2.556.85v1.466a1.947 1.947 0 01-2.818 1.742l-1.42-.707c-.067-.03-.149 0-.17.071-1.147 3.587-6.225 3.587-7.373 0a.123.123 0 00-.169-.07l-1.42.706c-1.29.65-2.817-.292-2.817-1.742v-1.466l-2.557-.85c-2.122-.697-1.614-3.828.615-3.828h1.942V9.096zm4.571 2.613a.784.784 0 00-.784.784v1.568a.784.784 0 101.568 0v-1.568a.784.784 0 00-.784-.784zm7.045.779a.784.784 0 100 1.568.784.784 0 000-1.568zm-6.186 3.415a.78.78 0 00.17.254 3.517 3.517 0 004.98 0 .782.782 0 10-1.106-1.107 1.958 1.958 0 01-2.767 0 .78.78 0 00-1.277.254.78.78 0 000 .6z"></path></svg>

After

Width:  |  Height:  |  Size: 866 B

Some files were not shown because too many files have changed in this diff Show More