Studio: surface Codex parallel-calls in the connections dialog
The `codexParallelCalls` field on `ExternalProviderConfig` was wired through `chat-adapter.ts` (it is serialised over the wire as `parallel_calls`) but the connections dialog never set or restored it. With no UI input and no persistence path, the value was always left as `undefined` after a reload, the adapter fell back to `?? 1`, and the Codex fan-out path stayed permanently dormant from the UI even though the backend supported it. Three plumbing fixes: 1. Add a "Parallel calls" number input to the Codex form section, bounded to [1, CODEX_MAX_PARALLEL_CALLS]. Clamped on every key stroke so a hand-edited entry cannot exceed the backend cap. 2. Persist the value on `addProvider`, `saveProviderEdits`, and restore it on `editProvider` -- gated on `isCodexProviderType` so other providers cannot accidentally carry the field. 3. Preserve the value through `syncedProviders` rebuild on backend re-sync. The backend row does not store the fan-out width (it is local-only), so we copy it from the existing in-memory entry. Form reset clears the field back to the default so opening "Add connection" after editing a Codex provider does not pre-fill an unrelated value.
This commit is contained in:
parent
8c1c63a64d
commit
9b4bd11c9f
1 changed files with 79 additions and 0 deletions
|
|
@ -53,6 +53,9 @@ import {
|
|||
CUSTOM_BACKEND_PROVIDER_TYPE,
|
||||
CUSTOM_PROVIDER_PRESETS,
|
||||
allowsManualModelIdsWithCatalog,
|
||||
CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
CODEX_MAX_PARALLEL_CALLS,
|
||||
clampCodexParallelCalls,
|
||||
customProviderBaseUrlPlaceholder,
|
||||
customProviderDisplayName,
|
||||
customProviderModelIdsPlaceholder,
|
||||
|
|
@ -249,6 +252,14 @@ export function ChatProvidersSettings({
|
|||
const [modelSearchQuery, setModelSearchQuery] = useState("");
|
||||
const [customProviderName, setCustomProviderName] = useState("Custom");
|
||||
const [isReasoningModel, setIsReasoningModel] = useState(false);
|
||||
// Per-Codex-connection fan-out width. Stored on the provider so
|
||||
// restoring it after a refresh / page reload does not collapse back
|
||||
// to single-call. Clamped to [1, MAX] at every write because the
|
||||
// input is a plain `<input type="number">` and a hand-edited
|
||||
// localStorage entry could otherwise overflow.
|
||||
const [codexParallelCalls, setCodexParallelCalls] = useState<number>(
|
||||
CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
);
|
||||
const reduceMotion = useReducedMotion();
|
||||
const connectionsEnabled = useExternalProvidersStore(
|
||||
(s) => s.connectionsEnabled,
|
||||
|
|
@ -462,6 +473,15 @@ export function ChatProvidersSettings({
|
|||
isReasoningModel: supportsProviderReasoningToggle(uiProviderType)
|
||||
? existing?.isReasoningModel === true
|
||||
: undefined,
|
||||
// Preserve the per-Codex fan-out width on sync. The
|
||||
// backend provider row does not carry it (it lives in
|
||||
// localStorage only), so we read it from `existing` and
|
||||
// skip the field entirely for non-Codex providers.
|
||||
codexParallelCalls: isCodexProviderType(uiProviderType)
|
||||
? clampCodexParallelCalls(
|
||||
existing?.codexParallelCalls ?? CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
)
|
||||
: undefined,
|
||||
createdAt: existing?.createdAt ?? createdAt,
|
||||
updatedAt,
|
||||
};
|
||||
|
|
@ -519,6 +539,7 @@ export function ChatProvidersSettings({
|
|||
setModelSearchQuery("");
|
||||
setCustomProviderName(customProviderDisplayName(providerType));
|
||||
setIsReasoningModel(false);
|
||||
setCodexParallelCalls(CODEX_DEFAULT_PARALLEL_CALLS);
|
||||
}
|
||||
|
||||
function openAddProvider() {
|
||||
|
|
@ -756,6 +777,11 @@ export function ChatProvidersSettings({
|
|||
isReasoningModel: supportsProviderReasoningToggle(uiProviderType)
|
||||
? isReasoningModel
|
||||
: undefined,
|
||||
// Persist the fan-out width on the Codex provider only; other
|
||||
// providers must not carry the field through normalization.
|
||||
codexParallelCalls: isCodexProviderType(uiProviderType)
|
||||
? clampCodexParallelCalls(codexParallelCalls)
|
||||
: undefined,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
};
|
||||
|
|
@ -876,6 +902,12 @@ export function ChatProvidersSettings({
|
|||
)
|
||||
? isReasoningModel
|
||||
: undefined,
|
||||
// Carry through the fan-out width for Codex; clear it on
|
||||
// every other provider type so a left-over value cannot
|
||||
// hitchhike on the persisted record.
|
||||
codexParallelCalls: isCodexProviderType(existing.providerType)
|
||||
? clampCodexParallelCalls(codexParallelCalls)
|
||||
: undefined,
|
||||
updatedAt,
|
||||
}
|
||||
: provider,
|
||||
|
|
@ -908,6 +940,13 @@ export function ChatProvidersSettings({
|
|||
? provider.isReasoningModel === true
|
||||
: false,
|
||||
);
|
||||
setCodexParallelCalls(
|
||||
isCodexProviderType(provider.providerType)
|
||||
? clampCodexParallelCalls(
|
||||
provider.codexParallelCalls ?? CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
)
|
||||
: CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
);
|
||||
if (
|
||||
isCustomProviderType(provider.providerType) &&
|
||||
!supportsRemoteModelCatalog(provider.providerType)
|
||||
|
|
@ -1175,6 +1214,46 @@ export function ChatProvidersSettings({
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{isCodexProvider ? (
|
||||
<div className="grid grid-cols-[minmax(150px,0.8fr)_minmax(260px,1.2fr)] items-center gap-4 px-4 py-3 max-sm:grid-cols-1">
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<Label
|
||||
htmlFor="codex-parallel-calls"
|
||||
className="text-sm font-medium"
|
||||
>
|
||||
Parallel calls
|
||||
</Label>
|
||||
<p className="text-xs leading-snug text-muted-foreground">
|
||||
Fan-out width. Each call runs the same prompt against
|
||||
Codex and the results are unified in a final synthesis
|
||||
tab. 1-{CODEX_MAX_PARALLEL_CALLS}.
|
||||
</p>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<Input
|
||||
id="codex-parallel-calls"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
min={1}
|
||||
max={CODEX_MAX_PARALLEL_CALLS}
|
||||
step={1}
|
||||
value={codexParallelCalls}
|
||||
onChange={(event) => {
|
||||
const raw = Number(event.target.value);
|
||||
setCodexParallelCalls(
|
||||
clampCodexParallelCalls(
|
||||
Number.isFinite(raw)
|
||||
? raw
|
||||
: CODEX_DEFAULT_PARALLEL_CALLS,
|
||||
),
|
||||
);
|
||||
}}
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{showApiKeyField ? (
|
||||
<div className="grid grid-cols-[minmax(150px,0.8fr)_minmax(260px,1.2fr)] items-center gap-4 px-4 py-3 max-sm:grid-cols-1">
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue