-
Notifications
You must be signed in to change notification settings - Fork 181
feat: add MiniMax provider support #686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import { describe, expect, it } from 'bun:test' | ||
| import { z } from 'zod' | ||
|
|
||
| /** | ||
| * Tests for MiniMax provider support in the model creation form schemas. | ||
| * | ||
| * These tests verify the provider-level validation rules: | ||
| * - MiniMax requires an API key (like openai / openrouter) | ||
| * - MiniMax does not require a URL (unlike custom) | ||
| */ | ||
|
|
||
| // Mirror the form schema from src/settings/models/new.tsx | ||
| const newModelFormSchema = z | ||
| .object({ | ||
| provider: z.enum(['thunderbolt', 'openai', 'custom', 'openrouter', 'minimax']), | ||
| name: z.string().min(1), | ||
| model: z.string().min(1), | ||
| url: z.string().optional(), | ||
| apiKey: z.string().optional(), | ||
| }) | ||
| .refine( | ||
| (data) => { | ||
| if (data.provider === 'custom') return data.url !== undefined && data.url.length > 0 | ||
| return true | ||
| }, | ||
| { message: 'URL is required for Custom providers', path: ['url'] }, | ||
| ) | ||
| .refine( | ||
| (data) => { | ||
| if (data.provider === 'custom' || data.provider === 'thunderbolt') return true | ||
| return data.apiKey !== undefined && data.apiKey.length > 0 | ||
| }, | ||
| { message: 'API Key is required for this provider', path: ['apiKey'] }, | ||
| ) | ||
|
|
||
| // Mirror the form schema from src/settings/models/detail.tsx | ||
| const detailModelFormSchema = z | ||
| .object({ | ||
| provider: z.enum(['thunderbolt', 'anthropic', 'openai', 'custom', 'openrouter', 'minimax']), | ||
| name: z.string().min(1), | ||
| model: z.string().min(1), | ||
| url: z.string().optional(), | ||
| apiKey: z.string().optional(), | ||
| }) | ||
| .refine( | ||
| (data) => { | ||
| if (data.provider === 'custom') return data.url !== undefined && data.url.length > 0 | ||
| return true | ||
| }, | ||
| { message: 'URL is required for Custom providers', path: ['url'] }, | ||
| ) | ||
| .refine( | ||
| (data) => { | ||
| if (data.provider === 'custom' || data.provider === 'thunderbolt') return true | ||
| return data.apiKey !== undefined && data.apiKey.length > 0 | ||
| }, | ||
| { message: 'API Key is required for this provider', path: ['apiKey'] }, | ||
| ) | ||
|
|
||
| const validBase = { name: 'MiniMax M2.7', model: 'MiniMax-M2.7', url: '', apiKey: '' } | ||
|
|
||
| describe('MiniMax provider - new model form schema', () => { | ||
| it('accepts minimax as a valid provider', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'minimax', | ||
| apiKey: 'mm-test-key', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
|
|
||
| it('rejects minimax without an API key', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'minimax', | ||
| apiKey: '', | ||
| }) | ||
| expect(result.success).toBe(false) | ||
| const errors = result.error?.flatten().fieldErrors | ||
| expect(errors?.apiKey).toBeDefined() | ||
| }) | ||
|
|
||
| it('does not require a URL for minimax', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'minimax', | ||
| apiKey: 'mm-test-key', | ||
| url: '', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
|
|
||
| it('includes minimax in the provider enum', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'unknown-provider', | ||
| apiKey: 'key', | ||
| }) | ||
| expect(result.success).toBe(false) | ||
| }) | ||
|
|
||
| it('accepts MiniMax-M2.7 as model name', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| provider: 'minimax', | ||
| name: 'MiniMax M2.7', | ||
| model: 'MiniMax-M2.7', | ||
| apiKey: 'mm-key', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
|
|
||
| it('accepts MiniMax-M2.7-highspeed as model name', () => { | ||
| const result = newModelFormSchema.safeParse({ | ||
| provider: 'minimax', | ||
| name: 'MiniMax M2.7 Highspeed', | ||
| model: 'MiniMax-M2.7-highspeed', | ||
| apiKey: 'mm-key', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
| }) | ||
|
|
||
| describe('MiniMax provider - model detail form schema', () => { | ||
| it('accepts minimax provider for editing', () => { | ||
| const result = detailModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'minimax', | ||
| apiKey: 'mm-test-key', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
|
|
||
| it('rejects minimax without API key on detail form', () => { | ||
| const result = detailModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'minimax', | ||
| apiKey: '', | ||
| }) | ||
| expect(result.success).toBe(false) | ||
| const errors = result.error?.flatten().fieldErrors | ||
| expect(errors?.apiKey).toBeDefined() | ||
| }) | ||
|
|
||
| it('includes anthropic in detail form provider enum (for direct Anthropic models)', () => { | ||
| const result = detailModelFormSchema.safeParse({ | ||
| ...validBase, | ||
| provider: 'anthropic', | ||
| apiKey: 'sk-ant-test', | ||
| }) | ||
| expect(result.success).toBe(true) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ import type { Model } from '@/types' | |
|
|
||
| const formSchema = z | ||
| .object({ | ||
| provider: z.enum(['thunderbolt', 'openai', 'custom', 'openrouter']), | ||
| provider: z.enum(['thunderbolt', 'openai', 'custom', 'openrouter', 'minimax']), | ||
| name: z.string().min(1, { message: 'Name is required.' }), | ||
| model: z.string().min(1, { message: 'Model name is required.' }), | ||
| url: z.string().optional(), | ||
|
|
@@ -119,6 +119,7 @@ export default function NewModelPage() { | |
| <SelectItem value="thunderbolt">Thunderbolt</SelectItem> | ||
| <SelectItem value="openai">OpenAI</SelectItem> | ||
| <SelectItem value="openrouter">OpenRouter</SelectItem> | ||
| <SelectItem value="minimax">MiniMax</SelectItem> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MiniMax UI added to unreachable component, not active pageHigh Severity The MiniMax provider dropdown and schema changes are added to Additional Locations (2)Reviewed by Cursor Bugbot for commit da72a19. Configure here. |
||
| <SelectItem value="custom">Custom</SelectItem> | ||
| </SelectContent> | ||
| </Select> | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests validate duplicated schemas, not production code
Medium Severity
The test file defines local copies of the form schemas from
new.tsxanddetail.tsxrather than importing or testing the actual production code. These duplicated schemas will silently drift if the source schemas change, giving false confidence in test coverage. Additionally, the file is namedfetch.test.tsbut tests no function fromsrc/ai/fetch.ts(likecreateModel).Reviewed by Cursor Bugbot for commit da72a19. Configure here.