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
1 change: 1 addition & 0 deletions models/embedjs-minimax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Changelog
53 changes: 53 additions & 0 deletions models/embedjs-minimax/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# @llm-tools/embedjs-minimax

MiniMax model provider for [EmbedJs](https://github.com/llm-tools/embedjs).

## Installation

```bash
npm install @llm-tools/embedjs-minimax
```

## Usage

```typescript
import { MiniMax } from '@llm-tools/embedjs-minimax';

// Uses MINIMAX_API_KEY env var by default
const model = new MiniMax({
modelName: 'MiniMax-M2.7', // default
});

// Or pass API key directly
const model = new MiniMax({
apiKey: 'your-api-key',
modelName: 'MiniMax-M2.7-highspeed',
temperature: 0.7,
});
```

### Available Models

| Model | Context Window | Description |
|-------|---------------|-------------|
| `MiniMax-M2.7` | 204K tokens | Latest flagship model (default) |
| `MiniMax-M2.7-highspeed` | 204K tokens | Fast variant for latency-sensitive use |

### With RAG Application

```typescript
import { RAGApplicationBuilder } from '@llm-tools/embedjs';
import { MiniMax } from '@llm-tools/embedjs-minimax';
import { OpenAiEmbeddings } from '@llm-tools/embedjs-openai';

const app = await new RAGApplicationBuilder()
.setModel(new MiniMax({ modelName: 'MiniMax-M2.7' }))
.setEmbeddingModel(new OpenAiEmbeddings())
.build();
```

## Configuration

Set the `MINIMAX_API_KEY` environment variable or pass `apiKey` in the constructor.

Get your API key at [MiniMax Platform](https://platform.minimaxi.com/).
20 changes: 20 additions & 0 deletions models/embedjs-minimax/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import baseConfig from '../../eslint.config.js';
import parser from '@nx/eslint-plugin';

export default [
...baseConfig,
{
files: ['**/*.json'],
rules: {
'@nx/dependency-checks': [
'error',
{
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
},
],
},
languageOptions: {
parser,
},
},
];
39 changes: 39 additions & 0 deletions models/embedjs-minimax/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@llm-tools/embedjs-minimax",
"version": "0.1.31",
"description": "Enable usage of MiniMax models with embedjs",
"dependencies": {
"@langchain/core": "^1.0.5",
"@langchain/openai": "^1.1.1",
"@llm-tools/embedjs-interfaces": "0.1.31",
"debug": "^4.4.3"
},
"type": "module",
"main": "./src/index.js",
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"keywords": [
"llm",
"ai",
"minimax",
"chain",
"prompt",
"prompt engineering",
"chatgpt",
"machine learning",
"ml",
"embeddings",
"vectorstores"
],
"author": "K V Adhityan",
"bugs": {
"url": "https://github.com/llm-tools/embedjs/issues"
},
"homepage": "https://github.com/llm-tools/embedjs#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/llm-tools/embedjs.git"
}
}
19 changes: 19 additions & 0 deletions models/embedjs-minimax/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "embedjs-minimax",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "models/embedjs-minimax/src",
"projectType": "library",
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/embedjs-minimax",
"main": "models/embedjs-minimax/src/index.ts",
"tsConfig": "models/embedjs-minimax/tsconfig.json",
"assets": ["models/embedjs-minimax/*.md"]
}
}
}
}
1 change: 1 addition & 0 deletions models/embedjs-minimax/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './minimax-model.js';
50 changes: 50 additions & 0 deletions models/embedjs-minimax/src/minimax-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import createDebugMessages from 'debug';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, AIMessage, SystemMessage } from '@langchain/core/messages';
import { BaseModel, ModelResponse } from '@llm-tools/embedjs-interfaces';

