Skip to content

Commit e105511

Browse files
authored
Merge branch 'main' into t3code/audit-token-performance
2 parents bffea9f + 4f0f24f commit e105511

5 files changed

Lines changed: 78 additions & 9 deletions

File tree

apps/web/src/components/chat/ChatComposer.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,9 +727,16 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
727727
model: selectedModel,
728728
models: selectedProviderModels,
729729
prompt,
730-
modelOptions: composerModelOptions?.[selectedProvider],
730+
modelOptions: composerModelOptions?.[selectedInstanceId],
731731
}),
732-
[composerModelOptions, prompt, selectedModel, selectedProvider, selectedProviderModels],
732+
[
733+
composerModelOptions,
734+
prompt,
735+
selectedInstanceId,
736+
selectedModel,
737+
selectedProvider,
738+
selectedProviderModels,
739+
],
733740
);
734741

735742
const selectedPromptEffort = composerProviderState.promptEffort;
@@ -1018,21 +1025,23 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
10181025

10191026
const providerTraitsMenuContent = renderProviderTraitsMenuContent({
10201027
provider: selectedProvider,
1028+
instanceId: selectedInstanceId,
10211029
...(routeKind === "server" ? { threadRef: routeThreadRef } : {}),
10221030
...(routeKind === "draft" && draftId ? { draftId } : {}),
10231031
model: selectedModel,
10241032
models: selectedProviderModels,
1025-
modelOptions: composerModelOptions?.[selectedProvider],
1033+
modelOptions: composerModelOptions?.[selectedInstanceId],
10261034
prompt,
10271035
onPromptChange: setPromptFromTraits,
10281036
});
10291037
const providerTraitsPicker = renderProviderTraitsPicker({
10301038
provider: selectedProvider,
1039+
instanceId: selectedInstanceId,
10311040
...(routeKind === "server" ? { threadRef: routeThreadRef } : {}),
10321041
...(routeKind === "draft" && draftId ? { draftId } : {}),
10331042
model: selectedModel,
10341043
models: selectedProviderModels,
1035-
modelOptions: composerModelOptions?.[selectedProvider],
1044+
modelOptions: composerModelOptions?.[selectedInstanceId],
10361045
prompt,
10371046
onPromptChange: setPromptFromTraits,
10381047
});

