豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/ai/fetch.test.ts
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'] },
)
Copy link
Copy Markdown

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.tsx and detail.tsx rather 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 named fetch.test.ts but tests no function from src/ai/fetch.ts (like createModel).

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit da72a19. Configure here.


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)
})
})
12 changes: 12 additions & 0 deletions src/ai/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ export const createModel = async (modelConfig: Model) => {
})
return openrouter(modelConfig.model)
}
case 'minimax': {
if (!modelConfig.apiKey) {
throw new Error('No API key provided')
}
const minimax = createOpenAICompatible({
name: 'minimax',
baseURL: 'https://api.minimax.io/v1',
apiKey: modelConfig.apiKey,
fetch,
})
return minimax(modelConfig.model)
}
default:
throw new Error(`Unsupported provider: ${modelConfig.provider}`)
}
Expand Down
2 changes: 1 addition & 1 deletion src/db/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const modelsTable = sqliteTable(
{
id: text('id').primaryKey(),
provider: text('provider', {
enum: ['openai', 'custom', 'openrouter', 'thunderbolt', 'anthropic'],
enum: ['openai', 'custom', 'openrouter', 'thunderbolt', 'anthropic', 'minimax'],
}),
name: text('name'),
model: text('model'),
Expand Down
2 changes: 1 addition & 1 deletion src/settings/models/detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Trash2 } from 'lucide-react'

const formSchema = z
.object({
provider: z.enum(['thunderbolt', 'anthropic', 'openai', 'custom', 'openrouter']),
provider: z.enum(['thunderbolt', 'anthropic', '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(),
Expand Down
3 changes: 2 additions & 1 deletion src/settings/models/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MiniMax UI added to unreachable component, not active page

High Severity

The MiniMax provider dropdown and schema changes are added to NewModelPage in new.tsx and ModelDetailPage in detail.tsx, but neither component is connected to any route. The only models page wired into the app router (src/app.tsx line 161) is ModelsPage from src/settings/models/index.tsx, which has its own form schema, provider dropdown, getProviderDisplay, and fetchAvailableModels — none of which include minimax. Users will be unable to select MiniMax as a provider through the UI.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit da72a19. Configure here.

<SelectItem value="custom">Custom</SelectItem>
</SelectContent>
</Select>
Expand Down