export class MiniMax extends BaseModel {
private readonly debug = createDebugMessages('embedjs:model:MiniMax');
private model: ChatOpenAI;

Check warning on line 8 in models/embedjs-minimax/src/minimax-model.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member 'model' is never reassigned; mark it as `readonly`.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVl1Z7p57OGEbcIE&open=AZ01TVl1Z7p57OGEbcIE&pullRequest=226

constructor({
temperature,
apiKey,
modelName,
}: {
temperature?: number;
apiKey?: string;
modelName?: string;
}) {
// MiniMax temperature must be in (0.0, 1.0]; clamp to valid range
const clampedTemp = temperature !== undefined ? Math.min(Math.max(temperature, 0.01), 1.0) : undefined;

Check warning on line 20 in models/embedjs-minimax/src/minimax-model.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVl1Z7p57OGEbcIF&open=AZ01TVl1Z7p57OGEbcIF&pullRequest=226

Check warning on line 20 in models/embedjs-minimax/src/minimax-model.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVl1Z7p57OGEbcIG&open=AZ01TVl1Z7p57OGEbcIG&pullRequest=226
super(clampedTemp);

this.model = new ChatOpenAI({
apiKey: apiKey ?? process.env.MINIMAX_API_KEY,
model: modelName ?? 'MiniMax-M2.7',
temperature: clampedTemp,
configuration: {
baseURL: 'https://api.minimax.io/v1',
},
});
}

override async runQuery(messages: (AIMessage | SystemMessage | HumanMessage)[]): Promise<ModelResponse> {
this.debug('Executing MiniMax model with prompt -', messages[messages.length - 1].content);

Check warning on line 34 in models/embedjs-minimax/src/minimax-model.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `.at(…)` over `[….length - index]`.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVl1Z7p57OGEbcIH&open=AZ01TVl1Z7p57OGEbcIH&pullRequest=226
const result = await this.model.invoke(messages);
this.debug('MiniMax response -', result);

// Strip <think>...</think> reasoning tags from MiniMax responses
const raw = result.content.toString();
const cleaned = raw.replace(/<think>[\s\S]*?<\/think>\s*/g, '').trim();

Check warning on line 40 in models/embedjs-minimax/src/minimax-model.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVl1Z7p57OGEbcII&open=AZ01TVl1Z7p57OGEbcII&pullRequest=226

return {
result: cleaned,
tokenUse: {
inputTokens: (result.usage_metadata as Record<string, number>)?.input_tokens ?? 0,
outputTokens: (result.usage_metadata as Record<string, number>)?.output_tokens ?? 0,
},
};
}
}
124 changes: 124 additions & 0 deletions models/embedjs-minimax/tests/integration.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* Standalone integration test for MiniMax model.
* Tests the MiniMax API directly using @langchain/openai ChatOpenAI.
* Run: MINIMAX_API_KEY=xxx node models/embedjs-minimax/tests/integration.test.mjs
*/
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

const MINIMAX_API_KEY = process.env.MINIMAX_API_KEY;

describe('MiniMax Integration (standalone)', { skip: !MINIMAX_API_KEY }, () => {
function createModel(modelName, temperature) {

Check warning on line 14 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function 'createModel' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH4&open=AZ01TVj_Z7p57OGEbcH4&pullRequest=226
return new ChatOpenAI({
apiKey: MINIMAX_API_KEY,
model: modelName,
temperature: Math.min(Math.max(temperature ?? 0.1, 0.01), 1.0),

Check warning on line 18 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH5&open=AZ01TVj_Z7p57OGEbcH5&pullRequest=226
configuration: {
baseURL: 'https://api.minimax.io/v1',
},
});
}

it('should get a response from MiniMax-M2.7', async () => {
const model = createModel('MiniMax-M2.7', 0.1);
const result = await model.invoke([new HumanMessage('Reply with exactly one word: hello')]);
assert.ok(result.content.toString().length > 0, 'Response should not be empty');
console.log('M2.7 response:', result.content.toString().substring(0, 100));
});

it('should get a response from MiniMax-M2.7-highspeed', async () => {
const model = createModel('MiniMax-M2.7-highspeed', 0.1);
const result = await model.invoke([new HumanMessage('Reply with exactly one word: hello')]);
assert.ok(result.content.toString().length > 0, 'Response should not be empty');
console.log('M2.7-highspeed response:', result.content.toString().substring(0, 100));
});

it('should return token usage metadata', async () => {
const model = createModel('MiniMax-M2.7-highspeed', 0.5);
const result = await model.invoke([new HumanMessage('Say hi')]);
assert.ok(result.usage_metadata, 'Usage metadata should be present');
assert.ok(typeof result.usage_metadata.input_tokens === 'number');
assert.ok(typeof result.usage_metadata.output_tokens === 'number');
console.log('Token usage:', result.usage_metadata);
});

it('should handle system messages', async () => {
const model = createModel('MiniMax-M2.7-highspeed', 0.1);
const result = await model.invoke([
new SystemMessage('You are a pirate. Speak in pirate language.'),
new HumanMessage('Say hello'),
]);
assert.ok(result.content.toString().length > 0);
console.log('System msg response:', result.content.toString().substring(0, 100));
});
});

