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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ LLM_PROVIDER=
# Not needed for codex (uses ~/.codex/auth.json)
LLM_API_KEY=
# Optional override. Each provider has a sensible default:
# anthropic: claude-sonnet-4-6 | openai: gpt-5.4 | gemini: gemini-3.1-pro | codex: gpt-5.3-codex | openrouter: openrouter/auto | minimax: MiniMax-M2.5
# anthropic: claude-sonnet-4-6 | openai: gpt-5.4 | gemini: gemini-3.1-pro | codex: gpt-5.3-codex | openrouter: openrouter/auto | minimax: MiniMax-M2.7
LLM_MODEL=

# === Telegram Alerts (optional, requires LLM) ===
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Set `LLM_PROVIDER` to one of: `anthropic`, `openai`, `gemini`, `codex`, `openrou
| `gemini` | `LLM_API_KEY` | gemini-3.1-pro |
| `openrouter` | `LLM_API_KEY` | openrouter/auto |
| `codex` | None (uses `~/.codex/auth.json`) | gpt-5.3-codex |
| `minimax` | `LLM_API_KEY` | MiniMax-M2.5 |
| `minimax` | `LLM_API_KEY` | MiniMax-M2.7 |

For Codex, run `npx @openai/codex login` to authenticate via your ChatGPT subscription.

Expand Down Expand Up @@ -280,7 +280,7 @@ crucix/
│ │ ├── gemini.mjs # Gemini
│ │ ├── openrouter.mjs # OpenRouter (Unified API)
│ │ ├── codex.mjs # Codex (ChatGPT subscription)
│ │ ├── minimax.mjs # MiniMax (M2.5, 204K context)
│ │ ├── minimax.mjs # MiniMax (M2.7, latest flagship model)
│ │ ├── ideas.mjs # LLM-powered trade idea generation
│ │ └── index.mjs # Factory: createLLMProvider()
│ ├── delta/ # Change tracking between sweeps
Expand Down
2 changes: 1 addition & 1 deletion lib/llm/minimax.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class MiniMaxProvider extends LLMProvider {
super(config);
this.name = 'minimax';
this.apiKey = config.apiKey;
this.model = config.model || 'MiniMax-M2.5';
this.model = config.model || 'MiniMax-M2.7';
}

get isConfigured() { return !!this.apiKey; }
Expand Down
4 changes: 2 additions & 2 deletions test/llm-minimax-integration.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { MiniMaxProvider } from '../lib/llm/minimax.mjs';
const API_KEY = process.env.MINIMAX_API_KEY;

describe('MiniMax integration', { skip: !API_KEY && 'MINIMAX_API_KEY not set' }, () => {
it('should complete a prompt with MiniMax-M2.5', async () => {
const provider = new MiniMaxProvider({ apiKey: API_KEY, model: 'MiniMax-M2.5' });
it('should complete a prompt with MiniMax-M2.7', async () => {
const provider = new MiniMaxProvider({ apiKey: API_KEY, model: 'MiniMax-M2.7' });
assert.equal(provider.isConfigured, true);

const result = await provider.complete(
Expand Down
16 changes: 8 additions & 8 deletions test/llm-minimax.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ describe('MiniMaxProvider', () => {
it('should set defaults correctly', () => {
const provider = new MiniMaxProvider({ apiKey: 'sk-test' });
assert.equal(provider.name, 'minimax');
assert.equal(provider.model, 'MiniMax-M2.5');
assert.equal(provider.model, 'MiniMax-M2.7');
assert.equal(provider.isConfigured, true);
});

it('should accept custom model', () => {
const provider = new MiniMaxProvider({ apiKey: 'sk-test', model: 'MiniMax-M2.5-highspeed' });
assert.equal(provider.model, 'MiniMax-M2.5-highspeed');
const provider = new MiniMaxProvider({ apiKey: 'sk-test', model: 'MiniMax-M2.7-highspeed' });
assert.equal(provider.model, 'MiniMax-M2.7-highspeed');
});

it('should report not configured without API key', () => {
Expand Down Expand Up @@ -50,7 +50,7 @@ describe('MiniMaxProvider', () => {
const mockResponse = {
choices: [{ message: { content: 'Hello from MiniMax' } }],
usage: { prompt_tokens: 10, completion_tokens: 5 },
model: 'MiniMax-M2.5',
model: 'MiniMax-M2.7',
};
const originalFetch = globalThis.fetch;
globalThis.fetch = mock.fn(() =>
Expand All @@ -61,14 +61,14 @@ describe('MiniMaxProvider', () => {
assert.equal(result.text, 'Hello from MiniMax');
assert.equal(result.usage.inputTokens, 10);
assert.equal(result.usage.outputTokens, 5);
assert.equal(result.model, 'MiniMax-M2.5');
assert.equal(result.model, 'MiniMax-M2.7');
} finally {
globalThis.fetch = originalFetch;
}
});

it('should send correct request format', async () => {
const provider = new MiniMaxProvider({ apiKey: 'sk-test-key', model: 'MiniMax-M2.5' });
const provider = new MiniMaxProvider({ apiKey: 'sk-test-key', model: 'MiniMax-M2.7' });
let capturedUrl, capturedOpts;
const originalFetch = globalThis.fetch;
globalThis.fetch = mock.fn((url, opts) => {
Expand All @@ -79,7 +79,7 @@ describe('MiniMaxProvider', () => {
json: () => Promise.resolve({
choices: [{ message: { content: 'ok' } }],
usage: { prompt_tokens: 1, completion_tokens: 1 },
model: 'MiniMax-M2.5',
model: 'MiniMax-M2.7',
}),
});
});
Expand All @@ -91,7 +91,7 @@ describe('MiniMaxProvider', () => {
assert.equal(headers['Content-Type'], 'application/json');
assert.equal(headers['Authorization'], 'Bearer sk-test-key');
const body = JSON.parse(capturedOpts.body);
assert.equal(body.model, 'MiniMax-M2.5');
assert.equal(body.model, 'MiniMax-M2.7');
assert.equal(body.max_tokens, 2048);
assert.equal(body.messages[0].role, 'system');
assert.equal(body.messages[0].content, 'system prompt');
Expand Down