refactor: move shared AI model UI into modules/aiModels (#29040)

> 🤖 Refactoring with Cursor.

AI settings was importing model form fields, ModelSelector, and
ProviderIcon from AgentsPage. Those now live in
`site/src/modules/aiModels`.

No user-facing behavior change.

- Move ProviderIcon, ModelSelector, and the model admin form fields into
`modules/aiModels`
- Point AgentsPage and AI settings at the new paths
- Drop the leftover ChatModelAdminPanel ProviderIcon duplicate and the
knownModels barrel
- Update the known-models catalog generator paths
This commit is contained in:
Jake Howell
2026-09-08 14:34:51 +10:00
committed by GitHub
parent 93089d447f
commit 257c6f1dca
54 changed files with 72 additions and 97 deletions
+1 -1
View File
@@ -40,4 +40,4 @@ site/src/api/typesGenerated.ts linguist-generated=true
# AI Gateway prices
coderd/aibridge/prices/data/prices.json linguist-generated=true
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json linguist-generated=true
site/src/modules/aiModels/knownModels/knownModelsGenerated.json linguist-generated=true
@@ -29,7 +29,7 @@ concurrency:
env:
REFRESH_BRANCH: bot/aigateway-prices-refresh
PRICES_FILE: coderd/aibridge/prices/data/prices.json
CATALOG_FILE: site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json
CATALOG_FILE: site/src/modules/aiModels/knownModels/knownModelsGenerated.json
jobs:
refresh:
+1 -1
View File
@@ -46,4 +46,4 @@ codersdk/aiproviders_bedrock.go @ibetitsmike @johnstcn
# models.dev data, and they carry customer-visible cost numbers, so every
# refresh needs a review from someone who owns them.
coderd/aibridge/prices/data/prices.json @evgeniy-scherbina @ssncferreira @johnstcn
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json @evgeniy-scherbina @ssncferreira @johnstcn
site/src/modules/aiModels/knownModels/knownModelsGenerated.json @evgeniy-scherbina @ssncferreira @johnstcn
+3 -3
View File
@@ -1063,13 +1063,13 @@ coderd/aibridge/prices/data/prices.json: _gen/bin/aibridgepricesgen _gen/models-
# snapshot joined with the editorial curation in
# scripts/aibridgepricesgen/curation.json. Kept out of `make gen` for the
# same live-upstream-data reason as prices.json.
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json: _gen/bin/aibridgepricesgen _gen/models-dev.json | _gen
site/src/modules/aiModels/knownModels/knownModelsGenerated.json: _gen/bin/aibridgepricesgen _gen/models-dev.json | _gen
$(call atomic_write,_gen/bin/aibridgepricesgen -format=catalog -upstream _gen/models-dev.json,./scripts/biome_format.sh)
.PHONY: site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json
.PHONY: site/src/modules/aiModels/knownModels/knownModelsGenerated.json
gen/aibridge-prices: \
coderd/aibridge/prices/data/prices.json \
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json
site/src/modules/aiModels/knownModels/knownModelsGenerated.json
.PHONY: gen/aibridge-prices
gen/golden-files: \
+1 -1
View File
@@ -7,7 +7,7 @@ next regeneration will overwrite any changes.
The Make target fetches models.dev once into `_gen/models-dev.json`, applying
the upstream corrections in `scripts/aibridgepricesgen/overrides.jq`. Both
`prices.json` and the frontend known-models catalog at
`site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json`
`site/src/modules/aiModels/knownModels/knownModelsGenerated.json`
are generated from that single patched snapshot; the catalog is additionally
joined with the editorial curation in
`scripts/aibridgepricesgen/curation.json`.
+1 -1
View File
@@ -272,7 +272,7 @@ func TestCurationMatchesGeneratedCatalog(t *testing.T) {
curation := embeddedCuration(t)
data, err := os.ReadFile("../../site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json")
data, err := os.ReadFile("../../site/src/modules/aiModels/knownModels/knownModelsGenerated.json")
require.NoError(t, err)
var generated map[string][]catalogEntry
require.NoError(t, json.Unmarshal(data, &generated))
+1 -1
View File
@@ -78,7 +78,7 @@ Some end-to-end tests require a license. The Storybook MCP at `http://localhost:
## Performance
- `src/pages/AgentsPage/`, including `components/ChatElements/`, uses React Compiler. Do not add `useMemo`, `useCallback`, or `memo()` there.
- `src/pages/AgentsPage/`, including `components/ChatElements/`, and `src/modules/aiModels/` use React Compiler. Do not add `useMemo`, `useCallback`, or `memo()` there.
- `memo()` remains valid for list-item components rendered in a map because compiler memoization does not cross component boundaries.
- Isolate frequently changing state in a small child component instead of rerendering a large parent subtree.
- Throttle high-frequency handlers that set state with `requestAnimationFrame` or an established throttle utility.
+1
View File
@@ -19,6 +19,7 @@ const siteDir = new URL("..", import.meta.url).pathname;
// Directories opted in to React Compiler. Keep this list in sync with
// the include filter in vite.config.mts.
const targetDirs = [
"src/modules/aiModels",
"src/pages/AgentsPage",
"src/pages/AIBridgePage",
"src/pages/TemplateBuilder",
@@ -41,7 +41,7 @@ import {
findKnownModelByCanonicalId,
findKnownModelByExactAlias,
formatPricePerMillionTokens,
} from "./knownModels";
} from "./knownModels/catalog";
import {
isFieldConflictDisabled,
isVisibleWhenSatisfied,
@@ -13,15 +13,15 @@ import { Input } from "#/components/Input/Input";
import { Label } from "#/components/Label/Label";
import { normalizeProvider } from "#/modules/aiModels/helpers";
import type { FormHelpers } from "#/utils/formUtils";
import { applyKnownModelDefaults } from "./knownModels/applyKnownModelDefaults";
import {
findKnownModelByCanonicalId,
findKnownModelByExactAlias,
formatContextBadge,
getKnownModelsForProvider,
type KnownModel,
searchKnownModels,
} from "./knownModels";
import { applyKnownModelDefaults } from "./knownModels/applyKnownModelDefaults";
} from "./knownModels/catalog";
import type { KnownModel } from "./knownModels/types";
import { deepGet, deepSet, type ModelFormValues } from "./modelConfigFormLogic";
type ModelFormMode = "add" | "edit" | "duplicate";
@@ -76,7 +76,7 @@ const longNameModel: ModelSelectorOption = {
};
const meta: Meta<typeof ModelSelector> = {
title: "pages/AgentsPage/ChatElements/ModelSelector",
title: "modules/aiModels/ModelSelector",
component: ModelSelector,
args: {
options: openAIModels,
@@ -22,9 +22,9 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { formatReasoningEffort } from "#/modules/aiModels/helpers";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { formatProviderLabel as defaultFormatProviderLabel } from "#/utils/aiProviders";
import { formatReasoningEffort } from "../../utils/reasoningEffort";
export interface ModelSelectorOption {
id: string;
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { ProviderIcon } from "./ProviderIcon";
const meta: Meta<typeof ProviderIcon> = {
title: "pages/AISettingsPage/ProviderIcon",
title: "modules/aiModels/ProviderIcon",
component: ProviderIcon,
};
@@ -1,5 +1,6 @@
import { Building2Icon } from "lucide-react";
import { ExternalImage } from "#/components/ExternalImage/ExternalImage";
import { normalizeProvider } from "#/modules/aiModels/helpers";
type ProviderIconProps = {
provider: string;
@@ -8,7 +9,7 @@ type ProviderIconProps = {
};
export const getProviderIcon = (provider: string): string | undefined => {
switch (provider) {
switch (normalizeProvider(provider)) {
case "openai":
return "/icon/openai.svg";
case "anthropic":
+4
View File
@@ -1,3 +1,7 @@
export function normalizeProvider(provider: string): string {
return provider.trim().toLowerCase();
}
/** Display label for an effort value, e.g. "xhigh" renders as "Xhigh". */
export const formatReasoningEffort = (value: string): string =>
value.charAt(0).toUpperCase() + value.slice(1);
@@ -5,7 +5,8 @@ import {
type ApplyKnownModelDefaultsResult,
applyKnownModelDefaults,
} from "./applyKnownModelDefaults";
import { findKnownModelByCanonicalId, type KnownModel } from "./index";
import { findKnownModelByCanonicalId } from "./catalog";
import type { KnownModel } from "./types";
const requireKnownModel = (
provider: string,
@@ -6,7 +6,7 @@ import {
formatPricePerMillionTokens,
getKnownModelsForProvider,
searchKnownModels,
} from "./index";
} from "./catalog";
const modelIds = (provider: string): readonly string[] =>
getKnownModelsForProvider(provider).map(
@@ -2,8 +2,6 @@ import { normalizeProvider } from "#/modules/aiModels/helpers";
import knownModelsGenerated from "./knownModelsGenerated.json";
import type { KnownModel } from "./types";
export type { KnownModel };
// knownModelsGenerated.json is produced by `make gen/aibridge-prices` from
// models.dev joined with the editorial curation in
// scripts/aibridgepricesgen/curation.json. Do not edit it manually. JSON
@@ -3,7 +3,7 @@ import type { FC, ReactNode } from "react";
import type * as TypesGen from "#/api/typesGenerated";
import { Button } from "#/components/Button/Button";
import { useTemporarySavedState } from "#/components/TemporarySavedState/TemporarySavedState";
import { ModelSelector } from "#/pages/AgentsPage/components/ChatElements/ModelSelector";
import { ModelSelector } from "#/modules/aiModels/ModelSelector";
import { ModelOverrideAlerts } from "#/pages/AgentsPage/components/ModelOverrideAlerts";
import type { ProviderInfo } from "#/pages/AgentsPage/utils/modelOptions";
import { pickReasoningEffort } from "#/pages/AgentsPage/utils/reasoningEffort";
@@ -38,11 +38,11 @@ import {
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import {
canManageProviderModels,
type ProviderState,
} from "#/modules/aiModels/providerStates";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { paginateItems } from "#/utils/paginateItems";
import { ModelRow } from "./components/ModelRow";
import {
@@ -6,17 +6,17 @@ import type * as TypesGen from "#/api/typesGenerated";
import { OrganizationField } from "#/components/OrganizationAutocomplete/OrganizationAutocomplete";
import { SettingsHeaderTitle } from "#/components/SettingsHeader/SettingsHeader";
import { useUnsavedChangesPrompt } from "#/hooks/useUnsavedChangesPrompt";
import {
canManageProviderModels,
type ProviderState,
} from "#/modules/aiModels/providerStates";
import {
buildInitialModelFormValues,
buildModelConfigFromForm,
type ModelFormValues,
parsePositiveInteger,
parseThresholdInteger,
} from "#/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic";
} from "#/modules/aiModels/modelConfigFormLogic";
import {
canManageProviderModels,
type ProviderState,
} from "#/modules/aiModels/providerStates";
import { getFormHelpers } from "#/utils/formUtils";
import {
creatableModelOrganizations,
@@ -11,7 +11,7 @@ import {
DialogHeader,
DialogTitle,
} from "#/components/Dialog/Dialog";
import type { ModelFormValues } from "#/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic";
import type { ModelFormValues } from "#/modules/aiModels/modelConfigFormLogic";
export const ModelFormDialogs: FC<{
editingModel?: TypesGen.ChatModel;
@@ -32,18 +32,18 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import type { ProviderState } from "#/modules/aiModels/providerStates";
import {
GeneralModelConfigFields,
ModelConfigFields,
PricingEstimateFields,
ReasoningEffortConfigFields,
} from "#/pages/AgentsPage/components/ChatModelAdminPanel/ModelConfigFields";
import { ModelIdentifierField } from "#/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField";
} from "#/modules/aiModels/ModelConfigFields";
import { ModelIdentifierField } from "#/modules/aiModels/ModelIdentifierField";
import type {
ModelConfigFormBuildResult,
ModelFormValues,
} from "#/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic";
} from "#/modules/aiModels/modelConfigFormLogic";
import type { ProviderState } from "#/modules/aiModels/providerStates";
import { docs } from "#/utils/docs";
import type { FormHelpers } from "#/utils/formUtils";
import {
@@ -26,8 +26,8 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { getProviderIcon } from "#/modules/aiModels/ProviderIcon";
import type { ProviderState } from "#/modules/aiModels/providerStates";
import { getProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { useOrganizationModelsPath } from "../organizationModels";
export const ModelFormBackLink: FC = () => {
@@ -7,8 +7,8 @@ import {
SelectTrigger,
SelectValue,
} from "#/components/Select/Select";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import type { ProviderState } from "#/modules/aiModels/providerStates";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
export const ModelFormProviderSelect: FC<{
providerStates: readonly ProviderState[];
@@ -11,7 +11,7 @@ import {
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { useClickableTableRow } from "#/hooks/useClickableTableRow";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
type ModelRowProps = {
model: ChatModel;
@@ -8,9 +8,9 @@ import { createAIProviderMutation } from "#/api/queries/aiProviders";
import { Avatar } from "#/components/Avatar/Avatar";
import { Button } from "#/components/Button/Button";
import { SettingsHeaderTitle } from "#/components/SettingsHeader/SettingsHeader";
import { getProviderIcon } from "#/modules/aiModels/ProviderIcon";
import type { AddableProvider } from "../components/addableProviderTypes";
import { ProviderForm } from "../components/ProviderForm";
import { getProviderIcon } from "../components/ProviderIcon";
import { providerFormValuesToCreate } from "../components/providerFormApiMap";
interface AddProviderPageViewProps {
@@ -24,8 +24,8 @@ import {
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { addableProviders } from "#/pages/AISettingsPage/ProvidersPage/components/addableProviderTypes";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { ProviderRow } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderRow";
import { docs } from "#/utils/docs";
@@ -18,9 +18,9 @@ import { DeleteDialog } from "#/components/Dialog/DeleteDialog/DeleteDialog";
import { Loader } from "#/components/Loader/Loader";
import { SettingsHeaderTitle } from "#/components/SettingsHeader/SettingsHeader";
import { Switch } from "#/components/Switch/Switch";
import { getProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { pageTitle } from "#/utils/page";
import { ProviderForm } from "../components/ProviderForm";
import { getProviderIcon } from "../components/ProviderIcon";
import {
aiProviderToFormValues,
bedrockExternalId,
@@ -14,7 +14,7 @@ import {
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { useClickableTableRow } from "#/hooks/useClickableTableRow";
import { ProviderIcon } from "./ProviderIcon";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { getProviderDisplayType } from "./providerFormApiMap";
type ProviderRowProps = {
@@ -8,12 +8,12 @@ import type {
CreateAIProviderRequest,
UpdateAIProviderRequest,
} from "#/api/typesGenerated";
import { getProviderIcon } from "#/modules/aiModels/ProviderIcon";
import {
type ProviderFormValues,
parseBedrockRegionFromBaseUrl,
SAVED_CREDENTIAL_MASK,
} from "./ProviderForm";
import { getProviderIcon } from "./ProviderIcon";
/** Drop placeholder masks so they don't round-trip back to the API. */
const sanitizeCredential = (
@@ -15,6 +15,7 @@ import { reactRouterParameters } from "storybook-addon-remix-react-router";
import { API } from "#/api/api";
import type * as TypesGen from "#/api/typesGenerated";
import type { ChatDiffStatus, ChatMessagePart } from "#/api/typesGenerated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import { AGENT_BROWSER_APP_SLUG } from "#/modules/apps/apps";
import { MockChat } from "#/testHelpers/chatEntities";
import {
@@ -43,7 +44,6 @@ import {
import type { ChatDetailError } from "./components/ChatConversation/chatError";
import { createChatStore } from "./components/ChatConversation/chatStore";
import { buildLongConversation } from "./components/ChatConversation/storyFixtures";
import type { ModelSelectorOption } from "./components/ChatElements/ModelSelector";
import { lastActiveSidebarTabStorageKeyPrefix } from "./utils/sidebarTabStorage";
// ---------------------------------------------------------------------------
@@ -18,6 +18,7 @@ import type {
import { ErrorAlert } from "#/components/Alert/ErrorAlert";
import { useProxy } from "#/contexts/ProxyContext";
import { useAuthenticated } from "#/hooks/useAuthenticated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import {
getAgentBrowserApp,
isWorkspaceAppEmbeddable,
@@ -39,7 +40,6 @@ import type { ChatDetailError } from "./components/ChatConversation/chatError";
import { getParentChatID } from "./components/ChatConversation/chatHelpers";
import type { useChatStore } from "./components/ChatConversation/chatStore";
import { QueuedForCapacityCallout } from "./components/ChatConversation/QueuedForCapacityCallout";
import type { ModelSelectorOption } from "./components/ChatElements/ModelSelector";
import { DesktopPanelContext } from "./components/ChatElements/tools/DesktopPanelContext";
import type { SkillMetadata } from "./components/ChatMessageInput/SkillsTriggerMenu";
import type { PendingAttachment } from "./components/ChatPageContent";
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { useState } from "react";
import { expect, fn, screen, userEvent, waitFor, within } from "storybook/test";
import type * as TypesGen from "#/api/typesGenerated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import { MockChatModel } from "#/testHelpers/chatModels";
import {
MockDefaultOrganization,
@@ -11,7 +12,6 @@ import {
AgentSettingsUserAgentsPageView,
type AgentSettingsUserAgentsPageViewProps,
} from "./AgentSettingsUserAgentsPageView";
import type { ModelSelectorOption } from "./components/ChatElements/ModelSelector";
const UNAVAILABLE_WARNING =
"The saved model is unavailable and will be ignored until you choose a valid model override.";
@@ -7,7 +7,7 @@ import {
getOrganizationLabel,
OrganizationAutocomplete,
} from "#/components/OrganizationAutocomplete/OrganizationAutocomplete";
import type { ModelSelectorOption } from "./components/ChatElements/ModelSelector";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import {
PersonalModelOverrideRow,
type SavePersonalOverride,
@@ -58,6 +58,10 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import {
ModelSelector,
type ModelSelectorOption,
} from "#/modules/aiModels/ModelSelector";
import { countInvisibleCharacters } from "#/utils/invisibleUnicode";
import { isBelowMdViewport, isMobileViewport } from "#/utils/mobile";
import { chatWidthClass, useChatFullWidth } from "../hooks/useChatFullWidth";
@@ -79,10 +83,6 @@ import {
isUploadInProgress,
type UploadState,
} from "./AttachmentPreview";
import {
ModelSelector,
type ModelSelectorOption,
} from "./ChatElements/ModelSelector";
import {
ChatMessageInput,
type ChatMessageInputRef,
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import type * as TypesGen from "#/api/typesGenerated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import { MockChatMessage } from "#/testHelpers/chatEntities";
import type { ModelSelectorOption } from "../ChatElements/ModelSelector";
import {
extractContextUsageFromMessage,
getLatestContextUsage,
@@ -1,7 +1,7 @@
import type * as TypesGen from "#/api/typesGenerated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import { findWorkspaceAgent } from "#/utils/workspace";
import type { AgentContextUsage } from "../AgentChatInput";
import type { ModelSelectorOption } from "../ChatElements/ModelSelector";
import { asString } from "../ChatElements/runtimeTypeUtils";
import { asNonEmptyString } from "./blockUtils";
@@ -1,8 +1,8 @@
import type React from "react";
import { ScrollArea } from "#/components/ScrollArea/ScrollArea";
import { ProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
import { formatContextLimit } from "#/modules/aiModels/ModelSelector";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { formatProviderLabel } from "#/utils/aiProviders";
import { formatContextLimit } from "../ModelSelector";
import { ToolCall } from "./ToolCall";
import { asNumber, asRecord, asString, type ToolStatus } from "./utils";
@@ -1,32 +0,0 @@
import { cn } from "cn";
import { Building2Icon } from "lucide-react";
import type { FC } from "react";
import { ExternalImage } from "#/components/ExternalImage/ExternalImage";
import { normalizeProvider } from "#/modules/aiModels/helpers";
import { getProviderIcon } from "#/pages/AISettingsPage/ProvidersPage/components/ProviderIcon";
interface ProviderIconProps {
provider: string;
className?: string;
}
export const ProviderIcon: FC<ProviderIconProps> = ({
provider,
className,
}) => {
const iconPath = getProviderIcon(normalizeProvider(provider));
return (
<div
className={cn(
"flex shrink-0 items-center justify-center rounded-full bg-surface-secondary",
className,
)}
>
{iconPath ? (
<ExternalImage src={iconPath} alt="" className="size-3/5" />
) : (
<Building2Icon className="size-3/5 text-content-secondary" />
)}
</div>
);
};
@@ -6,6 +6,7 @@ import type { UrlTransform } from "streamdown";
import { chatPromptsQuery, refreshChatContext } from "#/api/queries/chats";
import type * as TypesGen from "#/api/typesGenerated";
import type { AgentChatSendShortcut } from "#/api/typesGenerated";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import { useChatDraftAttachments } from "../hooks/useChatDraftAttachments";
import { chatWidthClass, useChatFullWidth } from "../hooks/useChatFullWidth";
import { useFileAttachments } from "../hooks/useFileAttachments";
@@ -49,7 +50,6 @@ import {
} from "./ChatConversation/messageParsing";
import { buildStreamTools } from "./ChatConversation/streamState";
import { useOnRenderProfiler } from "./ChatConversation/useOnRenderProfiler";
import type { ModelSelectorOption } from "./ChatElements/ModelSelector";
import type { SkillMetadata } from "./ChatMessageInput/SkillsTriggerMenu";
import { ChatMessageScroller } from "./ChatMessageScroller";
@@ -3,11 +3,11 @@ import type { FC } from "react";
import type * as TypesGen from "#/api/typesGenerated";
import { Alert, AlertDescription } from "#/components/Alert/Alert";
import { Button } from "#/components/Button/Button";
import { pickReasoningEffort } from "../utils/reasoningEffort";
import {
ModelSelector,
type ModelSelectorOption,
} from "./ChatElements/ModelSelector";
} from "#/modules/aiModels/ModelSelector";
import { pickReasoningEffort } from "../utils/reasoningEffort";
import { ModelOverrideAlerts } from "./ModelOverrideAlerts";
import { SectionHeader } from "./SectionHeader";
@@ -29,8 +29,8 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { ProviderIcon } from "#/modules/aiModels/ProviderIcon";
import { formatProviderLabel } from "#/utils/aiProviders";
import { ProviderIcon } from "./ChatModelAdminPanel/ProviderIcon";
interface UserCompactionThresholdSettingsProps {
models: readonly TypesGen.ChatModel[];
@@ -371,7 +371,12 @@ export const UserCompactionThresholdSettings: FC<
className="w-fit"
aria-label={`${providerLabel} ${modelName} in ${organizationName}`}
>
<ProviderIcon provider={provider} className="size-4" />
<span className="flex size-4 shrink-0 items-center justify-center rounded-full bg-surface-secondary">
<ProviderIcon
provider={provider}
className="size-3/5 text-content-secondary"
/>
</span>
{modelName}
</Badge>
{rowError && (
@@ -1,6 +1,6 @@
import type * as TypesGen from "#/api/typesGenerated";
import { normalizeProvider } from "#/modules/aiModels/helpers";
import type { ModelSelectorOption } from "../components/ChatElements/ModelSelector";
import type { ModelSelectorOption } from "#/modules/aiModels/ModelSelector";
import {
asNumber,
asString,
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { formatReasoningEffort } from "#/modules/aiModels/helpers";
import {
formatReasoningEffort,
getReasoningEffortForModel,
pickReasoningEffort,
saveReasoningEffortForModel,
@@ -28,10 +28,6 @@ export const saveReasoningEffortForModel = (
}
};
/** Display label for an effort value, e.g. "xhigh" renders as "Xhigh". */
export const formatReasoningEffort = (value: string): string =>
value.charAt(0).toUpperCase() + value.slice(1);
/** Chooses requested effort, then default effort, then the last selectable effort. */
export const pickReasoningEffort = (
value: string | undefined,
+1
View File
@@ -20,6 +20,7 @@ compilerPreset.rolldown.filter = {
id: {
// Keep in sync with targetDirs in scripts/check-compiler.mjs.
include: [
/src\/modules\/aiModels\//,
/src\/pages\/AgentsPage\//,
/src\/pages\/AIBridgePage\//,
/src\/pages\/TemplateBuilder\//,