Skip to content

AI Gateway model IDs containing @ are misparsed from modelKey #3453

Description

@MicroMilo

Summary

AI Gateway custom model IDs are accepted as strings, but later serialized into an unescaped @-delimited modelKey format:

aiGateway@<modelId>@teable

If the model ID itself contains @, for example custom/image-model@beta, the serialized key becomes:

aiGateway@custom/image-model@beta@teable

Current parsers split on @ and interpret this as model=custom/image-model and name=beta, so the original model ID is truncated and the teable provider suffix is lost.

Steps to reproduce

  1. Configure AI Gateway.
  2. Add a custom Gateway model ID containing @, for example:
custom/image-model@beta
  1. Let Teable serialize it into a model key:
aiGateway@custom/image-model@beta@teable
  1. Parse or select this model through the AI model selection / image model config path.

Expected behavior

The Gateway model ID should round-trip as:

custom/image-model@beta

The provider suffix should still be recognized as:

teable

The key should still be treated as an AI Gateway model.

Actual behavior

The key is parsed as:

{
  type: 'aiGateway',
  model: 'custom/image-model',
  name: 'beta'
}

As a result:

  • frontend Gateway recognition can fail because name !== 'teable'
  • image model metadata lookup uses the truncated ID custom/image-model
  • matching against the stored Gateway model ID custom/image-model@beta fails
  • backend model config / test paths can send the truncated model ID to the Gateway provider

Evidence

Relevant code anchors:

  • gatewayModelSchema defines id: z.string() and has no delimiter restriction.
  • The UI path can forward custom search text as the model ID.
  • The save path checks that id and label are present before saving.
  • The frontend serializes Gateway models as aiGateway@${model.id}@teable.
  • Frontend and backend parsers use modelKey.split('@').
  • Backend model config / test paths use the parsed model as the Gateway model ID.
  • Image model config also extracts the image model ID with modelKey.split('@') and then matches it against gatewayModels[].id.

Observed minimal repro result:

{
  "modelId": "custom/image-model@beta",
  "modelKey": "aiGateway@custom/image-model@beta@teable",
  "parsed": {
    "type": "aiGateway",
    "model": "custom/image-model",
    "name": "beta"
  },
  "frontendIsGatewayModelKey": false,
  "imageModelId": "custom/image-model",
  "matchedGatewayModel": false
}

Suggested fix

Either:

  1. Reject @ in Gateway model IDs at schema/UI/save time and show a validation message before saving, or
  2. Change modelKey encoding to escape or structurally encode the model ID, then update all parsers consistently.

If preserving the current key format is preferred, the smaller fix is to validate Gateway model IDs and add regression tests for delimiter-bearing IDs.

Suggested tests:

  • frontend parseModelKey / isGatewayModelKey regression
  • backend parseModelKey or model test selection regression
  • image model config lookup regression
  • schema/UI validation test if @ is intentionally disallowed

Additional context / Related coverage

A current GitHub search did not identify exact existing GitHub issue/PR coverage for terms such as aiGateway parseModelKey @ model id, gatewayModels modelKey split @, and parseModelKey aiGateway.

A live end-to-end Gateway call was not run because it requires a configured Teable stack and AI Gateway key. The issue is reproducible from the serialization/parsing contract and local parser behavior.


Submitted with Codex.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions