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
17 changes: 7 additions & 10 deletions apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
type OrchestrationEvent,
type ProviderModelOptions,
type ProviderKind,
type ProviderServiceTier,
type ProviderStartOptions,
type OrchestrationSession,
ThreadId,
Expand Down Expand Up @@ -205,7 +204,6 @@ const make = Effect.gen(function* () {
readonly provider?: ProviderKind;
readonly model?: string;
readonly modelOptions?: ProviderModelOptions;
readonly serviceTier?: ProviderServiceTier | null;
readonly providerOptions?: ProviderStartOptions;
},
) {
Expand Down Expand Up @@ -241,7 +239,6 @@ const make = Effect.gen(function* () {
: {}),
...(effectiveCwd ? { cwd: effectiveCwd } : {}),
...(desiredModel ? { model: desiredModel } : {}),
...(options?.serviceTier !== undefined ? { serviceTier: options.serviceTier } : {}),
...(options?.modelOptions !== undefined ? { modelOptions: options.modelOptions } : {}),
...(options?.providerOptions !== undefined ? { providerOptions: options.providerOptions } : {}),
...(input?.resumeCursor !== undefined ? { resumeCursor: input.resumeCursor } : {}),
Expand Down Expand Up @@ -328,7 +325,6 @@ const make = Effect.gen(function* () {
readonly attachments?: ReadonlyArray<ChatAttachment>;
readonly provider?: ProviderKind;
readonly model?: string;
readonly serviceTier?: ProviderServiceTier | null;
readonly modelOptions?: ProviderModelOptions;
readonly providerOptions?: ProviderStartOptions;
readonly interactionMode?: "default" | "plan";
Expand All @@ -344,7 +340,6 @@ const make = Effect.gen(function* () {
yield* ensureSessionForThread(input.threadId, input.createdAt, {
...(input.provider !== undefined ? { provider: input.provider } : {}),
...(input.model !== undefined ? { model: input.model } : {}),
...(input.serviceTier !== undefined ? { serviceTier: input.serviceTier } : {}),
...(input.modelOptions !== undefined ? { modelOptions: input.modelOptions } : {}),
...(input.providerOptions !== undefined ? { providerOptions: input.providerOptions } : {}),
});
Expand All @@ -365,7 +360,6 @@ const make = Effect.gen(function* () {
...(normalizedInput ? { input: normalizedInput } : {}),
...(normalizedAttachments.length > 0 ? { attachments: normalizedAttachments } : {}),
...(modelForTurn !== undefined ? { model: modelForTurn } : {}),
...(input.serviceTier !== undefined ? { serviceTier: input.serviceTier } : {}),
...(input.modelOptions !== undefined ? { modelOptions: input.modelOptions } : {}),
...(input.interactionMode !== undefined ? { interactionMode: input.interactionMode } : {}),
});
Expand Down Expand Up @@ -480,7 +474,6 @@ const make = Effect.gen(function* () {
...(message.attachments !== undefined ? { attachments: message.attachments } : {}),
...(event.payload.provider !== undefined ? { provider: event.payload.provider } : {}),
...(event.payload.model !== undefined ? { model: event.payload.model } : {}),
...(event.payload.serviceTier !== undefined ? { serviceTier: event.payload.serviceTier } : {}),
...(event.payload.modelOptions !== undefined ? { modelOptions: event.payload.modelOptions } : {}),
...(event.payload.providerOptions !== undefined ? { providerOptions: event.payload.providerOptions } : {}),
interactionMode: event.payload.interactionMode,
Expand Down Expand Up @@ -639,9 +632,13 @@ const make = Effect.gen(function* () {
return;
}
const cachedProviderOptions = threadProviderOptions.get(event.payload.threadId);
yield* ensureSessionForThread(event.payload.threadId, event.occurredAt, {
...(cachedProviderOptions !== undefined ? { providerOptions: cachedProviderOptions } : {}),
});
yield* ensureSessionForThread(
event.payload.threadId,
event.occurredAt,
cachedProviderOptions !== undefined
? { providerOptions: cachedProviderOptions }
: undefined,
);
return;
}
case "thread.turn-start-requested":
Expand Down
1 change: 0 additions & 1 deletion apps/server/src/orchestration/decider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand"
messageId: command.message.messageId,
...(command.provider !== undefined ? { provider: command.provider } : {}),
...(command.model !== undefined ? { model: command.model } : {}),
...(command.serviceTier !== undefined ? { serviceTier: command.serviceTier } : {}),
...(command.modelOptions !== undefined ? { modelOptions: command.modelOptions } : {}),
...(command.providerOptions !== undefined ? { providerOptions: command.providerOptions } : {}),
assistantDeliveryMode: command.assistantDeliveryMode ?? DEFAULT_ASSISTANT_DELIVERY_MODE,
Expand Down
1 change: 0 additions & 1 deletion apps/server/src/provider/Layers/CodexAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1354,7 +1354,6 @@ const makeCodexAdapter = (options?: CodexAdapterLiveOptions) =>
threadId: input.threadId,
...(input.input !== undefined ? { input: input.input } : {}),
...(input.model !== undefined ? { model: input.model } : {}),
...(input.serviceTier !== undefined ? { serviceTier: input.serviceTier } : {}),
...(input.modelOptions?.codex?.reasoningEffort !== undefined
? { effort: input.modelOptions.codex.reasoningEffort }
: {}),
Expand Down
21 changes: 0 additions & 21 deletions apps/web/src/appSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
getAppModelOptions,
getSlashModelOptions,
normalizeCustomModelSlugs,
resolveAppServiceTier,
shouldShowFastTierIcon,
resolveAppModelSelection,
} from "./appSettings";

Expand Down Expand Up @@ -84,22 +82,3 @@ describe("getSlashModelOptions", () => {
expect(options.map((option) => option.slug)).toEqual(["openai/gpt-oss-120b"]);
});
});

describe("resolveAppServiceTier", () => {
it("maps automatic to no override", () => {
expect(resolveAppServiceTier("auto")).toBeNull();
});

it("preserves explicit service tier overrides", () => {
expect(resolveAppServiceTier("fast")).toBe("fast");
expect(resolveAppServiceTier("flex")).toBe("flex");
});
});

describe("shouldShowFastTierIcon", () => {
it("shows the fast-tier icon only for gpt-5.4 on fast tier", () => {
expect(shouldShowFastTierIcon("gpt-5.4", "fast")).toBe(true);
expect(shouldShowFastTierIcon("gpt-5.4", "auto")).toBe(false);
expect(shouldShowFastTierIcon("gpt-5.3-codex", "fast")).toBe(false);
});
});
39 changes: 1 addition & 38 deletions apps/web/src/appSettings.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
import { useCallback, useSyncExternalStore } from "react";
import { Option, Schema } from "effect";
import { type ProviderKind, type ProviderServiceTier } from "@t3tools/contracts";
import { type ProviderKind } from "@t3tools/contracts";
import { getDefaultModel, getModelOptions, normalizeModelSlug } from "@t3tools/shared/model";

const APP_SETTINGS_STORAGE_KEY = "t3code:app-settings:v1";
const MAX_CUSTOM_MODEL_COUNT = 32;
export const MAX_CUSTOM_MODEL_LENGTH = 256;
export const APP_SERVICE_TIER_OPTIONS = [
{
value: "auto",
label: "Automatic",
description: "Use Codex defaults without forcing a service tier.",
},
{
value: "fast",
label: "Fast",
description: "Request the fast service tier when the model supports it.",
},
{
value: "flex",
label: "Flex",
description: "Request the flex service tier when the model supports it.",
},
] as const;
export type AppServiceTier = (typeof APP_SERVICE_TIER_OPTIONS)[number]["value"];
const AppServiceTierSchema = Schema.Literals(["auto", "fast", "flex"]);
const MODELS_WITH_FAST_SUPPORT = new Set(["gpt-5.4"]);
const BUILT_IN_MODEL_SLUGS_BY_PROVIDER: Record<ProviderKind, ReadonlySet<string>> = {
codex: new Set(getModelOptions("codex").map((option) => option.slug)),
};
Expand All @@ -41,7 +21,6 @@ const AppSettingsSchema = Schema.Struct({
enableAssistantStreaming: Schema.Boolean.pipe(
Schema.withConstructorDefault(() => Option.some(false)),
),
codexServiceTier: AppServiceTierSchema.pipe(Schema.withConstructorDefault(() => Option.some("auto"))),
customCodexModels: Schema.Array(Schema.String).pipe(
Schema.withConstructorDefault(() => Option.some([])),
),
Expand All @@ -53,22 +32,6 @@ export interface AppModelOption {
isCustom: boolean;
}

export function resolveAppServiceTier(serviceTier: AppServiceTier): ProviderServiceTier | null {
return serviceTier === "auto" ? null : serviceTier;
}

export function shouldShowFastTierIcon(
model: string | null | undefined,
serviceTier: AppServiceTier,
): boolean {
const normalizedModel = normalizeModelSlug(model);
return (
resolveAppServiceTier(serviceTier) === "fast" &&
normalizedModel !== null &&
MODELS_WITH_FAST_SUPPORT.has(normalizedModel)
);
}

const DEFAULT_APP_SETTINGS = AppSettingsSchema.makeUnsafe({});

let listeners: Array<() => void> = [];
Expand Down
24 changes: 1 addition & 23 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ import {
XIcon,
CopyIcon,
CheckIcon,
ZapIcon,
} from "lucide-react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
Expand Down Expand Up @@ -205,9 +204,6 @@ import { readNativeApi } from "~/nativeApi";
import {
getAppModelOptions,
resolveAppModelSelection,
resolveAppServiceTier,
shouldShowFastTierIcon,
type AppServiceTier,
useAppSettings,
} from "../appSettings";
import {
Expand Down Expand Up @@ -410,7 +406,6 @@ type ComposerCommandItem =
model: ModelSlug;
label: string;
description: string;
showFastBadge: boolean;
};

type SendPhase = "idle" | "preparing-worktree" | "sending-turn";
Expand Down Expand Up @@ -521,9 +516,6 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: {
</Badge>
) : null}
<span className="flex min-w-0 items-center gap-1.5 truncate">
{props.item.type === "model" && props.item.showFastBadge ? (
<ZapIcon className="size-3.5 shrink-0 text-amber-500" />
) : null}
<span className="truncate">{props.item.label}</span>
</span>
<span className="truncate text-muted-foreground/70 text-xs">{props.item.description}</span>
Expand Down Expand Up @@ -785,8 +777,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
activeThread.messages.length > 0 ||
activeThread.session !== null),
);
const selectedServiceTierSetting = settings.codexServiceTier;
const selectedServiceTier = resolveAppServiceTier(selectedServiceTierSetting);
const lockedProvider: ProviderKind | null = hasThreadStarted
? (sessionProvider ?? selectedProviderByThreadId ?? null)
: null;
Expand Down Expand Up @@ -1254,10 +1244,8 @@ export default function ChatView({ threadId }: ChatViewProps) {
model: slug,
label: name,
description: `${providerLabel} · ${slug}`,
showFastBadge:
provider === "codex" && shouldShowFastTierIcon(slug, selectedServiceTierSetting),
}));
}, [composerTrigger, searchableModelOptions, selectedServiceTierSetting, workspaceEntries]);
}, [composerTrigger, searchableModelOptions, workspaceEntries]);
const composerMenuOpen = Boolean(composerTrigger);
const activeComposerMenuItem = useMemo(
() =>
Expand Down Expand Up @@ -2664,7 +2652,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
attachments: turnAttachments,
},
model: selectedModel || undefined,
serviceTier: selectedServiceTier,
...(selectedModelOptionsForDispatch
? { modelOptions: selectedModelOptionsForDispatch }
: {}),
Expand Down Expand Up @@ -3699,7 +3686,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
model={selectedModelForPickerWithCustomFallback}
lockedProvider={lockedProvider}
modelOptionsByProvider={modelOptionsByProvider}
serviceTierSetting={selectedServiceTierSetting}
onProviderModelChange={onProviderModelSelect}
/>

