Skip to content

Conversation

@zerob13
Copy link
Owner

@zerob13 zerob13 commented Sep 4, 2025

#17

Summary by CodeRabbit

  • New Features

    • Added configurable model name mapping, allowing custom user-facing model aliases.
    • Introduced MODEL_MAPPING_CONFIG environment variable and -c/--config CLI option to specify a mapping file.
    • Mapped model names now appear in startup logs and API responses.
  • Documentation

    • Added guidance on model mapping configuration, including example JSON and CLI usage.
    • Documented default mapping file (model-mapping.json) and how to override it via environment variable or CLI.

…names

- 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
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 4, 2025

Walkthrough

Adds configurable model-name mapping support. Introduces a mapping config file (default: model-mapping.json), a loader and lookup utilities, CLI option to specify config, startup loading, and usage of mapped names across logs, OpenAI model listing, and Anthropic/Gemini model resolution. Updates docs and adds example mapping files.

Changes

Cohort / File(s) Summary
Docs updates
README.md, README.zh.md
Document MODEL_MAPPING_CONFIG, model-mapping.json usage, examples, and CLI/environment variable options.
Mapping config assets
model-mapping.json, custom-mapping.json
Add default and sample mapping JSONs for mock→actual/custom model identifiers.
Model mapping module
src/config/modelMapping.ts
New in-memory mapping loader and accessors: loadModelMapping, getMappedModelName, getOriginalModelName, getAllMappings, hasMappings. Reads from env MODEL_MAPPING_CONFIG or provided path.
CLI integration
src/cli.ts
Add -c/--config option, load mapping at startup, log config path, display mapped model names in “Available models” and example curl.
Server startup integration
src/index.ts
Load mapping on boot; replace hard-coded display names with mapped names in startup logs and examples.
OpenAI service mapping
src/services/openaiService.ts
Map each returned model’s id via getMappedModelName in ModelsResponse.
Anthropic helpers mapping
src/utils/anthropicHelpers.ts
Normalize lookup by mapping requested modelId before search.
Gemini helpers mapping
src/utils/geminiHelpers.ts
Strip prefix, map cleaned ID, then find model by mapped ID.
Generic helpers resolution
src/utils/helpers.ts
Enhance findModelById to resolve via direct, original-from-mapped, and mapped-from-original lookups.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User
  participant CLI/Server as CLI / Server
  participant Mapping as ModelMapping
  participant Services as OpenAI/Anthropic/Gemini

  rect rgb(236,248,255)
    note over CLI/Server,Mapping: Startup
    CLI/Server->>Mapping: loadModelMapping(configPath | env | default)
    Mapping-->>CLI/Server: mappings loaded (idempotent)
    CLI/Server->>Mapping: getMappedModelName(...) for display
    Mapping-->>CLI/Server: mapped names
  end

  rect rgb(244,240,255)
    note over User,Services: Request handling
    User->>CLI/Server: List/Use model (modelId)
    CLI/Server->>Mapping: getMappedModelName(modelId)
    Mapping-->>CLI/Server: mappedId (or original)
    CLI/Server->>Services: findModelById(mappedId)
    Services-->>CLI/Server: model (if found)
    CLI/Server-->>User: Response (ids reflect mapped names)
  end

  alt Model not found via direct id
    CLI/Server->>Mapping: getOriginalModelName(modelId)
    Mapping-->>CLI/Server: originalId or undefined
    CLI/Server->>Services: fallback lookup using original/mapped
    Services-->>CLI/Server: result or not found
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • feat: mock anthropic api #13 — Introduces anthropicHelpers.ts with findModelById; this PR modifies the same helper to normalize IDs via model-mapping, indicating a direct code-level relation.

Poem

A rabbit maps the names with glee,
From mocky woods to models’ sea.
A hop, a swap—ids align,
Logs now speak in mapped design.
JSON trails and carrots bright,
Config loaded—it’s working right! 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/model-names-conf

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot
Copy link

dosubot bot commented Sep 4, 2025

Related Documentation

