Skip to content

feat(platform-ui): Add LLM Providers and Models entity pages #1396

@rowan-stein

Description

@rowan-stein

User Request

Add LLM Providers and LLM Models as new entity sections in the platform UI. They should appear under the Settings sidebar group with Create, List, and Edit pages that follow the same layout and styles as existing entity pages.

Specification

Overview

Two new entity types backed by the gateway's LLM REST API (/llm/v1):

LLM Provider — represents an external LLM endpoint (e.g., OpenAI, Anthropic)

  • Fields: id (uuid), endpoint (uri), authMethod (enum: "bearer"), createdAt, updatedAt
  • Create: POST /providers — body: { endpoint, authMethod, token } (all required)
  • Update: PATCH /providers/{id} — body: { endpoint?, authMethod?, token? }
  • List: GET /providers?page=1&perPage=20{ items, page, perPage, total }
  • Get: GET /providers/{id}
  • Delete: DELETE /providers/{id}

LLM Model — a named model reference linked to a provider

  • Fields: id (uuid), name, llmProviderId (uuid), remoteName, createdAt, updatedAt
  • Create: POST /models — body: { name, llmProviderId, remoteName } (all required)
  • Update: PATCH /models/{id} — body: { name?, llmProviderId?, remoteName? }
  • List: GET /models?page=1&perPage=20&providerId={uuid}{ items, page, perPage, total }
  • Get: GET /models/{id}
  • Delete: DELETE /models/{id}

1. Vite Proxy + HTTP Client

vite.config.ts — Add proxy entry for /llm:

'/llm': {
  target: process.env.VITE_LLM_GATEWAY_URL || 'http://llm-gateway:8080',
  changeOrigin: true,
},

src/api/http.ts — Add new gateway HTTP client:

export const llmHttp: HttpClient = wrap(createHttp('/llm/v1'));

2. API Module — src/api/modules/llmEntities.ts

Types (LLMProvider, LLMModel, CreateLLMProviderInput, UpdateLLMProviderInput, CreateLLMModelInput, UpdateLLMModelInput, PaginatedResponse<T>) and CRUD functions using llmHttp:

  • listProviders, getProvider, createProvider, updateProvider, deleteProvider
  • listModels, getModel, createModel, updateModel, deleteModel

3. TanStack Query Hooks

src/api/hooks/useLLMProviders.ts:

  • useLLMProviders(page, perPage) — query
  • useLLMProvider(id) — query (enabled when id is set)
  • useCreateLLMProvider() — mutation
  • useUpdateLLMProvider() — mutation
  • useDeleteLLMProvider() — mutation

src/api/hooks/useLLMModels.ts:

  • useLLMModels(page, perPage, providerId?) — query
  • useLLMModel(id) — query
  • useCreateLLMModel() — mutation
  • useUpdateLLMModel() — mutation
  • useDeleteLLMModel() — mutation

Query keys: ['llm', 'providers', ...] and ['llm', 'models', ...]. All mutations invalidate their respective query keys on success with notifySuccess/notifyError.

4. List Pages

src/pages/LLMProvidersListPage.tsx:

  • Layout: header bar (title "LLM Providers" + description + "New provider" button) → table → pagination
  • Table columns: Endpoint | Auth Method | Created | Actions (Edit/Delete)
  • Server-side pagination with page state
  • Empty state: "No LLM providers configured yet."
  • Delete via window.confirm() + mutation

src/pages/LLMModelsListPage.tsx:

  • Same layout
  • Table columns: Name | Remote Name | Provider (resolved endpoint) | Created | Actions
  • Fetches providers (page=1, perPage=100) for provider lookup map
  • Empty state: "No LLM models configured yet."

Both pages follow the styling from EntityListPage — border-b header, flex column overflow, sticky thead, agyn CSS variables.

5. Upsert Pages

src/pages/LLMProviderUpsertPage.tsx:

  • Props: mode: 'create' | 'edit'
  • Edit mode reads :id from route params, fetches provider
  • Fields: endpoint (Input, required, URL pattern), authMethod (SelectInput, options: bearer), token (Input type=password, required on create)
  • Uses react-hook-form + Form/FormField/FormItem/FormLabel/FormControl/FormMessage components
  • Layout: white bg, header with title + Cancel/Submit buttons, form body max-w-xl

src/pages/LLMModelUpsertPage.tsx:

  • Fields: name (Input, required), llmProviderId (SelectInput populated from useLLMProviders, required), remoteName (Input, required)
  • Same layout pattern

6. Navigation + Routing

src/layout/RootLayout.tsx:

  • Add to MENU_ITEM_ROUTES: llmProviders: '/settings/llm-providers', llmModels: '/settings/llm-models'
  • Add to MENU_ITEMS settings group (after existing llm):
    • { id: 'llmProviders', label: 'LLM Providers', icon: <Server ...> }
    • { id: 'llmModels', label: 'LLM Models', icon: <Bot ...> }

src/App.tsx:

<Route path="/settings/llm-providers" element={<LLMProvidersListPage />} />
<Route path="/settings/llm-providers/new" element={<LLMProviderUpsertPage mode="create" />} />
<Route path="/settings/llm-providers/:id/edit" element={<LLMProviderUpsertPage mode="edit" />} />
<Route path="/settings/llm-models" element={<LLMModelsListPage />} />
<Route path="/settings/llm-models/new" element={<LLMModelUpsertPage mode="create" />} />
<Route path="/settings/llm-models/:id/edit" element={<LLMModelUpsertPage mode="edit" />} />

7. Tests

src/pages/__tests__/llm-providers.test.tsx and src/pages/__tests__/llm-models.test.tsx:

  • Use MSW handlers for /llm/v1/providers and /llm/v1/models (both relative and absolute)
  • Use TestProviders + MemoryRouter from existing test utils
  • Test cases: list renders rows, create navigates on submit, edit loads existing values, delete confirms and removes, validation blocks empty submit

Files Summary

File Action
packages/platform-ui/vite.config.ts Modify — add /llm proxy
packages/platform-ui/src/api/http.ts Modify — add llmHttp
packages/platform-ui/src/api/modules/llmEntities.ts New
packages/platform-ui/src/api/hooks/useLLMProviders.ts New
packages/platform-ui/src/api/hooks/useLLMModels.ts New
packages/platform-ui/src/pages/LLMProvidersListPage.tsx New
packages/platform-ui/src/pages/LLMModelsListPage.tsx New
packages/platform-ui/src/pages/LLMProviderUpsertPage.tsx New
packages/platform-ui/src/pages/LLMModelUpsertPage.tsx New
packages/platform-ui/src/layout/RootLayout.tsx Modify — sidebar + routes
packages/platform-ui/src/App.tsx Modify — add routes
packages/platform-ui/src/pages/__tests__/llm-providers.test.tsx New
packages/platform-ui/src/pages/__tests__/llm-models.test.tsx New

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions