diff --git a/src/renderer/src/utils/__tests__/model.test.ts b/src/renderer/src/utils/__tests__/model.test.ts index 79f7948fb1..d06a7a2f4b 100644 --- a/src/renderer/src/utils/__tests__/model.test.ts +++ b/src/renderer/src/utils/__tests__/model.test.ts @@ -118,13 +118,42 @@ describe('model', () => { }) }) + it('should handle model identifiers without provider prefix', () => { + expect(parseModelId('claude-3-sonnet')).toEqual({ + providerId: undefined, + modelId: 'claude-3-sonnet' + }) + + expect(parseModelId('gpt-4')).toEqual({ + providerId: undefined, + modelId: 'gpt-4' + }) + }) + it('should return undefined for invalid inputs', () => { expect(parseModelId(undefined)).toBeUndefined() expect(parseModelId('')).toBeUndefined() - expect(parseModelId('no-colon')).toBeUndefined() - expect(parseModelId(':missing-provider')).toBeUndefined() - expect(parseModelId('missing-model:')).toBeUndefined() - expect(parseModelId(':')).toBeUndefined() + expect(parseModelId(' ')).toBeUndefined() + }) + + it('should handle edge cases with colons', () => { + // Colon at start - treat as modelId without provider + expect(parseModelId(':missing-provider')).toEqual({ + providerId: undefined, + modelId: ':missing-provider' + }) + + // Colon at end - treat everything before as modelId + expect(parseModelId('missing-model:')).toEqual({ + providerId: undefined, + modelId: 'missing-model' + }) + + // Only colon - treat as modelId without provider + expect(parseModelId(':')).toEqual({ + providerId: undefined, + modelId: ':' + }) }) it('should handle edge cases', () => { diff --git a/src/renderer/src/utils/model.ts b/src/renderer/src/utils/model.ts index 23e955eff3..c3efda2cb7 100644 --- a/src/renderer/src/utils/model.ts +++ b/src/renderer/src/utils/model.ts @@ -87,7 +87,7 @@ export const apiModelAdapter = (model: ApiModel): AdaptedApiModel => { * where modelId may contain additional colons (e.g., "openrouter:anthropic/claude-3.5-sonnet:free") * * @param modelIdentifier - The full model identifier string - * @returns Object with providerId and modelId, or undefined if invalid + * @returns Object with providerId and modelId. If no provider prefix found, providerId will be undefined * * @example * parseModelId("openrouter:anthropic/claude-3.5-sonnet:free") @@ -98,20 +98,38 @@ export const apiModelAdapter = (model: ApiModel): AdaptedApiModel => { * // => { providerId: "anthropic", modelId: "claude-3-sonnet" } * * @example - * parseModelId("invalid") // => undefined + * parseModelId("claude-3-sonnet") + * // => { providerId: undefined, modelId: "claude-3-sonnet" } + * + * @example + * parseModelId("") // => undefined */ -export function parseModelId(modelIdentifier: string | undefined): { providerId: string; modelId: string } | undefined { - if (!modelIdentifier || typeof modelIdentifier !== 'string') { +export function parseModelId( + modelIdentifier: string | undefined +): { providerId: string | undefined; modelId: string } | undefined { + if (!modelIdentifier || typeof modelIdentifier !== 'string' || modelIdentifier.trim() === '') { return undefined } const colonIndex = modelIdentifier.indexOf(':') - // Must contain at least one colon and have content on both sides - if (colonIndex <= 0 || colonIndex >= modelIdentifier.length - 1) { - return undefined + // No colon found or colon at the start - treat entire string as modelId + if (colonIndex <= 0) { + return { + providerId: undefined, + modelId: modelIdentifier + } } + // Colon at the end - treat everything before as modelId + if (colonIndex >= modelIdentifier.length - 1) { + return { + providerId: undefined, + modelId: modelIdentifier.substring(0, colonIndex) + } + } + + // Standard format: "provider:modelId" return { providerId: modelIdentifier.substring(0, colonIndex), modelId: modelIdentifier.substring(colonIndex + 1)