Checked 1 published document(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (11)
custom-mapping.json (1)

1-11: Add missing alias for parity with default mapping.

Default mapping includes "mock-gpt-thinking-tag". For consistency and fewer surprises when users switch configs, mirror that alias here.

 {
   "mock-gpt-thinking": "custom-gpt-mini",
+  "mock-gpt-thinking-tag": "custom-gpt-mini",
   "gpt-4-mock": "custom-gpt-pro",
   "mock-gpt-markdown": "custom-gpt-markdown",
   "gpt-4o-image": "custom-dalle",
   "mock-claude-markdown": "custom-claude-pro",
   "gemini-1.5-pro": "custom-gemini-pro",
   "gemini-1.5-flash": "custom-gemini-flash",
   "gemini-pro": "custom-gemini-basic",
   "gemini-pro-vision": "custom-gemini-vision"
 }
README.md (1)

489-517: Document precedence (CLI vs env) and fix minor grammar.

  • Clarify that -c/--config overrides MODEL_MAPPING_CONFIG.
  • Tweak “better user experience” → “a better user experience”.
-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.
+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 a better user experience.
+
+Note:
+- The CLI option `-c/--config` takes precedence over the `MODEL_MAPPING_CONFIG` environment variable.
+- Relative paths are resolved from the server's current working directory.
model-mapping.json (1)

1-12: LGTM; consider staleness note for time-stamped targets.

Mappings look consistent. Some targets (e.g., gemini-2.0-*-exp-2025-01-15) are time-stamped and may go stale; keeping them in an example/default file is fine, but consider a brief README note that these are illustrative and can be customized.

src/config/modelMapping.ts (2)

11-19: Use absolute path in logs for clarity.

Helpful when the server is launched from an unexpected CWD.

-const CONFIG_FILE_PATH = process.env.MODEL_MAPPING_CONFIG || path.join(process.cwd(), 'model-mapping.json');
+const CONFIG_FILE_PATH =
+  process.env.MODEL_MAPPING_CONFIG
+    ? path.resolve(process.env.MODEL_MAPPING_CONFIG)
+    : path.resolve(process.cwd(), 'model-mapping.json');

50-52: Optional: cache reverse lookup for O(1) original-name queries.

getOriginalModelName is O(n). If called frequently, build a reverse index when loading.

src/cli.ts (1)

32-43: Log only when mappings are actually loaded.

If loading fails or file is absent, printing a config path can imply success. Consider conditionally logging mapping count using hasMappings().

-  console.log(`   • Config file: ${options.config}`);
+  // Display config path only when mappings are present
+  try {
+    const { hasMappings } = await import('./config/modelMapping');
+    if (hasMappings()) {
+      console.log(`   • Config file: ${options.config}`);
+    }
+  } catch {
+    // no-op
+  }
src/services/openaiService.ts (1)

846-856: Image model special-case ignores mapped names

If gpt-4o-image is mapped (e.g., to "dall-e-3"), the current check won't match and the special image branch won't run. Import the reverse lookup and compare against the original ID.

Apply this diff within existing imports:

-import { getMappedModelName } from "../config/modelMapping";
+import { getMappedModelName, getOriginalModelName } from "../config/modelMapping";

And update generateImage (outside the changed hunk) like this:

// inside generateImage(...)
const model = request.model || "gpt-4o-image";
const originalModel = getOriginalModelName(model) ?? model;

let imageUrls = mockImageUrls;
if (originalModel === "gpt-4o-image") {
  imageUrls = [ImgData];
}

Also applies to: 851-855

README.zh.md (1)

329-360: Clarify that requests accept both original and mapped model names

Add one sentence so users know inbound requests can use either name.

  服务器将自动加载配置并在控制台输出和 API 响应中显示映射后的模型名称。
+ 同时,请求参数中的 `model` 可使用“原始名称”或“映射后名称”,服务会自动解析。
src/utils/helpers.ts (1)

3-3: Lookup order is solid; consider centralizing to avoid drift

The direct → reverse → forward resolution sequence is correct and future‑proof.

To prevent the Gemini/Anthropic helpers from diverging, extract this resolution into a shared utility (e.g., resolveModelIdToInternal) and reuse it there.

Also applies to: 30-46

src/index.ts (2)

31-41: DRY the model list logging with a small table + loop.

Reduces duplication and guarantees future additions automatically go through getMappedModelName.

   console.log(`\n✨ Available models:`);
   console.log(`   OpenAI Compatible:`);
-  console.log(`   - ${getMappedModelName('mock-gpt-thinking')}: Model supporting thought process`);
-  console.log(`   - ${getMappedModelName('gpt-4-mock')}: Model supporting function calls with tool calls format`);
-  console.log(`   - ${getMappedModelName('mock-gpt-markdown')}: Model outputting standard Markdown`);
-  console.log(`   - ${getMappedModelName('gpt-4o-image')}: Model specifically for image generation`);
+  const openAIModels: Array<[string, string]> = [
+    ['mock-gpt-thinking', 'Model supporting thought process'],
+    ['gpt-4-mock', 'Model supporting function calls with tool calls format'],
+    ['mock-gpt-markdown', 'Model outputting standard Markdown'],
+    ['gpt-4o-image', 'Model specifically for image generation'],
+  ];
+  for (const [id, desc] of openAIModels) {
+    console.log(`   - ${getMappedModelName(id)}: ${desc}`);
+  }
   console.log(`   Anthropic Compatible:`);
-  console.log(`   - ${getMappedModelName('mock-claude-markdown')}: Claude markdown sample model`);
+  const anthropicModels: Array<[string, string]> = [
+    ['mock-claude-markdown', 'Claude markdown sample model'],
+  ];
+  for (const [id, desc] of anthropicModels) {
+    console.log(`   - ${getMappedModelName(id)}: ${desc}`);
+  }
   console.log(`   Gemini Compatible:`);
-  console.log(`   - ${getMappedModelName('gemini-1.5-pro')}: Advanced multimodal AI model`);
-  console.log(`   - ${getMappedModelName('gemini-1.5-flash')}: Fast and efficient model`);
-  console.log(`   - ${getMappedModelName('gemini-pro')}: Versatile model for various tasks`);
-  console.log(`   - ${getMappedModelName('gemini-pro-vision')}: Multimodal model for text and images`);
+  const geminiModels: Array<[string, string]> = [
+    ['gemini-1.5-pro', 'Advanced multimodal AI model'],
+    ['gemini-1.5-flash', 'Fast and efficient model'],
+    ['gemini-pro', 'Versatile model for various tasks'],
+    ['gemini-pro-vision', 'Multimodal model for text and images'],
+  ];
+  for (const [id, desc] of geminiModels) {
+    console.log(`   - ${getMappedModelName(id)}: ${desc}`);
+  }

15-18: Bind to HOST when starting the server (log matches actual bind address).

You log HOST but app.listen doesn’t use it; pass it to avoid confusion in environments using a non-default host.

-app.listen(PORT, () => {
+app.listen(PORT, HOST, () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bb4206a and 5e36c21.

📒 Files selected for processing (11)
  • README.md (1 hunks)
  • README.zh.md (1 hunks)
  • custom-mapping.json (1 hunks)
  • model-mapping.json (1 hunks)
  • src/cli.ts (4 hunks)
  • src/config/modelMapping.ts (1 hunks)
  • src/index.ts (2 hunks)
  • src/services/openaiService.ts (1 hunks)
  • src/utils/anthropicHelpers.ts (2 hunks)
  • src/utils/geminiHelpers.ts (2 hunks)
  • src/utils/helpers.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/services/openaiService.ts (3)
src/types/index.ts (2)
  • ModelsResponse (10-13)
  • Model (3-8)
src/data/mockData.ts (1)
  • mockModels (7-48)
src/config/modelMapping.ts (1)
  • getMappedModelName (46-48)
src/utils/geminiHelpers.ts (2)
src/config/modelMapping.ts (1)
  • getMappedModelName (46-48)
src/data/geminiMockData.ts (1)
  • geminiMockModels (4-33)
src/utils/anthropicHelpers.ts (2)
src/config/modelMapping.ts (1)
  • getMappedModelName (46-48)
src/data/anthropicMockData.ts (1)
  • anthropicMockModels (4-12)
src/cli.ts (1)
src/config/modelMapping.ts (2)
  • loadModelMapping (13-44)
  • getMappedModelName (46-48)
src/index.ts (1)
src/config/modelMapping.ts (2)
  • loadModelMapping (13-44)
  • getMappedModelName (46-48)
src/utils/helpers.ts (2)
src/data/mockData.ts (1)
  • mockModels (7-48)
src/config/modelMapping.ts (2)
  • getOriginalModelName (50-52)
  • getMappedModelName (46-48)
🪛 LanguageTool
README.md

[grammar] ~491-~491: There might be a mistake here.
Context: ... internal model names to external names for better user experience. **Example mode...

(QB_NEW_EN)

🔇 Additional comments (6)
src/cli.ts (2)

21-22: Good addition of --config; ensure README table is updated.

The flag is correctly wired with a sensible default. Sync docs to avoid drift.


56-67: Nice touch mapping displayed model IDs.

Keeps console output aligned with configured aliases.

src/services/openaiService.ts (1)

21-21: Model listing uses mapped IDs — good integration

Returning mapped IDs in /v1/models aligns responses with the configured mapping and remains compatible with downstream lookups.

Also applies to: 28-28

src/index.ts (3)

4-4: LGTM: correct imports from mapping module.


46-46: Usage example correctly reflects mapped names.

Good call using getMappedModelName('gpt-4-mock') so the sample always matches the configured alias.


12-14: Eager load concern is invalid – src/cli.ts never imports src/index.ts, so loadModelMapping(options.config) always runs first and respects the custom config path.

Likely an incorrect or invalid review comment.

Comment on lines +489 to +517
### Model Mapping Configuration

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.

**Example model-mapping.json:**
```json
{
"mock-gpt-thinking": "gpt-4o-mini",
"gpt-4-mock": "gpt-4-turbo",
"mock-gpt-markdown": "gpt-4o",
"gpt-4o-image": "dall-e-3",
"mock-claude-markdown": "claude-3-opus-20240229",
"gemini-1.5-pro": "gemini-2.0-pro-exp-2025-01-15",
"gemini-1.5-flash": "gemini-2.0-flash-exp-2025-01-15",
"gemini-pro": "gemini-pro-1.0",
"gemini-pro-vision": "gemini-pro-vision-1.0"
}
```

**CLI Usage:**
```bash
# Use custom model mapping configuration
npx mock-openai-api -c custom-mapping.json

# Or set via environment variable
MODEL_MAPPING_CONFIG=custom-mapping.json npx mock-openai-api
```

The server will automatically load the configuration and display mapped model names in the console output and API responses.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update the CLI options table to include --config.

The new option exists in src/cli.ts but is missing from the README table; users will miss it.

-| Option             | Short | Description                       | Default   |
-| ------------------ | ----- | --------------------------------- | --------- |
-| `--port <number>`  | `-p`  | Server port                       | `3000`    |
-| `--host <address>` | `-H`  | Server host address               | `0.0.0.0` |
-| `--verbose`        | `-v`  | Enable request logging to console | `false`   |
-| `--version`        |       | Show version number               |           |
-| `--help`           |       | Show help information             |           |
+| Option               | Short | Description                             | Default               |
+| -------------------- | ----- | --------------------------------------- | --------------------- |
+| `--port <number>`    | `-p`  | Server port                             | `3000`                |
+| `--host <address>`   | `-H`  | Server host address                     | `0.0.0.0`             |
+| `--verbose`          | `-v`  | Enable request logging to console       | `false`               |
+| `--config <path>`    | `-c`  | Path to model mapping config file       | `./model-mapping.json`|
+| `--version`          |       | Show version number                     |                       |
+| `--help`             |       | Show help information                   |                       |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Model Mapping Configuration
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.
**Example model-mapping.json:**
```json
{
"mock-gpt-thinking": "gpt-4o-mini",
"gpt-4-mock": "gpt-4-turbo",
"mock-gpt-markdown": "gpt-4o",
"gpt-4o-image": "dall-e-3",
"mock-claude-markdown": "claude-3-opus-20240229",
"gemini-1.5-pro": "gemini-2.0-pro-exp-2025-01-15",
"gemini-1.5-flash": "gemini-2.0-flash-exp-2025-01-15",
"gemini-pro": "gemini-pro-1.0",
"gemini-pro-vision": "gemini-pro-vision-1.0"
}
```
**CLI Usage:**
```bash
# Use custom model mapping configuration
npx mock-openai-api -c custom-mapping.json
# Or set via environment variable
MODEL_MAPPING_CONFIG=custom-mapping.json npx mock-openai-api
```
The server will automatically load the configuration and display mapped model names in the console output and API responses.
| Option | Short | Description | Default |
| -------------------- | ----- | --------------------------------------- | --------------------- |
| `--port <number>` | `-p` | Server port | `3000` |
| `--host <address>` | `-H` | Server host address | `0.0.0.0` |
| `--verbose` | `-v` | Enable request logging to console | `false` |
| `--config <path>` | `-c` | Path to model mapping config file | `./model-mapping.json`|
| `--version` | | Show version number | |
| `--help` | | Show help information | |
🧰 Tools
🪛 LanguageTool

[grammar] ~491-~491: There might be a mistake here.
Context: ... internal model names to external names for better user experience. **Example mode...

(QB_NEW_EN)

🤖 Prompt for AI Agents
In README.md around lines 489 to 517, the CLI options table is missing the new
--config option introduced in src/cli.ts; add a table row describing --config
(and its short form if present), a concise description like "Path to custom
model-mapping JSON file", the default behavior, and an example usage line
showing npx mock-openai-api -c custom-mapping.json (and the equivalent
environment variable usage). Ensure the table formatting matches the existing
CLI options table style and that the example references MODEL_MAPPING_CONFIG as
shown elsewhere.

Comment on lines +13 to +44
export function loadModelMapping(configPath?: string): void {
if (configLoaded) {
return;
}

const configFilePath = configPath || CONFIG_FILE_PATH;

try {
if (fs.existsSync(configFilePath)) {
const configContent = fs.readFileSync(configFilePath, 'utf-8');
const config = JSON.parse(configContent);

if (typeof config === 'object' && config !== null) {
modelMapping = config;
console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);

if (Object.keys(modelMapping).length > 0) {
Object.entries(modelMapping).forEach(([original, mapped]) => {
console.log(` • ${original}${mapped}`);
});
}
}
} else {
console.log(`ℹ️ No model mapping configuration found at ${configFilePath}`);
}
} catch (error) {
console.error(`❌ Failed to load model mapping configuration: ${error}`);
}

configLoaded = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t set configLoaded=true on failure; allows second attempt with another path.

If the first call points to a non-existent or invalid file, configLoaded is still set to true, blocking subsequent loads (e.g., a later call with a valid default path). Move the flag to the successful load path.

 export function loadModelMapping(configPath?: string): void {
   if (configLoaded) {
     return;
   }
 
   const configFilePath = configPath || CONFIG_FILE_PATH;
 
   try {
     if (fs.existsSync(configFilePath)) {
       const configContent = fs.readFileSync(configFilePath, 'utf-8');
       const config = JSON.parse(configContent);
       
-      if (typeof config === 'object' && config !== null) {
-        modelMapping = config;
+      if (typeof config === 'object' && config !== null) {
+        modelMapping = config;
         console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
         console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);
         
         if (Object.keys(modelMapping).length > 0) {
           Object.entries(modelMapping).forEach(([original, mapped]) => {
             console.log(`   • ${original} → ${mapped}`);
           });
         }
+        configLoaded = true;
       }
     } else {
       console.log(`ℹ️  No model mapping configuration found at ${configFilePath}`);
     }
   } catch (error) {
     console.error(`❌ Failed to load model mapping configuration: ${error}`);
   }
-  
-  configLoaded = true;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function loadModelMapping(configPath?: string): void {
if (configLoaded) {
return;
}
const configFilePath = configPath || CONFIG_FILE_PATH;
try {
if (fs.existsSync(configFilePath)) {
const configContent = fs.readFileSync(configFilePath, 'utf-8');
const config = JSON.parse(configContent);
if (typeof config === 'object' && config !== null) {
modelMapping = config;
console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);
if (Object.keys(modelMapping).length > 0) {
Object.entries(modelMapping).forEach(([original, mapped]) => {
console.log(` • ${original}${mapped}`);
});
}
}
} else {
console.log(`ℹ️ No model mapping configuration found at ${configFilePath}`);
}
} catch (error) {
console.error(`❌ Failed to load model mapping configuration: ${error}`);
}
configLoaded = true;
}
export function loadModelMapping(configPath?: string): void {
if (configLoaded) {
return;
}
const configFilePath = configPath || CONFIG_FILE_PATH;
try {
if (fs.existsSync(configFilePath)) {
const configContent = fs.readFileSync(configFilePath, 'utf-8');
const config = JSON.parse(configContent);
if (typeof config === 'object' && config !== null) {
modelMapping = config;
console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);
if (Object.keys(modelMapping).length > 0) {
Object.entries(modelMapping).forEach(([original, mapped]) => {
console.log(` • ${original}${mapped}`);
});
}
configLoaded = true;
}
} else {
console.log(`ℹ️ No model mapping configuration found at ${configFilePath}`);
}
} catch (error) {
console.error(`❌ Failed to load model mapping configuration: ${error}`);
}
}
🤖 Prompt for AI Agents
In src/config/modelMapping.ts around lines 13 to 44, configLoaded is currently
set to true unconditionally at the end of the function which prevents retrying
with another path after a failed or missing config load; move the assignment so
it only runs after a successful parse and assignment of modelMapping (i.e.,
after modelMapping = config and successful logs), and remove the unconditional
configLoaded = true from the function exit so that if the file is missing or
JSON parsing throws, subsequent calls can attempt loading again.

Comment on lines +20 to +36
try {
if (fs.existsSync(configFilePath)) {
const configContent = fs.readFileSync(configFilePath, 'utf-8');
const config = JSON.parse(configContent);

if (typeof config === 'object' && config !== null) {
modelMapping = config;
console.log(`✅ Loaded model mapping configuration from ${configFilePath}`);
console.log(`📋 Model mappings: ${Object.keys(modelMapping).length} mappings configured`);

if (Object.keys(modelMapping).length > 0) {
Object.entries(modelMapping).forEach(([original, mapped]) => {
console.log(` • ${original}${mapped}`);
});
}
}
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate mapping shape; reject/skip non-string values to avoid runtime surprises.

Currently any JSON object is accepted. Guard against arrays and non-string values; log warnings for skipped entries.

-      if (typeof config === 'object' && config !== null) {
-        modelMapping = config;
+      if (typeof config === 'object' && config !== null && !Array.isArray(config)) {
+        const next: ModelMappingConfig = {};
+        for (const [k, v] of Object.entries(config as Record<string, unknown>)) {
+          if (typeof k === 'string' && typeof v === 'string') {
+            next[k] = v;
+          } else {
+            console.warn(`⚠️  Skipping invalid mapping entry: ${String(k)} → ${String(v)}`);
+          }
+        }
+        modelMapping = next;
🤖 Prompt for AI Agents
In src/config/modelMapping.ts around lines 20 to 36, the code currently accepts
any JSON object as modelMapping which can include arrays or non-string values;
update the loader to validate the mapping shape by ensuring the parsed config is
an object whose keys map to string values only, skip or reject any entries where
the mapped value is not a string (or where a value is an array/object), and log
a warning for each skipped entry including the key and the unexpected type; set
modelMapping to the filtered object (or empty if none valid) and keep the
existing success logs but report the count of valid mappings.

import { MockModel } from "../types/index";
import { anthropicMockModels } from "../data/anthropicMockData";
import { ErrorResponse, StreamingEvent } from "../types/anthropic";
import { getMappedModelName } from "../config/modelMapping";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Anthropic model lookup also uses the wrong mapping direction

Same issue as Gemini: external IDs won’t resolve. Switch to reverse lookup first, keep direct and forward as fallbacks.

-import { getMappedModelName } from "../config/modelMapping";
+import { getMappedModelName, getOriginalModelName } from "../config/modelMapping";
@@
 export function findModelById(modelId: string): MockModel | undefined {
-  const mappedModelId = getMappedModelName(modelId);
-  return anthropicMockModels.find(model => model.id === mappedModelId);
+  // 1) direct
+  const direct = anthropicMockModels.find(model => model.id === modelId);
+  if (direct) return direct;
+  // 2) external -> internal
+  const originalId = getOriginalModelName(modelId);
+  if (originalId) return anthropicMockModels.find(model => model.id === originalId);
+  // 3) internal -> external (unlikely to hit)
+  const mappedId = getMappedModelName(modelId);
+  return anthropicMockModels.find(model => model.id === mappedId);
 }

Also applies to: 31-33

🤖 Prompt for AI Agents
In src/utils/anthropicHelpers.ts around lines 4 and 31-33, the model lookup uses
the wrong mapping direction causing external IDs not to resolve; change the
lookup order to attempt a reverse mapping lookup first (map external ID ->
internal name), then fall back to the existing direct lookup, and finally the
forward mapping as last resort, returning the resolved internal model name;
update any helper functions or calls in those lines to perform reverse lookup
first and keep the current direct/forward logic as fallbacks.

@@ -1,4 +1,5 @@
import { geminiMockModels } from '../data/geminiMockData';
import { getMappedModelName } from '../config/modelMapping';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong mapping direction causes lookups to fail for external names

getMappedModelName maps internal->external. For requests that send external (mapped) IDs, this returns the external again, so the lookup misses. Use getOriginalModelName first, with sensible fallbacks.

-import { getMappedModelName } from '../config/modelMapping';
+import { getMappedModelName, getOriginalModelName } from '../config/modelMapping';
@@
 export function findGeminiModelById(modelId: string) {
   // Remove 'models/' prefix if present
   const cleanModelId = modelId.replace('models/', '');
-  const mappedModelId = getMappedModelName(cleanModelId);
-  return geminiMockModels.find(model => model.id === mappedModelId);
+  // 1) direct hit
+  const direct = geminiMockModels.find(model => model.id === cleanModelId);
+  if (direct) return direct;
+  // 2) external -> internal
+  const originalId = getOriginalModelName(cleanModelId);
+  if (originalId) return geminiMockModels.find(model => model.id === originalId);
+  // 3) last resort: internal -> external (unlikely to match, but harmless)
+  const mappedId = getMappedModelName(cleanModelId);
+  return geminiMockModels.find(model => model.id === mappedId);
 }

Also applies to: 33-35

🤖 Prompt for AI Agents
In src/utils/geminiHelpers.ts around lines 2 and also affecting lines 33-35, the
code imports and uses getMappedModelName (which maps internal->external) when
handling incoming external/mapped IDs, causing lookups to fail; change the logic
to call getOriginalModelName first to translate external names back to internal
names (with sensible fallbacks to the incoming value if no mapping exists), and
only use getMappedModelName where you need to produce external names for
outgoing responses; update any conditional branches on lines 33-35 to attempt
getOriginalModelName(inputName) before using getMappedModelName so lookups use
internal model names.

@zerob13 zerob13 merged commit 8a8e94f into master Sep 4, 2025
2 checks passed
@dosubot
Copy link

dosubot bot commented Sep 4, 2025

Documentation updates

Checked 1 published document(s). No updates required.

How did I do? Any feedback?  Join Discord

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants