refactor: move summarization model settings to notifications
- Remove Utility Model controls from Session Defaults and restore that section’s original scope. - Add Zen-based Summarization Model selector to Notifications under AI Summarization. - Keep config resolution aligned with zenModel-only utility behavior in useConfigStore and git API flow.
This commit is contained in:
@@ -12,9 +12,6 @@ import { useUIStore } from '@/stores/useUIStore';
|
||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
import { getModifierLabel, cn } from '@/lib/utils';
|
||||
|
||||
const UTILITY_PROVIDER_ID = 'zen';
|
||||
const UTILITY_PREFERRED_MODEL_ID = 'big-pickle';
|
||||
|
||||
const getDisplayModel = (
|
||||
storedModel: string | undefined
|
||||
): { providerId: string; modelId: string } => {
|
||||
@@ -28,45 +25,6 @@ const getDisplayModel = (
|
||||
return { providerId: '', modelId: '' };
|
||||
};
|
||||
|
||||
const getUtilityDisplayModel = (
|
||||
storedGitProviderId: string | undefined,
|
||||
storedGitModelId: string | undefined,
|
||||
zenModel: string | undefined,
|
||||
providers: Array<{ id: string; models: Array<{ id: string }> }>
|
||||
): { providerId: string; modelId: string } => {
|
||||
if (storedGitProviderId && storedGitModelId) {
|
||||
const provider = providers.find((p) => p.id === storedGitProviderId);
|
||||
if (provider?.models.some((m) => m.id === storedGitModelId)) {
|
||||
return { providerId: storedGitProviderId, modelId: storedGitModelId };
|
||||
}
|
||||
}
|
||||
|
||||
const utilityProvider = providers.find((p) => p.id === UTILITY_PROVIDER_ID);
|
||||
if (zenModel && utilityProvider?.models.some((m) => m.id === zenModel)) {
|
||||
return { providerId: UTILITY_PROVIDER_ID, modelId: zenModel };
|
||||
}
|
||||
|
||||
const preferredUtilityModel = utilityProvider?.models.find((m) => m.id === UTILITY_PREFERRED_MODEL_ID);
|
||||
if (preferredUtilityModel) {
|
||||
return { providerId: UTILITY_PROVIDER_ID, modelId: preferredUtilityModel.id };
|
||||
}
|
||||
|
||||
if (utilityProvider?.models.length) {
|
||||
const randomIndex = Math.floor(Math.random() * utilityProvider.models.length);
|
||||
const randomModel = utilityProvider.models[randomIndex];
|
||||
if (randomModel?.id) {
|
||||
return { providerId: UTILITY_PROVIDER_ID, modelId: randomModel.id };
|
||||
}
|
||||
}
|
||||
|
||||
const firstProvider = providers[0];
|
||||
if (firstProvider?.models[0]) {
|
||||
return { providerId: firstProvider.id, modelId: firstProvider.models[0].id };
|
||||
}
|
||||
|
||||
return { providerId: '', modelId: '' };
|
||||
};
|
||||
|
||||
export const DefaultsSettings: React.FC = () => {
|
||||
const setProvider = useConfigStore((state) => state.setProvider);
|
||||
const setModel = useConfigStore((state) => state.setModel);
|
||||
@@ -77,12 +35,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
const setSettingsDefaultAgent = useConfigStore((state) => state.setSettingsDefaultAgent);
|
||||
const settingsAutoCreateWorktree = useConfigStore((state) => state.settingsAutoCreateWorktree);
|
||||
const setSettingsAutoCreateWorktree = useConfigStore((state) => state.setSettingsAutoCreateWorktree);
|
||||
const settingsZenModel = useConfigStore((state) => state.settingsZenModel);
|
||||
const setSettingsZenModel = useConfigStore((state) => state.setSettingsZenModel);
|
||||
const settingsGitProviderId = useConfigStore((state) => state.settingsGitProviderId);
|
||||
const settingsGitModelId = useConfigStore((state) => state.settingsGitModelId);
|
||||
const setSettingsGitProviderId = useConfigStore((state) => state.setSettingsGitProviderId);
|
||||
const setSettingsGitModelId = useConfigStore((state) => state.setSettingsGitModelId);
|
||||
const showDeletionDialog = useUIStore((state) => state.showDeletionDialog);
|
||||
const setShowDeletionDialog = useUIStore((state) => state.setShowDeletionDialog);
|
||||
const providers = useConfigStore((state) => state.providers);
|
||||
@@ -93,9 +45,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
const parsedModel = React.useMemo(() => getDisplayModel(defaultModel), [defaultModel]);
|
||||
const utilityDisplayModel = React.useMemo(() => {
|
||||
return getUtilityDisplayModel(settingsGitProviderId, settingsGitModelId, settingsZenModel, providers);
|
||||
}, [settingsGitProviderId, settingsGitModelId, settingsZenModel, providers]);
|
||||
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -105,9 +54,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
defaultModel?: string;
|
||||
defaultVariant?: string;
|
||||
defaultAgent?: string;
|
||||
zenModel?: string;
|
||||
gitProviderId?: string;
|
||||
gitModelId?: string;
|
||||
} | null = null;
|
||||
|
||||
if (!data) {
|
||||
@@ -124,18 +70,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
? ((settings as Record<string, unknown>).defaultVariant as string)
|
||||
: undefined,
|
||||
defaultAgent: typeof settings.defaultAgent === 'string' ? settings.defaultAgent : undefined,
|
||||
zenModel:
|
||||
typeof (settings as Record<string, unknown>).zenModel === 'string'
|
||||
? ((settings as Record<string, unknown>).zenModel as string)
|
||||
: undefined,
|
||||
gitProviderId:
|
||||
typeof (settings as Record<string, unknown>).gitProviderId === 'string'
|
||||
? ((settings as Record<string, unknown>).gitProviderId as string)
|
||||
: undefined,
|
||||
gitModelId:
|
||||
typeof (settings as Record<string, unknown>).gitModelId === 'string'
|
||||
? ((settings as Record<string, unknown>).gitModelId as string)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
@@ -167,25 +101,10 @@ export const DefaultsSettings: React.FC = () => {
|
||||
typeof data.defaultAgent === 'string' && data.defaultAgent.trim().length > 0
|
||||
? data.defaultAgent.trim()
|
||||
: undefined;
|
||||
const zen =
|
||||
typeof data.zenModel === 'string' && data.zenModel.trim().length > 0
|
||||
? data.zenModel.trim()
|
||||
: undefined;
|
||||
const gitProviderId =
|
||||
typeof data.gitProviderId === 'string' && data.gitProviderId.trim().length > 0
|
||||
? data.gitProviderId.trim()
|
||||
: undefined;
|
||||
const gitModelId =
|
||||
typeof data.gitModelId === 'string' && data.gitModelId.trim().length > 0
|
||||
? data.gitModelId.trim()
|
||||
: undefined;
|
||||
|
||||
if (model !== undefined) setDefaultModel(model);
|
||||
if (variant !== undefined) setDefaultVariant(variant);
|
||||
if (agent !== undefined) setDefaultAgent(agent);
|
||||
if (zen !== undefined) setSettingsZenModel(zen);
|
||||
if (gitProviderId !== undefined) setSettingsGitProviderId(gitProviderId);
|
||||
if (gitModelId !== undefined) setSettingsGitModelId(gitModelId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load defaults settings:', error);
|
||||
@@ -194,7 +113,7 @@ export const DefaultsSettings: React.FC = () => {
|
||||
}
|
||||
};
|
||||
loadSettings();
|
||||
}, [setSettingsGitModelId, setSettingsGitProviderId, setSettingsZenModel]);
|
||||
}, []);
|
||||
|
||||
const handleModelChange = React.useCallback(
|
||||
async (providerId: string, modelId: string) => {
|
||||
@@ -303,26 +222,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
[setSettingsAutoCreateWorktree]
|
||||
);
|
||||
|
||||
const handleUtilityModelChange = React.useCallback(
|
||||
async (providerId: string, modelId: string) => {
|
||||
setSettingsGitProviderId(providerId);
|
||||
setSettingsGitModelId(modelId);
|
||||
if (providerId === UTILITY_PROVIDER_ID) {
|
||||
setSettingsZenModel(modelId);
|
||||
}
|
||||
try {
|
||||
await updateDesktopSettings({
|
||||
gitProviderId: providerId,
|
||||
gitModelId: modelId,
|
||||
...(providerId === UTILITY_PROVIDER_ID ? { zenModel: modelId } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Failed to save utility model setting:', error);
|
||||
}
|
||||
},
|
||||
[setSettingsGitModelId, setSettingsGitProviderId, setSettingsZenModel]
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
@@ -393,29 +292,6 @@ export const DefaultsSettings: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 py-1 sm:flex-row sm:items-center sm:gap-8">
|
||||
<div className="flex min-w-0 flex-col sm:w-56 shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="typography-ui-label text-foreground">Utility Model</span>
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
The model used for lightweight background tasks like commit messages, PR descriptions, and summarization.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
||||
<ModelSelector
|
||||
providerId={utilityDisplayModel.providerId}
|
||||
modelId={utilityDisplayModel.modelId}
|
||||
onChange={handleUtilityModelChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1"
|
||||
role="button"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { RiRestartLine } from '@remixicon/react';
|
||||
import { RiInformationLine, RiRestartLine } from '@remixicon/react';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { isDesktopShell, isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from '@/components/ui';
|
||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
@@ -10,6 +12,8 @@ import { GridLoader } from '@/components/ui/grid-loader';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { NumberInput } from '@/components/ui/number-input';
|
||||
import { ButtonSmall } from '@/components/ui/button-small';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const DEFAULT_NOTIFICATION_TEMPLATES = {
|
||||
@@ -19,6 +23,10 @@ const DEFAULT_NOTIFICATION_TEMPLATES = {
|
||||
subtask: { title: '{agent_name} is ready', message: '{model_name} completed the task' },
|
||||
} as const;
|
||||
|
||||
const UTILITY_PROVIDER_ID = 'zen';
|
||||
const UTILITY_PREFERRED_MODEL_ID = 'big-pickle';
|
||||
const UTILITY_NOT_SELECTED_VALUE = '__not_selected__';
|
||||
|
||||
const DEFAULT_SUMMARY_THRESHOLD = 200;
|
||||
const DEFAULT_SUMMARY_LENGTH = 100;
|
||||
const DEFAULT_MAX_LAST_MESSAGE_LENGTH = 250;
|
||||
@@ -50,11 +58,105 @@ export const NotificationSettings: React.FC = () => {
|
||||
const setSummaryLength = useUIStore(state => state.setSummaryLength);
|
||||
const maxLastMessageLength = useUIStore(state => state.maxLastMessageLength);
|
||||
const setMaxLastMessageLength = useUIStore(state => state.setMaxLastMessageLength);
|
||||
const providers = useConfigStore((state) => state.providers);
|
||||
const settingsZenModel = useConfigStore((state) => state.settingsZenModel);
|
||||
const setSettingsZenModel = useConfigStore((state) => state.setSettingsZenModel);
|
||||
|
||||
const [notificationPermission, setNotificationPermission] = React.useState<NotificationPermission>('default');
|
||||
const [pushSupported, setPushSupported] = React.useState(false);
|
||||
const [pushSubscribed, setPushSubscribed] = React.useState(false);
|
||||
const [pushBusy, setPushBusy] = React.useState(false);
|
||||
const [fetchedZenModels, setFetchedZenModels] = React.useState<Array<{ id: string; name: string }>>([]);
|
||||
|
||||
const providerZenModels = React.useMemo(() => {
|
||||
const zenProvider = providers.find((provider) => provider.id === UTILITY_PROVIDER_ID);
|
||||
const models = Array.isArray(zenProvider?.models) ? zenProvider.models : [];
|
||||
return models
|
||||
.map((model: Record<string, unknown>) => {
|
||||
const id = typeof model.id === 'string' ? model.id.trim() : '';
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
const name = typeof model.name === 'string' && model.name.trim().length > 0 ? model.name.trim() : id;
|
||||
return { id, name };
|
||||
})
|
||||
.filter((model): model is { id: string; name: string } => model !== null);
|
||||
}, [providers]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (providerZenModels.length > 0) {
|
||||
setFetchedZenModels([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
void fetch('/api/zen/models', {
|
||||
method: 'GET',
|
||||
headers: { Accept: 'application/json' },
|
||||
signal: controller.signal,
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
return [] as Array<{ id: string; name: string }>;
|
||||
}
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
const models = Array.isArray(payload?.models) ? payload.models : [];
|
||||
return models
|
||||
.map((entry: unknown) => {
|
||||
const id = typeof (entry as { id?: unknown })?.id === 'string'
|
||||
? (entry as { id: string }).id.trim()
|
||||
: '';
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
return { id, name: id };
|
||||
})
|
||||
.filter((entry: { id: string; name: string } | null): entry is { id: string; name: string } => entry !== null);
|
||||
})
|
||||
.then((models) => {
|
||||
setFetchedZenModels(models);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error?.name !== 'AbortError') {
|
||||
console.warn('Failed to load zen utility models:', error);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [providerZenModels]);
|
||||
|
||||
const utilityModelOptions = React.useMemo(() => {
|
||||
return providerZenModels.length > 0 ? providerZenModels : fetchedZenModels;
|
||||
}, [fetchedZenModels, providerZenModels]);
|
||||
|
||||
const utilitySelectedModelId = React.useMemo(() => {
|
||||
if (settingsZenModel && utilityModelOptions.some((model) => model.id === settingsZenModel)) {
|
||||
return settingsZenModel;
|
||||
}
|
||||
if (utilityModelOptions.some((model) => model.id === UTILITY_PREFERRED_MODEL_ID)) {
|
||||
return UTILITY_PREFERRED_MODEL_ID;
|
||||
}
|
||||
return utilityModelOptions[0]?.id ?? '';
|
||||
}, [settingsZenModel, utilityModelOptions]);
|
||||
|
||||
const handleUtilityModelChange = React.useCallback(
|
||||
async (value: string) => {
|
||||
const modelId = value === UTILITY_NOT_SELECTED_VALUE ? undefined : value;
|
||||
setSettingsZenModel(modelId);
|
||||
try {
|
||||
await updateDesktopSettings({
|
||||
zenModel: modelId ?? '',
|
||||
gitProviderId: '',
|
||||
gitModelId: '',
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Failed to save utility model setting:', error);
|
||||
}
|
||||
},
|
||||
[setSettingsZenModel]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isBrowser) {
|
||||
@@ -648,6 +750,40 @@ export const NotificationSettings: React.FC = () => {
|
||||
<span className="typography-ui-label text-foreground">Summarize Last Message</span>
|
||||
</div>
|
||||
|
||||
<div className={cn("flex flex-col gap-2 py-1 sm:flex-row sm:items-center sm:gap-8")}>
|
||||
<div className="flex min-w-0 flex-col sm:w-56 shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="typography-ui-label text-foreground">Summarization Model</span>
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
Used for notification and voice summaries.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
||||
<Select
|
||||
value={utilitySelectedModelId || UTILITY_NOT_SELECTED_VALUE}
|
||||
onValueChange={handleUtilityModelChange}
|
||||
>
|
||||
<SelectTrigger className="w-fit min-w-[220px]">
|
||||
<SelectValue placeholder="Not selected" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={UTILITY_NOT_SELECTED_VALUE}>Not selected</SelectItem>
|
||||
{utilityModelOptions.map((model) => (
|
||||
<SelectItem key={model.id} value={model.id}>
|
||||
{model.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{summarizeLastMessage ? (
|
||||
<>
|
||||
<div className="flex items-center gap-8 py-1.5 mt-1 border-t border-[var(--surface-subtle)]">
|
||||
|
||||
@@ -116,7 +116,7 @@ export async function generateCommitMessage(
|
||||
const generationSession = resolveSessionGenerationContext();
|
||||
|
||||
if (!generationSession) {
|
||||
throw new Error('Select an active session for generation');
|
||||
throw new Error('Select existing session for generation');
|
||||
}
|
||||
|
||||
console.info('[git-generation][browser] request', {
|
||||
@@ -205,7 +205,7 @@ export async function generatePullRequestDescription(
|
||||
const startedAt = Date.now();
|
||||
const generationSession = resolveSessionGenerationContext();
|
||||
if (!generationSession) {
|
||||
throw new Error('Select an active session for generation');
|
||||
throw new Error('Select existing session for generation');
|
||||
}
|
||||
|
||||
const commitLog = await getGitLog(directory, {
|
||||
|
||||
@@ -28,8 +28,6 @@ interface OpenChamberDefaults {
|
||||
autoCreateWorktree?: boolean;
|
||||
gitmojiEnabled?: boolean;
|
||||
zenModel?: string;
|
||||
gitProviderId?: string;
|
||||
gitModelId?: string;
|
||||
}
|
||||
|
||||
const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||
@@ -46,8 +44,6 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||
const defaultAgent = typeof data?.defaultAgent === 'string' ? data.defaultAgent.trim() : '';
|
||||
const gitmojiEnabled = typeof data?.gitmojiEnabled === 'boolean' ? data.gitmojiEnabled : undefined;
|
||||
const zenModel = typeof data?.zenModel === 'string' ? data.zenModel.trim() : '';
|
||||
const gitProviderId = typeof data?.gitProviderId === 'string' ? data.gitProviderId.trim() : '';
|
||||
const gitModelId = typeof data?.gitModelId === 'string' ? data.gitModelId.trim() : '';
|
||||
|
||||
return {
|
||||
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||
@@ -56,8 +52,6 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
||||
gitmojiEnabled,
|
||||
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
||||
gitProviderId: gitProviderId.length > 0 ? gitProviderId : undefined,
|
||||
gitModelId: gitModelId.length > 0 ? gitModelId : undefined,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
@@ -79,8 +73,6 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||
const defaultAgent = typeof data?.defaultAgent === 'string' ? data.defaultAgent.trim() : '';
|
||||
const gitmojiEnabled = typeof data?.gitmojiEnabled === 'boolean' ? data.gitmojiEnabled : undefined;
|
||||
const zenModel = typeof data?.zenModel === 'string' ? data.zenModel.trim() : '';
|
||||
const gitProviderId = typeof data?.gitProviderId === 'string' ? data.gitProviderId.trim() : '';
|
||||
const gitModelId = typeof data?.gitModelId === 'string' ? data.gitModelId.trim() : '';
|
||||
|
||||
return {
|
||||
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||
@@ -89,8 +81,6 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
||||
gitmojiEnabled,
|
||||
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
||||
gitProviderId: gitProviderId.length > 0 ? gitProviderId : undefined,
|
||||
gitModelId: gitModelId.length > 0 ? gitModelId : undefined,
|
||||
};
|
||||
} catch {
|
||||
return {};
|
||||
@@ -139,17 +129,11 @@ const hasProviderModel = (
|
||||
|
||||
const resolveGitGenerationModelSelection = ({
|
||||
providers,
|
||||
settingsGitProviderId,
|
||||
settingsGitModelId,
|
||||
settingsZenModel,
|
||||
}: {
|
||||
providers: ProviderWithModelList[];
|
||||
settingsGitProviderId?: string;
|
||||
settingsGitModelId?: string;
|
||||
settingsZenModel?: string;
|
||||
}): GitModelSelection | null => {
|
||||
const gitProviderId = normalizeOptionalString(settingsGitProviderId);
|
||||
const gitModelId = normalizeOptionalString(settingsGitModelId);
|
||||
const zenModel = normalizeOptionalString(settingsZenModel);
|
||||
|
||||
if (!Array.isArray(providers) || providers.length === 0) {
|
||||
@@ -159,10 +143,6 @@ const resolveGitGenerationModelSelection = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (gitProviderId && gitModelId && hasProviderModel(providers, gitProviderId, gitModelId)) {
|
||||
return { providerId: gitProviderId, modelId: gitModelId };
|
||||
}
|
||||
|
||||
if (zenModel && hasProviderModel(providers, GIT_UTILITY_PROVIDER_ID, zenModel)) {
|
||||
return { providerId: GIT_UTILITY_PROVIDER_ID, modelId: zenModel };
|
||||
}
|
||||
@@ -180,12 +160,6 @@ const resolveGitGenerationModelSelection = ({
|
||||
}
|
||||
}
|
||||
|
||||
const firstProvider = providers.find((provider) => provider.models.length > 0);
|
||||
const firstModelId = normalizeOptionalString(firstProvider?.models[0]?.id);
|
||||
if (firstProvider?.id && firstModelId) {
|
||||
return { providerId: firstProvider.id, modelId: firstModelId };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -458,8 +432,6 @@ interface ConfigStore {
|
||||
settingsAutoCreateWorktree: boolean;
|
||||
settingsGitmojiEnabled: boolean;
|
||||
settingsZenModel: string | undefined;
|
||||
settingsGitProviderId: string | undefined;
|
||||
settingsGitModelId: string | undefined;
|
||||
// Voice provider preference ('browser', 'openai', or 'say' for macOS)
|
||||
voiceProvider: 'browser' | 'openai' | 'say';
|
||||
setVoiceProvider: (provider: 'browser' | 'openai' | 'say') => void;
|
||||
@@ -509,8 +481,6 @@ interface ConfigStore {
|
||||
setSettingsAutoCreateWorktree: (enabled: boolean) => void;
|
||||
setSettingsGitmojiEnabled: (enabled: boolean) => void;
|
||||
setSettingsZenModel: (model: string | undefined) => void;
|
||||
setSettingsGitProviderId: (providerId: string | undefined) => void;
|
||||
setSettingsGitModelId: (modelId: string | undefined) => void;
|
||||
getResolvedGitGenerationModel: () => { providerId: string; modelId: string } | null;
|
||||
saveAgentModelSelection: (agentName: string, providerId: string, modelId: string) => void;
|
||||
getAgentModelSelection: (agentName: string) => { providerId: string; modelId: string } | null;
|
||||
@@ -557,8 +527,6 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
settingsAutoCreateWorktree: false,
|
||||
settingsGitmojiEnabled: false,
|
||||
settingsZenModel: undefined,
|
||||
settingsGitProviderId: undefined,
|
||||
settingsGitModelId: undefined,
|
||||
// Voice provider preference - load from localStorage or default to 'browser'
|
||||
voiceProvider: (() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -1100,35 +1068,23 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
? get().providers
|
||||
: (get().directoryScoped[directoryKey]?.providers ?? []);
|
||||
|
||||
const existingGitProviderId = normalizeOptionalString(get().settingsGitProviderId);
|
||||
const existingGitModelId = normalizeOptionalString(get().settingsGitModelId);
|
||||
const existingZenModel = normalizeOptionalString(get().settingsZenModel);
|
||||
|
||||
const defaultGitProviderId = normalizeOptionalString(openChamberDefaults.gitProviderId);
|
||||
const defaultGitModelId = normalizeOptionalString(openChamberDefaults.gitModelId);
|
||||
const defaultZenModel = normalizeOptionalString(openChamberDefaults.zenModel);
|
||||
|
||||
const resolvedExistingGitSelection = resolveGitGenerationModelSelection({
|
||||
providers,
|
||||
settingsGitProviderId: existingGitProviderId,
|
||||
settingsGitModelId: existingGitModelId,
|
||||
settingsZenModel: existingZenModel,
|
||||
});
|
||||
|
||||
const resolvedDefaultGitSelection = resolveGitGenerationModelSelection({
|
||||
providers,
|
||||
settingsGitProviderId: defaultGitProviderId,
|
||||
settingsGitModelId: defaultGitModelId,
|
||||
settingsZenModel: defaultZenModel,
|
||||
});
|
||||
|
||||
const resolvedGitSelection = resolvedExistingGitSelection || resolvedDefaultGitSelection;
|
||||
const resolvedGitProviderId = resolvedGitSelection?.providerId;
|
||||
const resolvedGitModelId = resolvedGitSelection?.modelId;
|
||||
const resolvedZenModel =
|
||||
resolvedGitProviderId === GIT_UTILITY_PROVIDER_ID && resolvedGitModelId
|
||||
? resolvedGitModelId
|
||||
: (defaultZenModel || existingZenModel);
|
||||
const resolvedZenModel = resolvedGitModelId || defaultZenModel || existingZenModel;
|
||||
|
||||
set((state) => {
|
||||
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||
@@ -1155,8 +1111,6 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
settingsAutoCreateWorktree: openChamberDefaults.autoCreateWorktree ?? false,
|
||||
settingsGitmojiEnabled: openChamberDefaults.gitmojiEnabled ?? false,
|
||||
settingsZenModel: resolvedZenModel,
|
||||
settingsGitProviderId: resolvedGitProviderId,
|
||||
settingsGitModelId: resolvedGitModelId,
|
||||
directoryScoped: {
|
||||
...state.directoryScoped,
|
||||
[directoryKey]: nextSnapshot,
|
||||
@@ -1170,33 +1124,16 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
return nextState;
|
||||
});
|
||||
|
||||
const shouldPersistResolvedGitSelection =
|
||||
!!resolvedGitProviderId &&
|
||||
!!resolvedGitModelId &&
|
||||
(
|
||||
defaultGitProviderId !== resolvedGitProviderId ||
|
||||
defaultGitModelId !== resolvedGitModelId ||
|
||||
(
|
||||
resolvedGitProviderId === GIT_UTILITY_PROVIDER_ID &&
|
||||
resolvedZenModel !== defaultZenModel
|
||||
)
|
||||
);
|
||||
const shouldPersistResolvedZenModel =
|
||||
!!resolvedZenModel &&
|
||||
resolvedZenModel !== defaultZenModel;
|
||||
|
||||
if (shouldPersistResolvedGitSelection && resolvedGitProviderId && resolvedGitModelId) {
|
||||
const gitSettingsUpdate: {
|
||||
gitProviderId: string;
|
||||
gitModelId: string;
|
||||
zenModel?: string;
|
||||
} = {
|
||||
gitProviderId: resolvedGitProviderId,
|
||||
gitModelId: resolvedGitModelId,
|
||||
};
|
||||
|
||||
if (resolvedGitProviderId === GIT_UTILITY_PROVIDER_ID && resolvedZenModel) {
|
||||
gitSettingsUpdate.zenModel = resolvedZenModel;
|
||||
}
|
||||
|
||||
updateDesktopSettings(gitSettingsUpdate).catch(() => {
|
||||
if (shouldPersistResolvedZenModel && resolvedZenModel) {
|
||||
updateDesktopSettings({
|
||||
zenModel: resolvedZenModel,
|
||||
gitProviderId: '',
|
||||
gitModelId: '',
|
||||
}).catch(() => {
|
||||
// Ignore errors - best effort cleanup
|
||||
});
|
||||
}
|
||||
@@ -1616,20 +1553,10 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
set({ settingsZenModel: model });
|
||||
},
|
||||
|
||||
setSettingsGitProviderId: (providerId: string | undefined) => {
|
||||
set({ settingsGitProviderId: providerId });
|
||||
},
|
||||
|
||||
setSettingsGitModelId: (modelId: string | undefined) => {
|
||||
set({ settingsGitModelId: modelId });
|
||||
},
|
||||
|
||||
getResolvedGitGenerationModel: () => {
|
||||
const state = get();
|
||||
return resolveGitGenerationModelSelection({
|
||||
providers: state.providers,
|
||||
settingsGitProviderId: state.settingsGitProviderId,
|
||||
settingsGitModelId: state.settingsGitModelId,
|
||||
settingsZenModel: state.settingsZenModel,
|
||||
});
|
||||
},
|
||||
@@ -1844,8 +1771,6 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
settingsAutoCreateWorktree: state.settingsAutoCreateWorktree,
|
||||
settingsGitmojiEnabled: state.settingsGitmojiEnabled,
|
||||
settingsZenModel: state.settingsZenModel,
|
||||
settingsGitProviderId: state.settingsGitProviderId,
|
||||
settingsGitModelId: state.settingsGitModelId,
|
||||
speechRate: state.speechRate,
|
||||
speechPitch: state.speechPitch,
|
||||
speechVolume: state.speechVolume,
|
||||
|
||||
Reference in New Issue
Block a user