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
19 changes: 19 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const PROVIDER_OPTIONS: Array<{ key: ProviderName; label: string }> = [
{ key: 'grok', label: 'Grok (xAI)' },
{ key: 'ollamaCloud', label: 'Ollama Cloud' },
{ key: 'ollamaLocal', label: 'Ollama Local' },
{ key: 'minimax', label: 'MiniMax' },
];

function getConfiguredProviderNames(config: MercuryConfig): ProviderName[] {
Expand Down Expand Up @@ -666,6 +667,24 @@ async function configure(existingConfig?: MercuryConfig): Promise<void> {
config.providers.ollamaLocal.model = result.model;
config.providers.ollamaLocal.enabled = true;
}
continue;
}

if (provider === 'minimax') {
const mask = isReconfig && config.providers.minimax.apiKey ? ` [${maskKey(config.providers.minimax.apiKey)}]` : '';
const result = await promptApiKeyWithModelSelection(
config,
'minimax',
'MiniMax',
chalk.white(` MiniMax API key${mask}${isReconfig ? '' : ' (Enter to skip)'}: `),
isReconfig,
);
if (!result.skipped && result.apiKey && result.model) {
config.providers.minimax.apiKey = result.apiKey;
config.providers.minimax.model = result.model;
config.providers.minimax.enabled = true;
}
continue;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { BaseProvider } from './base.js';
export { OpenAICompatProvider } from './openai-compat.js';
export { AnthropicProvider } from './anthropic.js';
export { DeepSeekProvider } from './deepseek.js';
export { MiniMaxProvider } from './minimax.js';
export { OllamaProvider } from './ollama.js';
export { ProviderRegistry } from './registry.js';
export type { LLMResponse, LLMStreamChunk } from './base.js';
37 changes: 37 additions & 0 deletions src/providers/minimax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createOpenAI } from '@ai-sdk/openai';
import { BaseProvider } from './base.js';
import type { ProviderConfig } from '../utils/config.js';

export class MiniMaxProvider extends BaseProvider {
readonly name: string;
readonly model: string;
private modelInstance: any;

constructor(config: ProviderConfig) {
super(config);
this.name = config.name;
this.model = config.model;

const client = createOpenAI({
apiKey: config.apiKey,
baseURL: config.baseUrl,
});
this.modelInstance = client.chat(config.model);
}

async generateText(_prompt: string, _systemPrompt: string): Promise<never> {
throw new Error('Use getModelInstance() with the AI SDK agent loop');
}

async *streamText(_prompt: string, _systemPrompt: string): AsyncIterable<never> {
throw new Error('Use getModelInstance() with the AI SDK agent loop');
}

isAvailable(): boolean {
return this.config.apiKey.length > 0;
}

getModelInstance() {
return this.modelInstance;
}
}
4 changes: 4 additions & 0 deletions src/providers/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { BaseProvider } from './base.js';
import { OpenAICompatProvider } from './openai-compat.js';
import { AnthropicProvider } from './anthropic.js';
import { DeepSeekProvider } from './deepseek.js';
import { MiniMaxProvider } from './minimax.js';
import { OllamaProvider } from './ollama.js';
import { logger } from '../utils/logger.js';

Expand All @@ -22,6 +23,7 @@ export class ProviderRegistry {
config.providers.grok,
config.providers.ollamaCloud,
config.providers.ollamaLocal,
config.providers.minimax,
];

for (const pc of entries) {
Expand All @@ -34,6 +36,8 @@ export class ProviderRegistry {
provider = new DeepSeekProvider(pc);
} else if (pc.name === 'ollamaCloud' || pc.name === 'ollamaLocal') {
provider = new OllamaProvider(pc);
} else if (pc.name === 'minimax') {
provider = new MiniMaxProvider(pc);
} else {
provider = new OpenAICompatProvider(pc);
}
Expand Down
11 changes: 10 additions & 1 deletion src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export type ProviderName =
| 'deepseek'
| 'grok'
| 'ollamaCloud'
| 'ollamaLocal';
| 'ollamaLocal'
| 'minimax';

export interface MercuryConfig {
identity: {
Expand All @@ -68,6 +69,7 @@ export interface MercuryConfig {
grok: ProviderConfig;
ollamaCloud: ProviderConfig;
ollamaLocal: ProviderConfig;
minimax: ProviderConfig;
};
channels: {
telegram: {
Expand Down Expand Up @@ -173,6 +175,13 @@ export function getDefaultConfig(): MercuryConfig {
model: getEnv('OLLAMA_LOCAL_MODEL', 'gpt-oss:20b'),
enabled: getEnvBool('OLLAMA_LOCAL_ENABLED', false),
},
minimax: {
name: 'minimax',
apiKey: getEnv('MINIMAX_API_KEY', ''),
baseUrl: getEnv('MINIMAX_BASE_URL', 'https://api.minimax.io/v1'),
model: getEnv('MINIMAX_MODEL', 'MiniMax-M2.7'),
enabled: getEnvBool('MINIMAX_ENABLED', true),
},
},
channels: {
telegram: {
Expand Down
18 changes: 17 additions & 1 deletion src/utils/provider-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ const OLLAMA_LOCAL_PREFERRED_MODELS = [
'gpt-oss:120b',
] as const;

const MINIMAX_PREFERRED_MODELS = [
'MiniMax-M2.7',
'MiniMax-M2.7-highspeed',
'MiniMax-M2.5',
'MiniMax-M2.1',
] as const;

export class ProviderModelFetchError extends Error {
constructor(message: string) {
super(message);
Expand Down Expand Up @@ -154,6 +161,7 @@ function chooseRecommendedModel(
grok: GROK_PREFERRED_MODELS,
ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
minimax: MINIMAX_PREFERRED_MODELS,
};

for (const candidate of preferredByProvider[provider]) {
Expand Down Expand Up @@ -187,6 +195,7 @@ export function buildModelCatalog(
grok: GROK_PREFERRED_MODELS,
ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
minimax: MINIMAX_PREFERRED_MODELS,
};

const withoutRecommended = filtered.filter((model) => model !== recommendedModel);
Expand All @@ -206,7 +215,7 @@ async function fetchOpenAICompatModels(provider: ProviderName, config: ProviderC
Authorization: `Bearer ${config.apiKey}`,
},
},
`Mercury could not fetch models for this ${provider === 'grok' ? 'Grok' : provider === 'deepseek' ? 'DeepSeek' : 'OpenAI'} key. Please re-enter it.`,
`Mercury could not fetch models for this ${provider === 'grok' ? 'Grok' : provider === 'deepseek' ? 'DeepSeek' : provider === 'minimax' ? 'MiniMax' : 'OpenAI'} key. Please re-enter it.`,
);

const ids = (data.data ?? [])
Expand All @@ -215,6 +224,9 @@ async function fetchOpenAICompatModels(provider: ProviderName, config: ProviderC
if (provider === 'deepseek') {
return id.startsWith('deepseek-');
}
if (provider === 'minimax') {
return id.startsWith('MiniMax-');
}
return isOpenAIChatModel(id);
});

Expand Down Expand Up @@ -297,5 +309,9 @@ export async function fetchProviderModelCatalog(
return fetchOllamaModels(provider, config);
}

if (provider === 'minimax') {
return buildModelCatalog(provider, [...MINIMAX_PREFERRED_MODELS], config.model);
}

return fetchOpenAICompatModels(provider, config);
}