apps/web/src/components/chat/TraitsPicker.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
type ProviderDriverKind,
3+
type ProviderInstanceId,
34
type ProviderOptionDescriptor,
45
type ProviderOptionSelection,
56
type ScopedThreadRef,
@@ -196,6 +197,7 @@ export function shouldRenderTraitsControls(input: {
196197

197198
export interface TraitsMenuContentProps {
198199
provider: ProviderDriverKind;
200+
instanceId?: ProviderInstanceId;
199201
models: ReadonlyArray<ServerProviderModel>;
200202
model: string | null | undefined;
201203
prompt: string;
@@ -208,6 +210,7 @@ export interface TraitsMenuContentProps {
208210

209211
export const TraitsMenuContent = memo(function TraitsMenuContentImpl({
210212
provider,
213+
instanceId,
211214
models,
212215
model,
213216
prompt,
@@ -228,11 +231,12 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({
228231
return;
229232
}
230233
setProviderModelOptions(threadTarget, provider, nextOptions, {
234+
...(instanceId ? { instanceId } : {}),
231235
model,
232236
persistSticky: true,
233237
});
234238
},
235-
[model, persistence, provider, setProviderModelOptions],
239+
[instanceId, model, persistence, provider, setProviderModelOptions],
236240
);
237241
const {
238242
descriptors,
@@ -343,6 +347,7 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({
343347

344348
export const TraitsPicker = memo(function TraitsPicker({
345349
provider,
350+
instanceId,
346351
models,
347352
model,
348353
prompt,
@@ -431,6 +436,7 @@ export const TraitsPicker = memo(function TraitsPicker({
431436
<MenuPopup align="start">
432437
<TraitsMenuContent
433438
provider={provider}
439+
{...(instanceId ? { instanceId } : {})}
434440
models={models}
435441
model={model}
436442
prompt={prompt}

apps/web/src/components/chat/composerProviderState.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
type ProviderDriverKind,
3+
type ProviderInstanceId,
34
type ProviderOptionSelection,
45
type ScopedThreadRef,
56
type ServerProviderModel,
@@ -35,6 +36,7 @@ export type ComposerProviderState = {
3536

3637
type TraitsRenderInput = {
3738
provider: ProviderDriverKind;
39+
instanceId?: ProviderInstanceId;
3840
threadRef?: ScopedThreadRef;
3941
draftId?: DraftId;
4042
model: string;
@@ -76,8 +78,17 @@ function renderTraitsControl(
7678
Component: typeof TraitsMenuContent | typeof TraitsPicker,
7779
input: TraitsRenderInput,
7880
): ReactNode {
79-
const { provider, threadRef, draftId, model, models, modelOptions, prompt, onPromptChange } =
80-
input;
81+
const {
82+
provider,
83+
instanceId,
84+
threadRef,
85+
draftId,
86+
model,
87+
models,
88+
modelOptions,
89+
prompt,
90+
onPromptChange,
91+
} = input;
8192
const hasTarget = threadRef !== undefined || draftId !== undefined;
8293
if (
8394
!hasTarget ||
@@ -88,6 +99,7 @@ function renderTraitsControl(
8899
return (
89100
<Component
90101
provider={provider}
102+
{...(instanceId ? { instanceId } : {})}
91103
models={models}
92104
{...(threadRef ? { threadRef } : {})}
93105
{...(draftId ? { draftId } : {})}

apps/web/src/composerDraftStore.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { createModelSelection } from "@t3tools/shared/model";
2121
// `stickyModelSelectionByProvider` maps are keyed by `ProviderInstanceId`
2222
// in production; these aliases keep the legacy-key migration tests concise.
2323
const CODEX_INSTANCE = ProviderInstanceId.make("codex");
24+
const CODEX_SECONDARY_INSTANCE = ProviderInstanceId.make("codex_secondary");
2425
const CLAUDE_AGENT_INSTANCE = ProviderInstanceId.make("claudeAgent");
2526
const CURSOR_INSTANCE = ProviderInstanceId.make("cursor");
2627
const CODEX_DRIVER = ProviderDriverKind.make("codex");
@@ -1201,6 +1202,43 @@ describe("composerDraftStore modelSelection", () => {
12011202
);
12021203
});
12031204

1205+
it("stores provider option changes on a selected custom instance", () => {
1206+
const store = useComposerDraftStore.getState();
1207+
1208+
store.setProviderModelOptions(
1209+
threadRef,
1210+
CODEX_DRIVER,
1211+
toSelections({ reasoningEffort: "low" }),
1212+
{
1213+
instanceId: CODEX_SECONDARY_INSTANCE,
1214+
model: "gpt-5-codex",
1215+
persistSticky: true,
1216+
},
1217+
);
1218+
1219+
expect(
1220+
draftFor(threadId, TEST_ENVIRONMENT_ID)?.modelSelectionByProvider[CODEX_SECONDARY_INSTANCE],
1221+
).toEqual(
1222+
expect.objectContaining({
1223+
instanceId: CODEX_SECONDARY_INSTANCE,
1224+
options: [{ id: "reasoningEffort", value: "low" }],
1225+
}),
1226+
);
1227+
expect(draftFor(threadId, TEST_ENVIRONMENT_ID)?.activeProvider).toBe(CODEX_SECONDARY_INSTANCE);
1228+
expect(useComposerDraftStore.getState().stickyActiveProvider).toBe(CODEX_SECONDARY_INSTANCE);
1229+
expect(useComposerDraftStore.getState().stickyModelSelectionByProvider[CODEX_INSTANCE]).toBe(
1230+
undefined,
1231+
);
1232+
expect(
1233+
useComposerDraftStore.getState().stickyModelSelectionByProvider[CODEX_SECONDARY_INSTANCE],
1234+
).toEqual(
1235+
expect.objectContaining({
1236+
instanceId: CODEX_SECONDARY_INSTANCE,
1237+
options: [{ id: "reasoningEffort", value: "low" }],
1238+
}),
1239+
);
1240+
});
1241+
12041242
it("updates only the draft when sticky persistence is disabled", () => {
12051243
const store = useComposerDraftStore.getState();
12061244

apps/web/src/composerDraftStore.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ interface ComposerDraftStoreState {
371371
provider: ProviderDriverKind,
372372
nextProviderOptions: ReadonlyArray<ProviderOptionSelection> | null | undefined,
373373
options?: {
374+
instanceId?: ProviderInstanceId | null | undefined;
374375
model?: string | null | undefined;
375376
persistSticky?: boolean;
376377
},
@@ -2456,7 +2457,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
24562457
if (normalizedProvider === null) {
24572458
return;
24582459
}
2459-
const instanceKey = defaultInstanceIdForDriver(normalizedProvider);
2460+
const instanceKey = options?.instanceId ?? defaultInstanceIdForDriver(normalizedProvider);
24602461
const fallbackModel =
24612462
normalizeModelSlug(options?.model, normalizedProvider) ??
24622463
DEFAULT_MODEL_BY_PROVIDER[normalizedProvider] ??
@@ -2501,7 +2502,9 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
25012502
const { options: _, ...rest } = stickyBase;
25022503
nextStickyMap[instanceKey] = rest as ModelSelection;
25032504
}
2504-
nextStickyActiveProvider = base.activeProvider ?? instanceKey;
2505+
nextStickyActiveProvider = options.instanceId
2506+
? instanceKey
2507+
: (base.activeProvider ?? instanceKey);
25052508
}
25062509

25072510
if (
@@ -2514,6 +2517,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
25142517

25152518
const nextDraft: ComposerThreadDraftState = {
25162519
...base,
2520+
...(options?.instanceId ? { activeProvider: instanceKey } : {}),
25172521
modelSelectionByProvider: nextMap,
25182522
};
25192523
const nextDraftsByThreadKey = { ...state.draftsByThreadKey };

0 commit comments

Comments
 (0)