Skip to content

Commit 8a8e94f

Browse files
authored
feat: add model mapping configuration with custom and official model names (#19)
- Add model-mapping.json for official model name mappings - Add custom-mapping.json for custom model name mappings - Update CLI and service files to support model mapping configuration - Add config directory with modelMapping.ts for type definitions
1 parent bb4206a commit 8a8e94f

File tree

11 files changed

+201
-24
lines changed

11 files changed

+201
-24
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,37 @@ docker run -p 3000:3000 my-mock-openai-api
484484
- `PORT` - Server port (default: 3000)
485485
- `HOST` - Server host (default: 0.0.0.0)
486486
- `VERBOSE` - Enable verbose logging (default: false)
487+
- `MODEL_MAPPING_CONFIG` - Path to model mapping configuration file (default: model-mapping.json)
488+
489+
### Model Mapping Configuration
490+
491+
You can customize the model names displayed to users by creating a `model-mapping.json` file. This allows you to map internal model names to external names for better user experience.
492+
493+
**Example model-mapping.json:**
494+
```json
495+
{
496+
"mock-gpt-thinking": "gpt-4o-mini",
497+
"gpt-4-mock": "gpt-4-turbo",
498+
"mock-gpt-markdown": "gpt-4o",
499+
"gpt-4o-image": "dall-e-3",
500+
"mock-claude-markdown": "claude-3-opus-20240229",
501+
"gemini-1.5-pro": "gemini-2.0-pro-exp-2025-01-15",
502+
"gemini-1.5-flash": "gemini-2.0-flash-exp-2025-01-15",
503+
"gemini-pro": "gemini-pro-1.0",
504+
"gemini-pro-vision": "gemini-pro-vision-1.0"
505+
}
506+
```
507+
508+
**CLI Usage:**
509+
```bash
510+
# Use custom model mapping configuration
511+
npx mock-openai-api -c custom-mapping.json
512+
513+
# Or set via environment variable
514+
MODEL_MAPPING_CONFIG=custom-mapping.json npx mock-openai-api
515+
```
516+
517+
The server will automatically load the configuration and display mapped model names in the console output and API responses.
487518

488519
## 🧪 Testing
489520

README.zh.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,37 @@ docker run -p 3000:3000 mock-openai-api
326326

327327
- `PORT` - 服务器端口(默认:3000)
328328
- `HOST` - 服务器主机(默认:0.0.0.0)
329+
- `MODEL_MAPPING_CONFIG` - 模型映射配置文件路径(默认:model-mapping.json)
330+
331+
### 模型映射配置
332+
333+
您可以通过创建 `model-mapping.json` 文件来自定义显示给用户的模型名称。这允许您将内部模型名称映射到外部名称,以提供更好的用户体验。
334+
335+
**示例 model-mapping.json:**
336+
```json
337+
{
338+
"mock-gpt-thinking": "gpt-4o-mini",
339+
"gpt-4-mock": "gpt-4-turbo",
340+
"mock-gpt-markdown": "gpt-4o",
341+
"gpt-4o-image": "dall-e-3",
342+
"mock-claude-markdown": "claude-3-opus-20240229",
343+
"gemini-1.5-pro": "gemini-2.0-pro-exp-2025-01-15",
344+
"gemini-1.5-flash": "gemini-2.0-flash-exp-2025-01-15",
345+
"gemini-pro": "gemini-pro-1.0",
346+
"gemini-pro-vision": "gemini-pro-vision-1.0"
347+
}
348+
```
349+
350+
**CLI 使用:**
351+
```bash
352+
# 使用自定义模型映射配置
353+
npx mock-openai-api -c custom-mapping.json
354+
355+
# 或通过环境变量设置
356+
MODEL_MAPPING_CONFIG=custom-mapping.json npx mock-openai-api
357+
```
358+
359+
服务器将自动加载配置并在控制台输出和 API 响应中显示映射后的模型名称。
329360

330361
## 🧪 测试
331362

custom-mapping.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"mock-gpt-thinking": "custom-gpt-mini",
3+
"gpt-4-mock": "custom-gpt-pro",
4+
"mock-gpt-markdown": "custom-gpt-markdown",
5+
"gpt-4o-image": "custom-dalle",
6+
"mock-claude-markdown": "custom-claude-pro",
7+
"gemini-1.5-pro": "custom-gemini-pro",
8+
"gemini-1.5-flash": "custom-gemini-flash",
9+
"gemini-pro": "custom-gemini-basic",
10+
"gemini-pro-vision": "custom-gemini-vision"
11+
}