Expand Down Expand Up @@ -5542,7 +5528,6 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {
model: ModelSlug;
lockedProvider: ProviderKind | null;
modelOptionsByProvider: Record<ProviderKind, ReadonlyArray<{ slug: string; name: string }>>;
serviceTierSetting: AppServiceTier;
disabled?: boolean;
onProviderModelChange: (provider: ProviderKind, model: ModelSlug) => void;
}) {
Expand Down Expand Up @@ -5575,9 +5560,6 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {
>
<span className="flex min-w-0 items-center gap-2">
<ProviderIcon aria-hidden="true" className="size-4 shrink-0 text-muted-foreground/70" />
{props.provider === "codex" && shouldShowFastTierIcon(props.model, props.serviceTierSetting) ? (
<ZapIcon className="size-3.5 shrink-0 text-amber-500" />
) : null}
<span className="truncate">{selectedModelLabel}</span>
<ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />
</span>
Expand Down Expand Up @@ -5620,10 +5602,6 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {
value={modelOption.slug}
onClick={() => setIsMenuOpen(false)}
>
{option.value === "codex" &&
shouldShowFastTierIcon(modelOption.slug, props.serviceTierSetting) ? (
<ZapIcon className="size-3.5 shrink-0 text-amber-500" />
) : null}
{modelOption.name}
</MenuRadioItem>
))}
Expand Down
60 changes: 5 additions & 55 deletions apps/web/src/routes/_chat.settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,15 @@ import { useQuery } from "@tanstack/react-query";
import { useCallback, useState } from "react";
import { type ProviderKind } from "@t3tools/contracts";
import { getModelOptions, normalizeModelSlug } from "@t3tools/shared/model";
import { ZapIcon } from "lucide-react";

