Skip to content

Commit d5d4e40

Browse files
committed
feat: add MiniMax provider support
- Add MiniMax chat model provider with OpenAI-compatible API - Support MiniMax-M2.5 and MiniMax-M2.5-highspeed models (204K context) - Add MiniMaxIcon to icons component - Register provider in types, registry, utils, and models - Clamp temperature to (0, 1] range per MiniMax API constraints - Add unit tests for provider metadata and request execution
1 parent 4c12914 commit d5d4e40

File tree

8 files changed

+792
-0
lines changed

8 files changed

+792
-0
lines changed

apps/sim/components/icons.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,6 +2783,21 @@ export const GroqIcon = (props: SVGProps<SVGSVGElement>) => (
27832783
</svg>
27842784
)
27852785

2786+
export const MiniMaxIcon = (props: SVGProps<SVGSVGElement>) => (
2787+
<svg {...props} height='1em' viewBox='0 0 120 120' width='1em' xmlns='http://www.w3.org/2000/svg'>
2788+
<title>MiniMax</title>
2789+
<rect width='120' height='120' rx='24' fill='#1A1A2E' />
2790+
<path
2791+
d='M30 80V40l15 20 15-20v40M70 40v40M80 40l10 20 10-20v40'
2792+
stroke='#E94560'
2793+
strokeWidth='6'
2794+
strokeLinecap='round'
2795+
strokeLinejoin='round'
2796+
fill='none'
2797+
/>
2798+
</svg>
2799+
)
2800+
27862801
export const DeepseekIcon = (props: SVGProps<SVGSVGElement>) => (
27872802
<svg {...props} height='1em' viewBox='0 0 24 24' width='1em' xmlns='http://www.w3.org/2000/svg'>
27882803
<title>DeepSeek</title>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
6+
const { mockCreate, MockOpenAI } = vi.hoisted(() => {
7+
const mockCreate = vi.fn()
8+
const MockOpenAI = vi.fn().mockImplementation(() => ({
9+
chat: {
10+
completions: {
11+
create: mockCreate,
12+
},
13+
},
14+
}))
15+
return { mockCreate, MockOpenAI }
16+
})
17+
18+
vi.mock('openai', () => ({
19+
default: MockOpenAI,
20+
}))
21+
22+
vi.mock('@/tools', () => ({
23+
executeTool: vi.fn(),
24+
}))
25+
26+
import { minimaxProvider } from '@/providers/minimax'
27+
28+
describe('MiniMax Provider', () => {
29+
beforeEach(() => {
30+
vi.clearAllMocks()
31+
})
32+
33+
describe('provider metadata', () => {
34+
it('has correct id and name', () => {
35+
expect(minimaxProvider.id).toBe('minimax')
36+
expect(minimaxProvider.name).toBe('MiniMax')
37+
})
38+
39+
it('has correct version', () => {
40+
expect(minimaxProvider.version).toBe('1.0.0')
41+
})
42+
43+
it('has models defined', () => {
44+
expect(minimaxProvider.models).toBeDefined()
45+
expect(minimaxProvider.models.length).toBeGreaterThan(0)
46+
})
47+
48+
it('has default model set', () => {
49+
expect(minimaxProvider.defaultModel).toBe('MiniMax-M2.5')
50+
})
51+
})
52+
53+
describe('executeRequest', () => {
54+
it('throws when API key is missing', async () => {
55+
await expect(
56+
minimaxProvider.executeRequest({
57+
model: 'MiniMax-M2.5',
58+
messages: [{ role: 'user', content: 'Hello' }],
59+
})
60+
).rejects.toThrow('API key is required for MiniMax')
61+
})
62+
63+
it('creates OpenAI client with correct base URL', async () => {
64+
mockCreate.mockResolvedValue({
65+
choices: [{ message: { content: 'Hello!', tool_calls: undefined } }],
66+
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
67+
})
68+
69+
await minimaxProvider.executeRequest({
70+
model: 'MiniMax-M2.5',
71+
apiKey: 'test-key',
72+
messages: [{ role: 'user', content: 'Hello' }],
73+
})
74+
75+
expect(MockOpenAI).toHaveBeenCalledWith({
76+
apiKey: 'test-key',
77+
baseURL: 'https://api.minimax.io/v1',
78+
})
79+
})
80+
81+
it('returns content from response', async () => {
82+
mockCreate.mockResolvedValue({
83+
choices: [{ message: { content: 'Hello from MiniMax!', tool_calls: undefined } }],
84+
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
85+
})
86+
87+
const result = await minimaxProvider.executeRequest({
88+
model: 'MiniMax-M2.5',
89+
apiKey: 'test-key',
90+
messages: [{ role: 'user', content: 'Hello' }],
91+
})
92+
93+
expect(result).toHaveProperty('content', 'Hello from MiniMax!')
94+
})
95+
96+
it('clamps temperature to valid range', async () => {
97+
mockCreate.mockResolvedValue({
98+
choices: [{ message: { content: 'ok', tool_calls: undefined } }],
99+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
100+
})
101+
102+
await minimaxProvider.executeRequest({
103+
model: 'MiniMax-M2.5',
104+
apiKey: 'test-key',
105+
temperature: 0,
106+
messages: [{ role: 'user', content: 'Hi' }],
107+
})
108+
109+
const callArgs = mockCreate.mock.calls[0][0]
110+
expect(callArgs.temperature).toBeGreaterThan(0)
111+
expect(callArgs.temperature).toBeLessThanOrEqual(1)
112+
})
113+
114+
it('includes system prompt when provided', async () => {
115+
mockCreate.mockResolvedValue({
116+
choices: [{ message: { content: 'ok', tool_calls: undefined } }],
117+
usage: { prompt_tokens: 10, completion_tokens: 2, total_tokens: 12 },
118+
})
119+
120+
await minimaxProvider.executeRequest({
121+
model: 'MiniMax-M2.5',
122+
apiKey: 'test-key',
123+
systemPrompt: 'You are a helpful assistant',
124+
messages: [{ role: 'user', content: 'Hi' }],
125+
})
126+
127+
const callArgs = mockCreate.mock.calls[0][0]
128+
expect(callArgs.messages[0]).toEqual({
129+
role: 'system',
130+
content: 'You are a helpful assistant',
131+
})
132+
})
133+
134+
it('returns token usage information', async () => {
135+
mockCreate.mockResolvedValue({
136+
choices: [{ message: { content: 'Hello!', tool_calls: undefined } }],
137+
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
138+
})
139+
140+
const result = await minimaxProvider.executeRequest({
141+
model: 'MiniMax-M2.5',
142+
apiKey: 'test-key',
143+
messages: [{ role: 'user', content: 'Hello' }],
144+
})
145+
146+
expect(result).toHaveProperty('tokens')
147+
const response = result as any
148+
expect(response.tokens.input).toBe(10)
149+
expect(response.tokens.output).toBe(5)
150+
expect(response.tokens.total).toBe(15)
151+
})
152+
})
153+
})

0 commit comments

Comments
 (0)