Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
30f89ce
feat: shorten model names by dropping redundant family prefixes
flamboh Oct 23, 2026
cbc4d87
fix: keep derived model short names out of the provider cache
flamboh Oct 23, 2026
019ecb6
fix: space hyphenated short names and keep Grok Build whole
flamboh Oct 23, 2026
808d221
fix: strip vendor prefixes and native-only variant words from model s…
flamboh Oct 23, 2026
5284c7a
fix(web): keep the full model name as the picker's accessible label
flamboh Oct 23, 2026
9dca0cf
fix: treat Cursor as a native provider for Grok models
flamboh Oct 23, 2026
af9fe8d
fix: keep vendor prefixes when the rest names no model
flamboh Oct 23, 2026
ac834b1
fix: keep hyphens in spaced model names and treat Non/Transcribe as v…
flamboh Oct 23, 2026
f0c09d5
feat: show the spoken model name on the composer model button
flamboh Oct 23, 2026
17ece6a
fix(mobile): drop the composer trait suffix before truncating the mod…
flamboh Oct 23, 2026
21d46ae
fix(mobile): re-measure the composer model control when its text changes
flamboh Oct 24, 2026
461d437
fix(mobile): keep agent and session mode in the model control's acces…
flamboh Oct 24, 2026
a082340
chore: merge main into model short names
maria-rcks Oct 26, 2026
6eac980
fix: keep compact model labels unambiguous
maria-rcks Oct 26, 2026
a35c9dd
fix(web): retain full model names in the picker
maria-rcks Oct 26, 2026
89850f3
Merge commit 'dd5b5401e1f240f4314af29edf431369d182f386' into takeover…
maria-rcks Oct 26, 2026
58f5cca
Merge remote-tracking branch 'github/main' into takeover-24205
juliusmarminge Oct 27, 2026
fa80fac
refactor: drivers declare their model families; provider-core derives…
juliusmarminge Oct 27, 2026
e5da41b
refactor: derive model short names on the client from provider client…
juliusmarminge Oct 27, 2026
411947c
fix(web): custom models and the guest composer get derived short names
juliusmarminge Oct 27, 2026
8dd00d1
fix: keep the family on off-native Code models
juliusmarminge Oct 27, 2026
63c9020
refactor: web and mobile share one provider client registry in client…
juliusmarminge Oct 27, 2026
ad785cb
fix(web): guest model label keeps the full name for screen readers
juliusmarminge Oct 27, 2026
50a5eeb
fix: sidebar, handoff and subagent labels use the derived short name
juliusmarminge Oct 27, 2026
ea29efc
fix(web): every trigger label resolves its short name from the driver
juliusmarminge Oct 27, 2026
129245a
refactor(web): drop ModelListRow's unused shortName prop
juliusmarminge Oct 27, 2026
7c223ec
Merge remote-tracking branch 'github/main' into takeover-24205
juliusmarminge Oct 27, 2026
9cf74e0
fix: Codex's live model names ("GPT 6.0 Astra") shorten too
juliusmarminge Oct 27, 2026
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
8 changes: 0 additions & 8 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,6 @@
"@t3tools/mobile-review-diff-native": "file:./modules/t3-review-diff",
"@t3tools/mobile-source-text": "file:./modules/t3-source-text",
"@t3tools/mobile-terminal-native": "file:./modules/t3-terminal",
"@t3tools/provider-acp-registry": "workspace:*",
"@t3tools/provider-core": "workspace:*",
"@t3tools/provider-cursor": "workspace:*",
"@t3tools/provider-fx": "workspace:*",
"@t3tools/provider-grok": "workspace:*",
"@t3tools/provider-muse": "workspace:*",
"@t3tools/provider-opencode": "workspace:*",
"@t3tools/provider-pi": "workspace:*",
"@t3tools/shared": "workspace:*",
"@tabler/icons-react-native": "^3.44.0",
"clsx": "^2.1.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/ComposerModelControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function ComposerModelControl(props: {
/>
)
}
label={label}
label={props.modelOption?.shortName ?? label}
iconOnly={props.iconOnly}
suffix={
summary ? (
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/components/ProviderIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useId, useState } from "react";
import { resolveOfficialAcpRegistryIconUrl } from "@t3tools/contracts";
import { useAppearancePreferences } from "../features/settings/appearance/AppearancePreferencesProvider";
import { AppText as Text } from "./ui/AppText";
import { getProviderClient } from "../lib/providerClients";
import { getProviderClient } from "@t3tools/client-runtime/provider-clients";

type ProviderIconProps = {
readonly provider: string | null | undefined;
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/src/features/threads/thread-handoff-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Dialog from "../../components/ui/Dialog";
import { useAtomValue } from "@effect/atom-react";
import { scopeThreadRef } from "@t3tools/client-runtime/environment";
import { resolveHandoffEndpoints, type HandoffEndpoints } from "@t3tools/client-runtime/handoff";
import { resolveModelShortName } from "@t3tools/client-runtime/provider-clients";
import {
normalizeProviderAccentColor,
resolveProviderInstanceDisplayName,
Expand Down Expand Up @@ -136,7 +137,7 @@ function HandoffEndpoint(props: {
const provider = props.providers.find((candidate) => candidate.instanceId === props.instanceId);
const model = provider?.models.find((candidate) => candidate.slug === props.model);
const label =
model?.shortName ??
(model ? resolveModelShortName(model, provider?.driver) : undefined) ??
model?.name ??
props.model ??
(provider ? resolveProviderInstanceDisplayName(provider) : props.instanceId);
Expand Down
10 changes: 9 additions & 1 deletion apps/mobile/src/lib/modelOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import type {
RuntimeMode,
ServerConfig as T3ServerConfig,
} from "@t3tools/contracts";
import { resolveProjectProviderInstanceEnabled } from "@t3tools/contracts";
import { isProviderDriverKind, resolveProjectProviderInstanceEnabled } from "@t3tools/contracts";
import {
buildExplicitProviderOptionSelectionsFromDescriptors,
getProviderOptionDescriptors,
} from "@t3tools/shared/model";
import { resolveModelShortName } from "@t3tools/client-runtime/provider-clients";

export type ModelOption = {
readonly key: string;
readonly label: string;
readonly shortName?: string;
readonly subtitle: string;
readonly providerKey: string;
readonly providerLabel: string;
Expand Down Expand Up @@ -319,9 +321,11 @@ export function buildModelOptions(
models.sort((a, b) => (order.get(a.slug) ?? Infinity) - (order.get(b.slug) ?? Infinity));
for (const model of models) {
const key = `${provider.instanceId}:${model.slug}`;
const shortName = resolveModelShortName(model, provider.driver);
options.set(key, {
key,
label: model.name,
...(shortName ? { shortName } : {}),
subtitle: model.subProvider ?? "",
providerKey: provider.instanceId,
providerLabel,
Expand Down Expand Up @@ -384,9 +388,13 @@ export function buildModelOptions(
displayName: provider?.displayName ?? instanceConfig?.displayName,
instanceId: fallbackModelSelection.instanceId,
});
const shortName = model
? resolveModelShortName(model, isProviderDriverKind(providerDriver) ? providerDriver : null)
: undefined;
options.set(key, {
key,
label: model?.name ?? fallbackModelSelection.model,
...(shortName ? { shortName } : {}),
subtitle: model?.subProvider ?? "",
providerKey: fallbackModelSelection.instanceId,
providerLabel,
Expand Down
23 changes: 0 additions & 23 deletions apps/mobile/src/lib/providerClients.ts

This file was deleted.

7 changes: 0 additions & 7 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,7 @@
"@pierre/trees": "1.0.0-beta.6",
"@t3tools/client-runtime": "workspace:*",
"@t3tools/contracts": "workspace:*",
"@t3tools/provider-acp-registry": "workspace:*",
"@t3tools/provider-core": "workspace:*",
"@t3tools/provider-cursor": "workspace:*",
"@t3tools/provider-fx": "workspace:*",
"@t3tools/provider-grok": "workspace:*",
"@t3tools/provider-muse": "workspace:*",
"@t3tools/provider-opencode": "workspace:*",
"@t3tools/provider-pi": "workspace:*",
"@t3tools/shared": "workspace:*",
"@tanstack/react-pacer": "^0.19.4",
"@tanstack/react-router": "^1.160.2",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1628,7 +1628,7 @@ export default function ChatView(props: ChatViewProps) {
(model) => model.slug === providerSubagentModelSlug,
);
const providerSubagentModelLabel = providerSubagentCatalogModel
? getTriggerDisplayModelName(providerSubagentCatalogModel)
? getTriggerDisplayModelName(providerSubagentCatalogModel, selectedProviderEntry?.driverKind)
: formatModelSlugName(activeThread?.modelSelection.model ?? "");
const providerSubagentEffortLabel =
activeThread === undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("resolveContextWindowModelDisplayName", () => {
},
modelOptionsByInstance,
),
).toBe("5.6 Sol");
).toBe("GPT-5.6 Sol");
});
});

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/chat/ContextWindowMeter.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
resolveSelectableProviderInstanceEntry,
type ProviderInstanceEntry,
} from "../../providerInstances";
import { getTriggerDisplayModelName, type ModelEsque } from "./providerIconUtils";
import { getDisplayModelName, type ModelEsque } from "./providerIconUtils";

// Fallback TTL: Claude's 1h cache plus slack.
const CLAUDE_RESUME_COMPACTION_FALLBACK_MS = 70 * 60_000;
Expand Down Expand Up @@ -96,7 +96,7 @@ export function resolveContextWindowModelDisplayName(
.get(selection.instanceId)
?.find((model) => model.slug === selection.model);

return selectedModel ? getTriggerDisplayModelName(selectedModel) : selection.model;
return selectedModel ? getDisplayModelName(selectedModel) : selection.model;
}

export function formatContextWindowCompactionMessage(
Expand Down
17 changes: 2 additions & 15 deletions apps/web/src/components/chat/ModelListRow.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { type ProviderDriverKind, type ProviderInstanceId } from "@t3tools/contracts";
import { memo } from "react";
import { CheckIcon, StarIcon } from "lucide-react";
import {
getDisplayModelName,
getProviderRowLabel,
getTriggerDisplayModelLabel,
} from "./providerIconUtils";
import { getDisplayModelName, getProviderRowLabel } from "./providerIconUtils";
import { ComboboxItem } from "../ui/combobox";
import { Button } from "../ui/button";
import { Badge } from "../ui/badge";
Expand All @@ -19,7 +15,6 @@ export const ModelListRow = memo(function ModelListRow(props: {
index: number;
slug: string;
name: string;
shortName?: string | undefined;
subProvider?: string | undefined;
instanceId: ProviderInstanceId;
driverKind: ProviderDriverKind;
Expand All @@ -30,8 +25,6 @@ export const ModelListRow = memo(function ModelListRow(props: {
isFavorite: boolean;
isSelected: boolean;
showProvider: boolean;
preferShortName?: boolean;
useTriggerLabel?: boolean;
showNewBadge?: boolean;
unavailable?: boolean;
jumpLabel?: string | null;
Expand All @@ -41,7 +34,6 @@ export const ModelListRow = memo(function ModelListRow(props: {
const model = {
slug: props.slug,
name: props.name,
shortName: props.shortName,
subProvider: props.subProvider,
};
const providerLabel = getProviderRowLabel(props.providerDisplayName, props.subProvider);
Expand All @@ -61,12 +53,7 @@ export const ModelListRow = memo(function ModelListRow(props: {
<div className="min-w-0 flex-1 text-left">
<div className="flex min-w-0 items-center gap-2">
<div className="min-w-0 truncate text-sm font-normal leading-snug">
{props.useTriggerLabel
? getTriggerDisplayModelLabel(model)
: getDisplayModelName(
model,
props.preferShortName ? { preferShortName: true } : undefined,
)}
{getDisplayModelName(model)}
</div>
{props.showNewBadge ? (
<span
Expand Down
3 changes: 0 additions & 3 deletions apps/web/src/components/chat/ModelPickerContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,6 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
index={index}
slug={model.slug}
name={model.name}
shortName={model.shortName}
subProvider={model.subProvider}
instanceId={model.instanceId}
driverKind={model.driverKind}
Expand All @@ -1014,8 +1013,6 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
: modelKey === activeModelKey
}
showProvider={rowsMixProviders || Boolean(model.subProvider)}
preferShortName={!isLocked}
useTriggerLabel={false}
showNewBadge={model.badge === "new"}
unavailable={model.isUnavailable === true}
jumpLabel={modelJumpLabelByKey.get(modelKey) ?? null}
Expand Down
20 changes: 10 additions & 10 deletions apps/web/src/components/chat/ProviderModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import { cn } from "~/lib/utils";
import { ModelPickerContent, resolveModelPickerSelectedModel } from "./ModelPickerContent";
import { ChatGptSharingControl } from "./ChatGptSharingControl";
import { ProviderInstanceIcon } from "./ProviderInstanceIcon";
import {
ModelEsque,
getTriggerDisplayModelLabel,
getTriggerDisplayModelName,
} from "./providerIconUtils";
import { ModelEsque, getDisplayModelName, getTriggerDisplayModelName } from "./providerIconUtils";
import { shouldShowInstanceBadge, type ProviderInstanceEntry } from "../../providerInstances";
import {
ComposerControl,
Expand Down Expand Up @@ -121,12 +117,12 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
? undefined
: selectedInstanceOptions[0]);
const triggerTitle = selectedModel
? getTriggerDisplayModelName(selectedModel)
? getTriggerDisplayModelName(selectedModel, activeEntry?.driverKind)
: props.model === ANTIGRAVITY_DEFAULT_MODEL
? "Choose model"
: props.model || "Choose model";
const triggerLabel = selectedModel
? `${getTriggerDisplayModelLabel(selectedModel)}${selectedModel.isUnavailable ? " (Unavailable)" : ""}`
? `${getDisplayModelName(selectedModel)}${selectedModel.isUnavailable ? " (Unavailable)" : ""}`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
: triggerTitle;
const showInstanceBadge =
activeEntry !== null && shouldShowInstanceBadge(activeEntry, props.instanceEntries);
Expand Down Expand Up @@ -156,12 +152,14 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
model: selection.model,
options: props.modelOptionsByInstance.get(selection.instanceId) ?? [],
});
const unavailable = model?.isUnavailable ? " (Unavailable)" : "";
return {
...selection,
entry,
label: model
? `${getTriggerDisplayModelName(model)}${model.isUnavailable ? " (Unavailable)" : ""}`
? `${getTriggerDisplayModelName(model, entry?.driverKind)}${unavailable}`
: selection.model,
fullLabel: model ? `${getDisplayModelName(model)}${unavailable}` : selection.model,
};
});
const multipleLabel = selectedEntries
Expand All @@ -173,7 +171,7 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
.join(", ")}${selectedEntries.length > 2 ? `, ${selectedEntries.length - 2} more` : ""}`
: undefined;
const allModelNames = selectedEntries
? selectedEntries.map((selection) => selection.label).join(", ") || "Choose models"
? selectedEntries.map((selection) => selection.fullLabel).join(", ") || "Choose models"
: undefined;
const triggerTooltipContent = shortcutLabel
? `${props.triggerLabel ?? allModelNames ?? triggerLabel} · ${shortcutLabel}`
Expand All @@ -193,7 +191,9 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
<PopoverTrigger
render={
<ComposerControl
aria-label={props.triggerAriaLabel ?? allModelNames}
aria-label={
props.triggerAriaLabel ?? props.triggerLabel ?? allModelNames ?? triggerLabel
}
size={size}
data-chat-provider-model-picker="true"
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/chat/V2LifecycleRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ function HandoffEndpoint(props: {
: entry?.models.find((candidate) => candidate.slug === model);
const label =
providerModel !== undefined
? getTriggerDisplayModelName(providerModel)
? getTriggerDisplayModelName(providerModel, entry?.driverKind)
: model !== undefined && model.length > 0
? model
: (entry?.displayName ?? props.instanceId);
Expand Down
15 changes: 9 additions & 6 deletions apps/web/src/components/chat/providerIconUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { ProviderDriverKind } from "@t3tools/contracts";
import { resolveModelShortName } from "@t3tools/client-runtime/provider-clients";

export type ModelEsque = {
slug: string;
name: string;
Expand Down Expand Up @@ -47,10 +50,10 @@ export function getProviderRowLabel(
return `${providerDisplayName} · ${sub}`;
}

export function getTriggerDisplayModelName(model: ModelEsque): string {
return getDisplayModelName(model, { preferShortName: true });
}

export function getTriggerDisplayModelLabel(model: ModelEsque): string {
return getTriggerDisplayModelName(model);
export function getTriggerDisplayModelName(
model: ModelEsque,
driverKind: ProviderDriverKind | null | undefined,
): string {
const shortName = resolveModelShortName(model, driverKind);
return getDisplayModelName({ ...model, shortName }, { preferShortName: true });
}
2 changes: 1 addition & 1 deletion apps/web/src/components/guest/GuestPage.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ describe("GuestPage", () => {
expect(screen.container.querySelector('[aria-label="Fork from this response"]')).toBeNull();
expect(socket).not.toHaveBeenCalled();
// The composer names the thread's model, as the owner's does, with nothing to pick.
await expect.element(screen.getByLabelText("Model: 5.4")).toBeVisible();
await expect.element(screen.getByLabelText("Model: GPT-5.4")).toBeVisible();
});

it("sends attached files with the message, as form data", async () => {
Expand Down
7 changes: 4 additions & 3 deletions apps/web/src/components/guest/GuestPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { PierreEntryIcon } from "../chat/PierreEntryIcon";
import { GuestMessageName, guestBubbleClassName } from "../chat/guestMessageStyle";
import { MessagesTimeline } from "../chat/MessagesTimeline";
import { ProviderInstanceIcon } from "../chat/ProviderInstanceIcon";
import { getTriggerDisplayModelName } from "../chat/providerIconUtils";
import { getDisplayModelName, getTriggerDisplayModelName } from "../chat/providerIconUtils";
import { ScrollToEndButton } from "../chat/TimelineOverlays";
import type { TimelineRow } from "../chat/timelineRowContext";
import { useTimelineFollow } from "../chat/useTimelineFollow";
Expand Down Expand Up @@ -557,11 +557,12 @@ function UnplacedGuestMessage({

/** The thread's model, labeled as the owner's composer labels it, with no way to change it. */
function GuestModelLabel({ model }: { readonly model: GuestThreadModel }) {
const label = getTriggerDisplayModelName({ slug: model.name, ...model });
const label = getTriggerDisplayModelName({ slug: model.name, ...model }, model.driver);
const fullLabel = getDisplayModelName({ slug: model.name, ...model });
return (
<ComposerControl
render={<span />}
aria-label={`Model: ${label}`}
aria-label={`Model: ${fullLabel}`}
className="pointer-events-none min-w-0 shrink"
>
{model.driver === null ? null : (
Expand Down
Loading
Loading