import {
APP_SERVICE_TIER_OPTIONS,
MAX_CUSTOM_MODEL_LENGTH,
shouldShowFastTierIcon,
useAppSettings,
} from "../appSettings";

import { MAX_CUSTOM_MODEL_LENGTH, useAppSettings } from "../appSettings";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
import { serverConfigQueryOptions } from "../lib/serverReactQuery";
import { ensureNativeApi } from "../nativeApi";
import { preferredTerminalEditor } from "../terminal-links";
import { Button } from "../components/ui/button";
import { Input } from "../components/ui/input";
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../components/ui/select";
import { Switch } from "../components/ui/switch";
import { SidebarInset } from "~/components/ui/sidebar";

Expand Down Expand Up @@ -103,7 +96,6 @@ function SettingsRouteView() {

const codexBinaryPath = settings.codexBinaryPath;
const codexHomePath = settings.codexHomePath;
const codexServiceTier = settings.codexServiceTier;
const keybindingsConfigPath = serverConfigQuery.data?.keybindingsConfigPath ?? null;

const openKeybindingsFile = useCallback(() => {
Expand Down Expand Up @@ -310,43 +302,6 @@ function SettingsRouteView() {
</div>

<div className="space-y-5">
<label className="block space-y-1">
<span className="text-xs font-medium text-foreground">Default service tier</span>
<Select
items={APP_SERVICE_TIER_OPTIONS.map((option) => ({
label: option.label,
value: option.value,
}))}
value={codexServiceTier}
onValueChange={(value) => {
if (!value) return;
updateSettings({ codexServiceTier: value });
}}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup alignItemWithTrigger={false}>
{APP_SERVICE_TIER_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className="flex min-w-0 items-center gap-2">
{option.value === "fast" ? (
<ZapIcon className="size-3.5 text-amber-500" />
) : (
<span className="size-3.5 shrink-0" aria-hidden="true" />
)}
<span className="truncate">{option.label}</span>
</div>
</SelectItem>
))}
</SelectPopup>
</Select>
<span className="text-xs text-muted-foreground">
{APP_SERVICE_TIER_OPTIONS.find((option) => option.value === codexServiceTier)
?.description ?? "Use Codex defaults without forcing a service tier."}
</span>
</label>

{MODEL_PROVIDER_SETTINGS.map((providerSettings) => {
const provider = providerSettings.provider;
const customModels = getCustomModelsForProvider(settings, provider);
Expand Down Expand Up @@ -445,14 +400,9 @@ function SettingsRouteView() {
key={`${provider}:${slug}`}
className="flex items-center justify-between gap-3 rounded-lg border border-border bg-background px-3 py-2"
>
<div className="flex min-w-0 flex-1 items-center gap-2">
{provider === "codex" && shouldShowFastTierIcon(slug, codexServiceTier) ? (
<ZapIcon className="size-3.5 shrink-0 text-amber-500" />
) : null}
<code className="min-w-0 flex-1 truncate text-xs text-foreground">
{slug}
</code>
</div>
<code className="min-w-0 flex-1 truncate text-xs text-foreground">
{slug}
</code>
<Button
size="xs"
variant="ghost"
Expand Down
Loading