fix: support slash-containing model IDs (#1074)
* fix: support slash-containing model IDs * fix: parse worktree default model identifiers --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
f96374c738
commit
d35ff8a2db
@@ -16,6 +16,7 @@ import { ModelSelector } from './ModelSelector';
|
|||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -761,8 +762,8 @@ export const AgentsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
providerId={model ? model.split('/')[0] : ''}
|
providerId={parseModelIdentifier(model)?.providerId ?? ''}
|
||||||
modelId={model ? model.split('/')[1] : ''}
|
modelId={parseModelIdentifier(model)?.modelId ?? ''}
|
||||||
onChange={(providerId: string, modelId: string) => {
|
onChange={(providerId: string, modelId: string) => {
|
||||||
if (providerId && modelId) {
|
if (providerId && modelId) {
|
||||||
setModel(`${providerId}/${modelId}`);
|
setModel(`${providerId}/${modelId}`);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
|
|
||||||
export const CommandsPage: React.FC = () => {
|
export const CommandsPage: React.FC = () => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -295,8 +296,8 @@ export const CommandsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
<div className="flex min-w-0 flex-1 items-center gap-2 sm:w-fit sm:flex-initial">
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
providerId={model ? model.split('/')[0] : ''}
|
providerId={parseModelIdentifier(model)?.providerId ?? ''}
|
||||||
modelId={model ? model.split('/')[1] : ''}
|
modelId={parseModelIdentifier(model)?.modelId ?? ''}
|
||||||
onChange={(providerId: string, modelId: string) => {
|
onChange={(providerId: string, modelId: string) => {
|
||||||
if (providerId && modelId) {
|
if (providerId && modelId) {
|
||||||
setModel(`${providerId}/${modelId}`);
|
setModel(`${providerId}/${modelId}`);
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ import { useUIStore } from '@/stores/useUIStore';
|
|||||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
|
|
||||||
const getDisplayModel = (
|
const getDisplayModel = (
|
||||||
storedModel: string | undefined
|
storedModel: string | undefined
|
||||||
): { providerId: string; modelId: string } => {
|
): { providerId: string; modelId: string } => {
|
||||||
if (storedModel) {
|
const parsed = parseModelIdentifier(storedModel);
|
||||||
const parts = storedModel.split('/');
|
if (parsed) {
|
||||||
if (parts.length === 2 && parts[0] && parts[1]) {
|
return parsed;
|
||||||
return { providerId: parts[0], modelId: parts[1] };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { providerId: '', modelId: '' };
|
return { providerId: '', modelId: '' };
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { useUIStore } from '@/stores/useUIStore';
|
|||||||
import { useGitHubAuthStore } from '@/stores/useGitHubAuthStore';
|
import { useGitHubAuthStore } from '@/stores/useGitHubAuthStore';
|
||||||
import { opencodeClient } from '@/lib/opencode/client';
|
import { opencodeClient } from '@/lib/opencode/client';
|
||||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
import { createWorktreeSessionForNewBranch } from '@/lib/worktreeSessionCreator';
|
import { createWorktreeSessionForNewBranch } from '@/lib/worktreeSessionCreator';
|
||||||
import { generateBranchSlug } from '@/lib/git/branchNameGenerator';
|
import { generateBranchSlug } from '@/lib/git/branchNameGenerator';
|
||||||
import type { GitHubIssue, GitHubIssueComment, GitHubIssuesListResult, GitHubIssueSummary, GitHubRepoSelector } from '@/lib/api/types';
|
import type { GitHubIssue, GitHubIssueComment, GitHubIssuesListResult, GitHubIssueSummary, GitHubRepoSelector } from '@/lib/api/types';
|
||||||
@@ -234,14 +235,11 @@ export function GitHubIssuePickerDialog({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = settingsDefaultModel.split('/');
|
const parsed = parseModelIdentifier(settingsDefaultModel);
|
||||||
if (parts.length !== 2) {
|
if (!parsed) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const [providerID, modelID] = parts;
|
|
||||||
if (!providerID || !modelID) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { providerId: providerID, modelId: modelID } = parsed;
|
||||||
|
|
||||||
const modelMetadata = configState.getModelMetadata(providerID, modelID);
|
const modelMetadata = configState.getModelMetadata(providerID, modelID);
|
||||||
if (!modelMetadata) {
|
if (!modelMetadata) {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import { getRootBranch } from '@/lib/worktrees/worktreeStatus';
|
|||||||
import { generateBranchSlug } from '@/lib/git/branchNameGenerator';
|
import { generateBranchSlug } from '@/lib/git/branchNameGenerator';
|
||||||
import { opencodeClient } from '@/lib/opencode/client';
|
import { opencodeClient } from '@/lib/opencode/client';
|
||||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
import { rankBranchesForQuery } from '@/lib/worktrees/branchSearch';
|
import { rankBranchesForQuery } from '@/lib/worktrees/branchSearch';
|
||||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||||
import { useGitBranches, useGitStore, useGitLoadingBranches } from '@/stores/useGitStore';
|
import { useGitBranches, useGitStore, useGitLoadingBranches } from '@/stores/useGitStore';
|
||||||
@@ -428,10 +429,9 @@ export function NewWorktreeDialog({
|
|||||||
const settingsDefaultModel = configState.settingsDefaultModel;
|
const settingsDefaultModel = configState.settingsDefaultModel;
|
||||||
if (!settingsDefaultModel) return null;
|
if (!settingsDefaultModel) return null;
|
||||||
|
|
||||||
const parts = settingsDefaultModel.split('/');
|
const parsed = parseModelIdentifier(settingsDefaultModel);
|
||||||
if (parts.length !== 2) return null;
|
if (!parsed) return null;
|
||||||
const [providerID, modelID] = parts;
|
const { providerId: providerID, modelId: modelID } = parsed;
|
||||||
if (!providerID || !modelID) return null;
|
|
||||||
|
|
||||||
const modelMetadata = configState.getModelMetadata(providerID, modelID);
|
const modelMetadata = configState.getModelMetadata(providerID, modelID);
|
||||||
if (!modelMetadata) return null;
|
if (!modelMetadata) return null;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export interface ParsedModelIdentifier {
|
||||||
|
providerId: string;
|
||||||
|
modelId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseModelIdentifier = (value: string | undefined): ParsedModelIdentifier | null => {
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const separatorIndex = value.indexOf('/');
|
||||||
|
if (separatorIndex <= 0 || separatorIndex >= value.length - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
providerId: value.slice(0, separatorIndex),
|
||||||
|
modelId: value.slice(separatorIndex + 1),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@ import { useContextStore } from '@/stores/contextStore';
|
|||||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||||
import { checkIsGitRepository, previewGitWorktree } from '@/lib/gitApi';
|
import { checkIsGitRepository, previewGitWorktree } from '@/lib/gitApi';
|
||||||
import { generateBranchName } from '@/lib/git/branchNameGenerator';
|
import { generateBranchName } from '@/lib/git/branchNameGenerator';
|
||||||
|
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||||
import { getRootBranch } from '@/lib/worktrees/worktreeStatus';
|
import { getRootBranch } from '@/lib/worktrees/worktreeStatus';
|
||||||
import { getWorktreeSetupCommands } from '@/lib/openchamberConfig';
|
import { getWorktreeSetupCommands } from '@/lib/openchamberConfig';
|
||||||
import {
|
import {
|
||||||
@@ -87,12 +88,12 @@ const applyDefaultAgentAndModelSelection = (sessionId: string, configState = use
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = settingsDefaultModel.split('/');
|
const parsed = parseModelIdentifier(settingsDefaultModel);
|
||||||
if (parts.length !== 2) {
|
if (!parsed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [providerId, modelId] = parts;
|
const { providerId, modelId } = parsed;
|
||||||
const modelMetadata = configState.getModelMetadata(providerId, modelId);
|
const modelMetadata = configState.getModelMetadata(providerId, modelId);
|
||||||
if (!modelMetadata) {
|
if (!modelMetadata) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { getRegisteredRuntimeAPIs } from "@/contexts/runtimeAPIRegistry";
|
|||||||
import { updateDesktopSettings } from "@/lib/persistence";
|
import { updateDesktopSettings } from "@/lib/persistence";
|
||||||
import { useDirectoryStore } from "@/stores/useDirectoryStore";
|
import { useDirectoryStore } from "@/stores/useDirectoryStore";
|
||||||
import { streamDebugEnabled } from "@/stores/utils/streamDebug";
|
import { streamDebugEnabled } from "@/stores/utils/streamDebug";
|
||||||
|
import { parseModelIdentifier } from "@/lib/modelIdentifier";
|
||||||
|
|
||||||
const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
||||||
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
|
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
|
||||||
@@ -105,14 +106,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseModelString = (modelString: string): { providerId: string; modelId: string } | null => {
|
const parseModelString = (modelString: string): { providerId: string; modelId: string } | null => {
|
||||||
if (!modelString || typeof modelString !== 'string') {
|
return parseModelIdentifier(modelString);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const parts = modelString.split('/');
|
|
||||||
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { providerId: parts[0], modelId: parts[1] };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeProviderId = (value: string) => value?.toLowerCase?.() ?? '';
|
const normalizeProviderId = (value: string) => value?.toLowerCase?.() ?? '';
|
||||||
|
|||||||
Reference in New Issue
Block a user