model-mapping.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"mock-gpt-thinking": "gpt-4o-mini",
3+
"mock-gpt-thinking-tag": "gpt-4o-mini",
4+
"gpt-4-mock": "gpt-4-turbo",
5+
"mock-gpt-markdown": "gpt-4o",
6+
"gpt-4o-image": "dall-e-3",
7+
"mock-claude-markdown": "claude-3-opus-20240229",
8+
"gemini-1.5-pro": "gemini-2.0-pro-exp-2025-01-15",
9+
"gemini-1.5-flash": "gemini-2.0-flash-exp-2025-01-15",
10+
"gemini-pro": "gemini-pro-1.0",
11+
"gemini-pro-vision": "gemini-pro-vision-1.0"
12+
}

src/cli.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Command } from 'commander';
44
import app from './app';
55
import { version } from '../package.json'
6+
import { loadModelMapping, getMappedModelName } from './config/modelMapping';
67
// 扩展全局对象类型
78
declare global {
89
var verboseLogging: boolean;
@@ -17,6 +18,7 @@ program
1718
.option('-p, --port <number>', 'Server port', '3000')
1819
.option('-H, --host <address>', 'Server host address', '0.0.0.0')
1920
.option('-v, --verbose', 'Enable request logging to console', false)
21+
.option('-c, --config <path>', 'Path to model mapping config file', './model-mapping.json')
2022
.parse();
2123

2224
const options = program.opts();
@@ -27,13 +29,17 @@ const HOST = options.host || '0.0.0.0';
2729
// 设置全局变量控制日志输出
2830
global.verboseLogging = options.verbose;
2931

32+
// Load model mapping configuration
33+
loadModelMapping(options.config);
34+
3035
app.listen(PORT, HOST, () => {
3136
console.log(`🚀 Mock OpenAI API server started successfully!`);
3237
console.log(`📍 Server address: http://${HOST}:${PORT}`);
3338
console.log(`⚙️ Configuration:`);
3439
console.log(` • Port: ${PORT}`);
3540
console.log(` • Host: ${HOST}`);
3641
console.log(` • Verbose logging: ${options.verbose ? 'ENABLED' : 'DISABLED'}`);
42+
console.log(` • Config file: ${options.config}`);
3743
console.log(` • Version: ${version}`);
3844
console.log(`📖 API Documentation:`);
3945
console.log(` • GET /health - Health check`);
@@ -47,27 +53,28 @@ app.listen(PORT, HOST, () => {
4753
console.log(` • POST /v1beta/models/{model}:streamGenerateContent - Gemini streaming generation`);
4854
console.log(`\n✨ Available models:`);
4955
console.log(` OpenAI Compatible:`);
50-
console.log(` - mock-gpt-thinking: Model supporting thought process`);
51-
console.log(` - gpt-4-mock: Model supporting function calls`);
52-
console.log(` - mock-gpt-markdown: Model outputting standard Markdown`);
53-
console.log(` - gpt-4o-image: Model specifically for image generation`);
56+
console.log(` - ${getMappedModelName('mock-gpt-thinking')}: Model supporting thought process`);
57+
console.log(` - ${getMappedModelName('gpt-4-mock')}: Model supporting function calls`);
58+
console.log(` - ${getMappedModelName('mock-gpt-markdown')}: Model outputting standard Markdown`);
59+
console.log(` - ${getMappedModelName('gpt-4o-image')}: Model specifically for image generation`);
5460
console.log(` Anthropic Compatible:`);
55-
console.log(` - mock-claude-markdown: Claude markdown sample model`);
61+
console.log(` - ${getMappedModelName('mock-claude-markdown')}: Claude markdown sample model`);
5662
console.log(` Gemini Compatible:`);
57-
console.log(` - gemini-1.5-pro: Advanced multimodal AI model`);
58-
console.log(` - gemini-1.5-flash: Fast and efficient model`);
59-
console.log(` - gemini-pro: Versatile model for various tasks`);
60-
console.log(` - gemini-pro-vision: Multimodal model for text and images`);
63+
console.log(` - ${getMappedModelName('gemini-1.5-pro')}: Advanced multimodal AI model`);
64+
console.log(` - ${getMappedModelName('gemini-1.5-flash')}: Fast and efficient model`);
65+
console.log(` - ${getMappedModelName('gemini-pro')}: Versatile model for various tasks`);
66+
console.log(` - ${getMappedModelName('gemini-pro-vision')}: Multimodal model for text and images`);
6167
console.log(`\n🔗 Usage example:`);
6268
console.log(` curl -X POST http://localhost:${PORT}/v1/chat/completions \\`);
6369
console.log(` -H "Content-Type: application/json" \\`);
6470
console.log(` -d '{`);
65-
console.log(` "model": "gpt-4-mock",`);
71+
console.log(` "model": "${getMappedModelName('gpt-4-mock')}",`);
6672
console.log(` "messages": [{"role": "user", "content": "Hello"}]`);
6773
console.log(` }'`);
6874
console.log(`\n💡 CLI Options:`);
6975
console.log(` • Use --help to see all available options`);
7076
console.log(` • Use -v or --verbose to enable request logging`);
7177
console.log(` • Use -p <port> to specify custom port`);
7278
console.log(` • Use -H <host> to specify custom host address`);
79+
console.log(` • Use -c <path> to specify custom config file`);
7380
});

src/config/modelMapping.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
interface ModelMappingConfig {
5+
[originalModel: string]: string;
6+
}
7+
8+
let modelMapping: ModelMappingConfig = {};
9+
let configLoaded = false;
10+
11+
const CONFIG_FILE_PATH = process.env.MODEL_MAPPING_CONFIG || path.join(process.cwd(), 'model-mapping.json');
12+
13+
export function loadModelMapping(configPath?: string): void {
14+
if (configLoaded) {
15+
return;
16+
}
17+
18+
const configFilePath = configPath || CONFIG_FILE_PATH;
19+
20+
try {
21+
if (fs.existsSync(configFilePath)) {
22+
const configContent = fs.readFileSync(configFilePath, 'utf-8');
23+
const config = JSON.parse(configContent);
24+
25+
if (typeof config === 'object' && config !== null) {
26+
modelMapping = config;
27+
console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
28+
console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);
29+
30+
if (Object.keys(modelMapping).length > 0) {
31+
Object.entries(modelMapping).forEach(([original, mapped]) => {
32+
console.log(` • ${original}${mapped}`);
33+
});
34+
}
35+
}
36+
} else {
37+
console.log(`ℹ️ No model mapping configuration found at ${configFilePath}`);
38+
}
39+
} catch (error) {
40+
console.error(`❌ Failed to load model mapping configuration: ${error}`);
41+
}
42+
43+
configLoaded = true;
44+
}
45+
46+
export function getMappedModelName(originalModel: string): string {
47+
return modelMapping[originalModel] || originalModel;
48+
}
49+
50+
export function getOriginalModelName(mappedModel: string): string | undefined {
51+
return Object.keys(modelMapping).find(key => modelMapping[key] === mappedModel);
52+
}
53+
54+
export function getAllMappings(): ModelMappingConfig {
55+
return { ...modelMapping };
56+
}
57+
58+
export function hasMappings(): boolean {
59+
return Object.keys(modelMapping).length > 0;
60+
}

src/index.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
#!/usr/bin/env node
22

33
import app from './app';
4+
import { loadModelMapping, getMappedModelName } from './config/modelMapping';
45

56
const PORT = process.env.PORT || 3000;
67
const HOST = process.env.HOST || '0.0.0.0';
78

89
// Enable verbose logging by default in development or when VERBOSE is set
910
global.verboseLogging = process.env.NODE_ENV !== 'production' || process.env.VERBOSE === 'true';
1011

12+
// Load model mapping configuration
13+
loadModelMapping();
14+
1115
app.listen(PORT, () => {
1216
console.log(`🚀 Mock OpenAI API server started successfully!`);
1317
console.log(`📍 Server address: http://${HOST}:${PORT}`);
@@ -24,22 +28,22 @@ app.listen(PORT, () => {
2428
console.log(` • POST /v1beta/models/{model}:streamGenerateContent - Gemini streaming generation`);
2529
console.log(`\n✨ Available models:`);
2630
console.log(` OpenAI Compatible:`);
27-
console.log(` - mock-gpt-thinking: Model supporting thought process`);
28-
console.log(` - gpt-4-mock: Model supporting function calls with tool calls format`);
29-
console.log(` - mock-gpt-markdown: Model outputting standard Markdown`);
30-
console.log(` - gpt-4o-image: Model specifically for image generation`);
31+
console.log(` - ${getMappedModelName('mock-gpt-thinking')}: Model supporting thought process`);
32+
console.log(` - ${getMappedModelName('gpt-4-mock')}: Model supporting function calls with tool calls format`);
33+
console.log(` - ${getMappedModelName('mock-gpt-markdown')}: Model outputting standard Markdown`);
34+
console.log(` - ${getMappedModelName('gpt-4o-image')}: Model specifically for image generation`);
3135
console.log(` Anthropic Compatible:`);
32-
console.log(` - mock-claude-markdown: Claude markdown sample model`);
36+
console.log(` - ${getMappedModelName('mock-claude-markdown')}: Claude markdown sample model`);
3337
console.log(` Gemini Compatible:`);
34-
console.log(` - gemini-1.5-pro: Advanced multimodal AI model`);
35-
console.log(` - gemini-1.5-flash: Fast and efficient model`);
36-
console.log(` - gemini-pro: Versatile model for various tasks`);
37-
console.log(` - gemini-pro-vision: Multimodal model for text and images`);
38+
console.log(` - ${getMappedModelName('gemini-1.5-pro')}: Advanced multimodal AI model`);
39+
console.log(` - ${getMappedModelName('gemini-1.5-flash')}: Fast and efficient model`);
40+
console.log(` - ${getMappedModelName('gemini-pro')}: Versatile model for various tasks`);
41+
console.log(` - ${getMappedModelName('gemini-pro-vision')}: Multimodal model for text and images`);
3842
console.log(`\n🔗 Usage example:`);
3943
console.log(` curl -X POST http://localhost:${PORT}/v1/chat/completions \\`);
4044
console.log(` -H "Content-Type: application/json" \\`);
4145
console.log(` -d '{`);
42-
console.log(` "model": "gpt-4-mock",`);
46+
console.log(` "model": "${getMappedModelName('gpt-4-mock')}",`);
4347
console.log(` "messages": [{"role": "user", "content": "Hello"}]`);
4448
console.log(` }'`);
4549
console.log(`\n💡 Use CLI for more options: npm run build && npx mock-openai-api --help`);

src/services/openaiService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ import {
1818
randomChoice,
1919
formatErrorResponse,
2020
} from "../utils/helpers";
21+
import { getMappedModelName } from "../config/modelMapping";
2122
import { ImgData } from "../data/base64Img";
2223
/**
2324
* Get model list
2425
*/
2526
export function getModels(): ModelsResponse {
2627
const models: Model[] = mockModels.map((mockModel) => ({
27-
id: mockModel.id,
28+
id: getMappedModelName(mockModel.id),
2829
object: "model",
2930
created: getCurrentTimestamp(),
3031
owned_by: "mock-openai",

src/utils/anthropicHelpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { MockModel } from "../types/index";
22
import { anthropicMockModels } from "../data/anthropicMockData";
33
import { ErrorResponse, StreamingEvent } from "../types/anthropic";
4+
import { getMappedModelName } from "../config/modelMapping";
45

56
/**
67
* Get current timestamp
@@ -27,7 +28,8 @@ export function calculateTokens(text: string): number {
2728
* Find model by ID
2829
*/
2930
export function findModelById(modelId: string): MockModel | undefined {
30-
return anthropicMockModels.find(model => model.id === modelId);
31+
const mappedModelId = getMappedModelName(modelId);
32+
return anthropicMockModels.find(model => model.id === mappedModelId);
3133
}
3234

3335
/**

src/utils/geminiHelpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { geminiMockModels } from '../data/geminiMockData';
2+
import { getMappedModelName } from '../config/modelMapping';
23

34
/**
45
* Get current timestamp
@@ -29,7 +30,8 @@ export function generateModelName(): string {
2930
export function findGeminiModelById(modelId: string) {
3031
// Remove 'models/' prefix if present
3132
const cleanModelId = modelId.replace('models/', '');
32-
return geminiMockModels.find(model => model.id === cleanModelId);
33+
const mappedModelId = getMappedModelName(cleanModelId);
34+
return geminiMockModels.find(model => model.id === mappedModelId);
3335
}
3436

3537
/**

0 commit comments

Comments
 (0)