describe('MiniMax Temperature Clamping (standalone)', () => {
it('should clamp temperature 0 to 0.01', () => {
const clamped = Math.min(Math.max(0, 0.01), 1.0);

Check warning on line 61 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH6&open=AZ01TVj_Z7p57OGEbcH6&pullRequest=226
assert.equal(clamped, 0.01);
});

it('should clamp temperature 2.0 to 1.0', () => {
const clamped = Math.min(Math.max(2.0, 0.01), 1.0);

Check warning on line 66 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH7&open=AZ01TVj_Z7p57OGEbcH7&pullRequest=226

Check warning on line 66 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH8&open=AZ01TVj_Z7p57OGEbcH8&pullRequest=226
assert.equal(clamped, 1.0);

Check warning on line 67 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH9&open=AZ01TVj_Z7p57OGEbcH9&pullRequest=226
});

it('should not clamp temperature 0.5', () => {
const clamped = Math.min(Math.max(0.5, 0.01), 1.0);

Check warning on line 71 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH-&open=AZ01TVj_Z7p57OGEbcH-&pullRequest=226
assert.equal(clamped, 0.5);
});

it('should clamp negative temperature to 0.01', () => {
const clamped = Math.min(Math.max(-1, 0.01), 1.0);

Check warning on line 76 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcH_&open=AZ01TVj_Z7p57OGEbcH_&pullRequest=226
assert.equal(clamped, 0.01);
});

it('should handle boundary 1.0', () => {
const clamped = Math.min(Math.max(1.0, 0.01), 1.0);

Check warning on line 81 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcIB&open=AZ01TVj_Z7p57OGEbcIB&pullRequest=226

Check warning on line 81 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcIA&open=AZ01TVj_Z7p57OGEbcIA&pullRequest=226
assert.equal(clamped, 1.0);

Check warning on line 82 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcIC&open=AZ01TVj_Z7p57OGEbcIC&pullRequest=226
});

it('should handle boundary 0.01', () => {
const clamped = Math.min(Math.max(0.01, 0.01), 1.0);

Check warning on line 86 in models/embedjs-minimax/tests/integration.test.mjs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Don't use a zero fraction in the number.

See more on https://sonarcloud.io/project/issues?id=llm-tools_embedJs&issues=AZ01TVj_Z7p57OGEbcID&open=AZ01TVj_Z7p57OGEbcID&pullRequest=226
assert.equal(clamped, 0.01);
});
});

describe('MiniMax Constructor Logic (standalone)', () => {
it('should create ChatOpenAI with MiniMax base URL', () => {
const model = new ChatOpenAI({
apiKey: 'test-key',
model: 'MiniMax-M2.7',
temperature: 0.5,
configuration: {
baseURL: 'https://api.minimax.io/v1',
},
});
assert.ok(model instanceof ChatOpenAI);
});

it('should use MiniMax-M2.7 as default model', () => {
const defaultModel = 'MiniMax-M2.7';
assert.equal(defaultModel, 'MiniMax-M2.7');
});

it('should support MiniMax-M2.7-highspeed model', () => {
const model = new ChatOpenAI({
apiKey: 'test-key',
model: 'MiniMax-M2.7-highspeed',
configuration: {
baseURL: 'https://api.minimax.io/v1',
},
});
assert.ok(model instanceof ChatOpenAI);
});

it('should use MINIMAX_API_KEY from env when not provided', () => {
const key = process.env.MINIMAX_API_KEY ?? 'fallback-key';
assert.ok(typeof key === 'string');
});
});
23 changes: 23 additions & 0 deletions models/embedjs-minimax/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "ES2022.Object"],
"module": "NodeNext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"declaration": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"useDefineForClassFields": true,
"strictPropertyInitialization": false,
"allowJs": false,
"strict": false,
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
}
Loading