# Add Custom Model Dialog (`addmodels-01`) - SignalOS page block

> Modal dialog for creating or editing a custom AI model. Supports provider selection, dynamic provider fields, advanced options (max tokens, temperature, temperature policy), connection testing, and create/update actions.

- **Version:** 0.1.5
- **Kind:** block (group: reusable-components) · **Category:** pages
- **Install:** `npx shadcn@latest add @signalos/addmodels-01`
- **Registry dependencies (pulled automatically):** @signalos/tokens, @signalos/utils, @signalos/button, @signalos/select, @signalos/form-dialog, @signalos/text-field
- **npm dependencies:** lucide-react@^1.7.0
- **Files installed:** `src/components/blocks/addmodels-01/CustomModelDialog.tsx`, `src/components/blocks/addmodels-01/CustomModelDialog.types.ts`, `src/components/blocks/addmodels-01/components/CustomForm.tsx`, `src/components/blocks/addmodels-01/CustomModelDialog.constants.ts`

## Access

This is a private registry: pulling source requires a `SIGNALOS_REGISTRY_TOKEN`
(GitHub fine-grained PAT with read access to the signal-widgets repo) and an
`@signalos` entry in components.json `"registries"`. Previews and this document are public.

## Usage

```tsx
// after: npx shadcn@latest add @signalos/addmodels-01
// mount the pulled page in a route, e.g. src/app/reusable-components/page.tsx
import { CustomModelDialog } from "@/components/blocks/addmodels-01/CustomModelDialog"

export default function Page() {
  return <CustomModelDialog />
}
```

## Props

### `CustomModelProviderKind`

```ts
export type CustomModelProviderKind =
  | "openai_compatible"
  | "azure_openai"
  | "bedrock"
  | "vertex"
  | "openai"
  | "anthropic"
  | "google_genai"
  | "groq"
  | "mistral"
  | "deepseek"
```

### `ProviderFieldType`

```ts
export type ProviderFieldType = "text" | "url" | "textarea" | "keyvalue"
```

### `ProviderField`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key` | `string` | yes | - |  |
| `label` | `string` | yes | - | Human-readable label shown above the input. |
| `required` | `boolean` | yes | - |  |
| `secret` | `boolean` | yes | - | When true, the input renders as a password field. |
| `type` | `ProviderFieldType` | yes | - |  |
| `placeholder` | `string \| undefined` | no | - |  |

### `CustomModelProvider`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `provider_kind` | `CustomModelProviderKind` | yes | - |  |
| `label` | `string` | yes | - | Display name shown in the provider select. |
| `package` | `string` | yes | - |  |
| `installed` | `boolean` | yes | - | False when the server-side package is not installed — row is disabled. |
| `temperature_max` | `number` | yes | - |  |
| `fields` | `ProviderField[]` | yes | - |  |

### `CustomModelFields`

```ts
export type CustomModelFields = Record<string, string>
```

### `CustomModelDialogProps`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `customModelDialogOpen` | `boolean \| undefined` | no | - | Whether the dialog is open. |
| `onCustomModelDialogOpenChange` | `((open: boolean) => void) \| undefined` | no | - | Called when the dialog requests to open or close. |
| `editingModel` | `boolean \| undefined` | no | - | True when editing an existing model; false/undefined for create mode. |
| `editingModelHasStoredSecrets` | `boolean \| undefined` | no | `false` | True when the model being edited already has a stored API key. |
| `isSavingModel` | `boolean` | yes | - | True while the save mutation is in-flight. |
| `onCustomModelSave` | `(() => void) \| undefined` | no | - | Called when the user confirms the create/update action. |
| `providers` | `CustomModelProvider[] \| undefined` | no | - | Available providers to populate the provider select. |
| `isLoadingProviders` | `boolean \| undefined` | no | - | True while the provider list is loading. |
| `modelName` | `string` | yes | - | Current value of the model name field. |
| `providerKind` | `"" \| CustomModelProviderKind` | yes | - | Currently selected provider kind, or empty string when none selected. |
| `fields` | `CustomModelFields` | yes | - | Dynamic field values keyed by {@link ProviderField.key}. |
| `selectedProvider` | `CustomModelProvider \| undefined` | no | - | The resolved provider object for the selected `providerKind`. |
| `isProviderMissing` | `boolean` | yes | - | True when the chosen provider's server-side package is not installed. |
| `isTesting` | `boolean` | yes | - | True while a connection test is running. |
| `isTestSuccess` | `boolean \| undefined` | no | - | True once a test has passed for the current form state. |
| `testFeedback` | `{ ok: boolean; message: string; } \| null \| undefined` | no | - | Feedback from the most recent connection test. |
| `canTest` | `boolean` | yes | - | True when the form has enough data to attempt a connection test. |
| `onNameChange` | `(value: string) => void` | yes | - | Called when the model name input changes. |
| `onProviderChange` | `(value: CustomModelProviderKind) => void` | yes | - | Called when the provider select changes. |
| `onFieldChange` | `(key: string, value: string) => void` | yes | - | Called when any dynamic provider field changes. |
| `onTest` | `() => void` | yes | - | Called when the user clicks "Test Connection". |
| `data-testid` | `string \| undefined` | no | - | Test identifier rendered as `data-testid` on the root element. |

## Changelog

# addmodels-01
## 0.1.5
- format the code
## 0.1.4

- No code changes; version bumped to satisfy content-hash check after initial registration.

## 0.1.0

- Initial release: modal dialog for creating or editing a custom AI model. Includes provider select, dynamic provider fields, advanced options (max tokens, temperature, temperature policy), connection test, and create/update action.
