Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions apps/web/src/onboarding/provider-connect-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ export function ProviderConnectStep({
readonly tenantId: string;
readonly onConnected: (offering: ExistingOffering) => void;
readonly onError: (message: string) => void;
readonly onSkip: () => void;
/** Omitted outside onboarding: only the first-run flow can be skipped. */
readonly onSkip?: () => void;
}) {
const [selected, setSelected] = useState<string | null>(null);
const [apiKey, setApiKey] = useState("");
Expand Down Expand Up @@ -523,9 +524,11 @@ export function ProviderConnectStep({
<Button type="submit" variant="primary" disabled={submitting || waiting || !ready}>
{submitLabel}
</Button>
<Button type="button" variant="ghost" onClick={onSkip}>
Skip for now
</Button>
{onSkip === undefined ? null : (
<Button type="button" variant="ghost" onClick={onSkip}>
Skip for now
</Button>
)}
</div>
</form>
);
Expand Down
113 changes: 58 additions & 55 deletions apps/web/src/settings/credentials-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { SETTINGS_STRINGS } from "./strings";
import { ConfirmButton } from "../components/confirm-button";
import { ProviderConnectStep } from "../onboarding/provider-connect-step";
import { SettingsGroup, SettingsRow } from "./rows";
import { inferenceProviders, providerLabel } from "./inference-providers";

type CatalogProvider = typeof ModelProviderResponse.infer;
type CatalogOffering = typeof ModelOfferingResponse.infer;
Expand Down Expand Up @@ -279,66 +280,73 @@ export function CredentialsSection({ tenantId }: { readonly tenantId: string | n

return (
<QueryView query={query} label={SETTINGS_STRINGS.credentialsLoadError}>
{({ credentials }) => (
<SettingsGroup
title={SETTINGS_STRINGS.credentialsSectionTitle}
description={SETTINGS_STRINGS.credentialsSectionDescription}
action={
<Button variant="primary" onClick={() => setCreateOpen(true)}>
{SETTINGS_STRINGS.credentialsCreateAction}
</Button>
}
>
{del.error === null || del.error === undefined ? null : (
<p className="settings-inline-error" role="alert">
{SETTINGS_STRINGS.credentialsDeleteError}
</p>
)}
{signIn.error === null || signIn.error === undefined ? null : (
<p className="settings-inline-error" role="alert">
{SETTINGS_STRINGS.credentialsSignInAgainError}
</p>
)}
<CredentialsTable
credentials={credentials}
signingIn={signIn.isPending || login.data?.status === "pending"}
onEdit={setEditing}
onDelete={(credential) => del.mutate(credential)}
onSignIn={(credential) => signIn.mutate(credential)}
/>
<AddProviderDialog
tenantId={tenantId}
open={createOpen}
onOpenChange={setCreateOpen}
onConnected={() => {
setCreateOpen(false);
reload();
}}
/>
<EditCredentialDialog
credential={editing}
linked={editingLinkage}
onOpenChange={(open) => {
if (!open) setEditing(null);
}}
onSave={(input) => update.mutate(input)}
submitting={update.isPending}
error={update.error === null ? null : SETTINGS_STRINGS.credentialsEditError}
/>
</SettingsGroup>
)}
{({ credentials: all, providers }) => {
const inference = inferenceProviders(providers);
const credentials = all.filter((row) => inference.some((p) => p.id === row.providerId));
return (
<SettingsGroup
title={SETTINGS_STRINGS.credentialsSectionTitle}
description={SETTINGS_STRINGS.credentialsSectionDescription}
action={
<Button variant="primary" onClick={() => setCreateOpen(true)}>
{SETTINGS_STRINGS.credentialsCreateAction}
</Button>
}
>
{del.error === null || del.error === undefined ? null : (
<p className="settings-inline-error" role="alert">
{SETTINGS_STRINGS.credentialsDeleteError}
</p>
)}
{signIn.error === null || signIn.error === undefined ? null : (
<p className="settings-inline-error" role="alert">
{SETTINGS_STRINGS.credentialsSignInAgainError}
</p>
)}
<CredentialsTable
credentials={credentials}
providers={inference}
signingIn={signIn.isPending || login.data?.status === "pending"}
onEdit={setEditing}
onDelete={(credential) => del.mutate(credential)}
onSignIn={(credential) => signIn.mutate(credential)}
/>
<AddProviderDialog
tenantId={tenantId}
open={createOpen}
onOpenChange={setCreateOpen}
onConnected={() => {
setCreateOpen(false);
reload();
}}
/>
<EditCredentialDialog
credential={editing}
linked={editingLinkage}
onOpenChange={(open) => {
if (!open) setEditing(null);
}}
onSave={(input) => update.mutate(input)}
submitting={update.isPending}
error={update.error === null ? null : SETTINGS_STRINGS.credentialsEditError}
/>
</SettingsGroup>
);
}}
</QueryView>
);
}

function CredentialsTable({
credentials,
providers,
signingIn,
onEdit,
onDelete,
onSignIn,
}: {
readonly credentials: readonly Credential[];
readonly providers: readonly Provider[];
readonly signingIn: boolean;
readonly onEdit: (credential: Credential) => void;
readonly onDelete: (credential: Credential) => void;
Expand All @@ -357,7 +365,7 @@ function CredentialsTable({
{credentials.map((credential) => (
<SettingsRow
key={credential.id}
title={credential.name}
title={providerLabel(providers.find((p) => p.id === credential.providerId))}
meta={
<>
<span>{credential.type}</span>
Expand Down Expand Up @@ -424,12 +432,7 @@ function AddProviderDialog({
</DialogDescription>
</DialogHeader>
<DialogBody>
<ProviderConnectStep
tenantId={tenantId}
onConnected={onConnected}
onError={setError}
onSkip={() => onOpenChange(false)}
/>
<ProviderConnectStep tenantId={tenantId} onConnected={onConnected} onError={setError} />
{error !== null && (
<p className="settings-inline-error" role="alert">
{error}
Expand Down
18 changes: 18 additions & 0 deletions apps/web/src/settings/inference-providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ModelProviderPlugin } from "@intx/types";

import { PROVIDER_OPTIONS } from "../onboarding/provider-connect-step";
import type { Provider } from "./credentials-api";

/** Model providers only: MCP servers and hub-internal credentials hang off
* providers with other plugins and are managed elsewhere. */
export function inferenceProviders(providers: readonly Provider[]): readonly Provider[] {
return providers.filter((provider) => ModelProviderPlugin.allows(provider.plugin));
}

/** The picker's label when the row was created from it, else the row's own name. */
export function providerLabel(provider: Provider | undefined): string {
if (provider === undefined) return "Provider";
const name = provider.name.toLowerCase();
const option = PROVIDER_OPTIONS.find((candidate) => candidate.label.toLowerCase() === name);
return option?.label ?? provider.name;
}
5 changes: 3 additions & 2 deletions apps/web/src/settings/models-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { QueryView, toAPIQuery } from "@/lib/api-query";
import { listProviders, type Provider } from "./credentials-api";
import { getResolvedCatalog, type ModelInfo } from "./inference";
import { SettingsGroup, SettingsRow } from "./rows";
import { inferenceProviders, providerLabel } from "./inference-providers";
import { SETTINGS_STRINGS } from "./strings";

type ModelsData = {
Expand Down Expand Up @@ -48,7 +49,7 @@ export function ModelsSection({ tenantId }: { readonly tenantId: string | null }
description={SETTINGS_STRINGS.modelsSectionDescription}
>
<h3 className="settings-subhead">{SETTINGS_STRINGS.modelsProvidersHeading}</h3>
<ProvidersTable providers={providers} />
<ProvidersTable providers={inferenceProviders(providers)} />
<h3 className="settings-subhead">{SETTINGS_STRINGS.modelsCatalogHeading}</h3>
<ModelsTable models={models} />
</SettingsGroup>
Expand All @@ -69,7 +70,7 @@ function ProvidersTable({ providers }: { readonly providers: readonly Provider[]
return (
<>
{providers.map((provider) => (
<SettingsRow key={provider.id} title={provider.name} meta={provider.plugin} />
<SettingsRow key={provider.id} title={providerLabel(provider)} />
))}
</>
);
Expand Down
Loading