Skip to main content

自定义模型网关

🌐 Custom Model Gateways

自定义模型网关允许你通过扩展 MastraModelGateway 基类来实现私有或专用的 LLM 提供商集成。

🌐 Custom model gateways allow you to implement private or specialized LLM provider integrations by extending the MastraModelGateway base class.

概述
Direct link to 概述

🌐 Overview

网关处理访问语言模型的特定提供商逻辑:

🌐 Gateways handle provider-specific logic for accessing language models:

  • 提供商配置与模型发现
  • 身份验证和API密钥管理
  • API端点的URL构建
  • 语言模型实例创建

创建自定义网关以支持:

🌐 Create custom gateways to support:

  • 私有或企业级大型语言模型部署
  • 自定义认证方案
  • 专用路由逻辑
  • 带有唯一ID的网关版本控制

创建自定义网关
Direct link to 创建自定义网关

🌐 Creating a Custom Gateway

扩展 MastraModelGateway 类并实现所需的方法:

🌐 Extend the MastraModelGateway class and implement the required methods:

import { MastraModelGateway, type ProviderConfig } from '@mastra/core/llm';
import { createOpenAICompatible } from '@ai-sdk/openai-compatible-v5';
import type { LanguageModelV2 } from '@ai-sdk/provider-v5';

class MyPrivateGateway extends MastraModelGateway {
// Required: Unique identifier for the gateway
// This ID is used as the prefix for all providers from this gateway
readonly id = 'private';

// Required: Human-readable name
readonly name = 'My Private Gateway';

/**
* Fetch provider configurations from your gateway
* Returns a record of provider configurations
*/
async fetchProviders(): Promise<Record<string, ProviderConfig>> {
return {
'my-provider': {
name: 'My Provider',
models: ['model-1', 'model-2', 'model-3'],
apiKeyEnvVar: 'MY_API_KEY',
gateway: this.id,
url: 'https://api.myprovider.com/v1',
},
};
}

/**
* Build the API URL for a model
* @param modelId - Full model ID (e.g., "private/my-provider/model-1")
* @param envVars - Environment variables (optional)
*/
buildUrl(modelId: string, envVars?: Record<string, string>): string {
return 'https://api.myprovider.com/v1';
}

/**
* Get the API key for authentication
* @param modelId - Full model ID
*/
async getApiKey(modelId: string): Promise<string> {
const apiKey = process.env.MY_API_KEY;
if (!apiKey) {
throw new Error(`Missing MY_API_KEY environment variable`);
}
return apiKey;
}

/**
* Create a language model instance
* @param args - Model ID, provider ID, and API key
*/
async resolveLanguageModel({
modelId,
providerId,
apiKey,
}: {
modelId: string;
providerId: string;
apiKey: string;
}): Promise<LanguageModelV2> {
const baseURL = this.buildUrl(`${providerId}/${modelId}`);

return createOpenAICompatible({
name: providerId,
apiKey,
baseURL,
supportsStructuredOutputs: true,
}).chatModel(modelId);
}
}

注册自定义网关
Direct link to 注册自定义网关

🌐 Registering Custom Gateways

初始化过程中
Direct link to 初始化过程中

🌐 During Initialization

在创建你的 Mastra 实例时,将网关作为记录传递:

🌐 Pass gateways as a record when creating your Mastra instance:

import { Mastra } from '@mastra/core';

const mastra = new Mastra({
gateways: {
myGateway: new MyPrivateGateway(),
anotherGateway: new AnotherGateway(),
},
});

初始化后
Direct link to 初始化后

🌐 After Initialization

使用 addGateway 动态添加网关:

🌐 Add gateways dynamically using addGateway:

const mastra = new Mastra();

// Add with explicit key
mastra.addGateway(new MyPrivateGateway(), 'myGateway');

// Add using gateway's ID
mastra.addGateway(new MyPrivateGateway());
// Stored with key 'my-private-gateway' (the gateway's id)

使用自定义网关
Direct link to 使用自定义网关

🌐 Using Custom Gateways

使用网关 ID 作为前缀,从你的自定义网关引用模型:

🌐 Reference models from your custom gateway using the gateway ID as prefix:

import { Agent } from '@mastra/core/agent';

const agent = new Agent({
id: 'my-agent',
name: 'My Agent',
instructions: 'You are a helpful assistant',
model: 'private/my-provider/model-1', // Uses MyPrivateGateway
});

mastra.addAgent(agent, 'myAgent');

当你创建代理或使用模型时,Mastra 的模型路由会根据模型 ID 自动选择适当的网关。网关 ID 作为前缀。如果没有匹配的自定义网关,它会回退到内置网关。

🌐 When you create an agent or use a model, Mastra's model router automatically selects the appropriate gateway based on the model ID. The gateway ID serves as the prefix. If no custom gateways match, it falls back to the built-in gateways.

TypeScript 自动补齐
Direct link to TypeScript 自动补齐

🌐 TypeScript Autocomplete

开发中的自动类型生成

在开发模式下运行(MASTRA_DEV=true)时,Mastra 会自动为你的自定义网关生成 TypeScript 类型!

🌐 When running in development mode (MASTRA_DEV=true), Mastra automatically generates TypeScript types for your custom gateways!

  1. 设置环境变量

    export MASTRA_DEV=true
  2. 注册你的网关

    const mastra = new Mastra({
    gateways: {
    myGateway: new MyPrivateGateway(),
    },
    });
  3. 类型会自动生成

    • 当你添加网关时,Mastra 会与 GatewayRegistry 同步
    • 注册表从你的自定义网关获取提供程序
    • TypeScript 类型在 ~/.cache/mastra/ 中重新生成
    • 你的 IDE 会在几秒钟内识别新类型
  4. 自动补齐现已可用

    const agent = new Agent({
    model: 'my-gateway-id/my-provider/model-1', // Full autocomplete!
    });

运作方式

GatewayRegistry 执行每小时同步,该同步会:

🌐 The GatewayRegistry runs an hourly sync that:

  • 对所有已注册网关调用 fetchProviders()
  • 生成 TypeScript 类型定义
  • 将它们写入全局缓存和你项目的 dist/ 目录
  • 你的 TypeScript 服务器会自动检测到更改
tip

第一次添加网关时,类型生成可能需要几秒钟。后续的更新会每小时在后台进行。

手动类型生成替代方案
Direct link to 手动类型生成替代方案

🌐 Manual Type Generation Alternatives

如果你没有在开发模式下运行或需要立即更新类型:

🌐 If you're not running in development mode or need immediate type updates:

选项 1:使用类型断言(最简单)

const agent = new Agent({
id: 'my-agent',
name: 'my-agent',
instructions: 'You are a helpful assistant',
model: 'private/my-provider/model-1' as any, // Bypass type checking
});

选项 2:创建自定义类型联合(类型安全)

import type { ModelRouterModelId } from '@mastra/core/llm';

// Define your custom model IDs
type CustomModelId =
| 'private/my-provider/model-1'
| 'private/my-provider/model-2'
| 'private/my-provider/model-3';

// Combine with built-in models
type AllModelIds = ModelRouterModelId | CustomModelId;

const agent = new Agent({
id: 'my-agent',
name: 'my-agent',
instructions: 'You are a helpful assistant',
model: 'private/my-provider/model-1' satisfies AllModelIds,
});

选项 3:在全局范围内扩展 ModelRouterModelId(高级)

// In a types.d.ts file in your project
declare module '@mastra/core/llm' {
interface ProviderModelsMap {
'my-provider': readonly ['model-1', 'model-2', 'model-3'];
}
}

这将内置类型扩展为包含你的自定义模型,为你提供完整的自动补齐支持。

🌐 This extends the built-in type to include your custom models, giving you full autocomplete support.

网关管理
Direct link to 网关管理

🌐 Gateway Management

getGateway(key)
Direct link to getGateway(key)

通过注册密钥检索网关:

🌐 Retrieve a gateway by its registration key:

const gateway = mastra.getGateway('myGateway');
console.log(gateway.name); // 'My Private Gateway'

getGatewayById(id)
Direct link to getGatewayById(id)

通过其唯一 ID 检索网关:

🌐 Retrieve a gateway by its unique ID:

const gateway = mastra.getGatewayById('my-private-gateway');
console.log(gateway.name); // 'My Private Gateway'

这种情况适用时:

🌐 This is useful when:

  • 网关有明确的 ID,与其注册密钥不同
  • 你需要通过 ID 在不同实例中查找网关
  • 支持网关版本控制(例如,'gateway-v1''gateway-v2'

listGateways()
Direct link to listGateways()

获取所有已注册的网关:

🌐 Get all registered gateways:

const gateways = mastra.listGateways();
console.log(Object.keys(gateways)); // ['myGateway', 'anotherGateway']

门户属性
Direct link to 门户属性

🌐 Gateway Properties

必填
Direct link to 必填

🌐 Required

属性类型描述
idstring网关的唯一标识,用作型号字符串的网关前缀
namestring可读的网关名称

方法
Direct link to 方法

🌐 Methods

方法描述
fetchProviders()获取提供商配置
buildUrl(modelId, envVars?)为模型构建 API URL
getApiKey(modelId)获取 API 密钥用于认证
resolveLanguageModel(args)创建语言模型实例
getId()获取网关 ID(返回 idname

提供商配置
Direct link to 提供商配置

🌐 Provider Configuration

fetchProviders() 方法返回一个包含 ProviderConfig 对象的记录:

🌐 The fetchProviders() method returns a record of ProviderConfig objects:

interface ProviderConfig {
name: string; // Display name
models: string[]; // Available model IDs
apiKeyEnvVar: string | string[]; // Environment variable(s) for API key
gateway: string; // Gateway identifier
url?: string; // Optional API base URL
apiKeyHeader?: string; // Optional custom auth header
docUrl?: string; // Optional documentation URL
}

网关 ID 与密钥
Direct link to 网关 ID 与密钥

🌐 Gateway IDs vs Keys

理解区别:

🌐 Understanding the distinction:

  • 密钥:将网关添加到Mastra时使用的注册密钥(记录密钥)
  • ID:网关的唯一标识符(通过 id 属性或未设置时的 name
class VersionedGateway extends MastraModelGateway {
readonly id = 'my-gateway-v2'; // Unique ID for versioning and prefixing
readonly name = 'My Gateway'; // Display name
}

const mastra = new Mastra({
gateways: {
currentGateway: new VersionedGateway(), // Key: 'currentGateway'
},
});

// Retrieve by key
const byKey = mastra.getGateway('currentGateway');

// Retrieve by ID
const byId = mastra.getGatewayById('my-gateway-v2');

// Both return the same gateway
console.log(byKey === byId); // true

型号格式
Direct link to 型号格式

🌐 Model ID Format

通过自定义网关访问的模型遵循以下格式:

🌐 Models accessed through custom gateways follow this format:

[gatewayId]/[provider]/[model]

示例:

🌐 Examples:

  • private/my-provider/model-1

高级示例
Direct link to 高级示例

🌐 Advanced Example

基于令牌的网关,带缓存:

🌐 Token-based gateway with caching:

class TokenGateway extends MastraModelGateway {
readonly id = 'token-gateway-v1';
readonly name = 'Token Gateway';

private tokenCache: Map<string, { token: string; expiresAt: number }> = new Map();

async fetchProviders(): Promise<Record<string, ProviderConfig>> {
const response = await fetch('https://api.gateway.com/providers');
const data = await response.json();

return {
provider: {
name: data.name,
models: data.models,
apiKeyEnvVar: 'GATEWAY_TOKEN',
gateway: this.id,
},
};
}

async buildUrl(modelId: string, envVars?: Record<string, string>): Promise<string> {
const token = await this.getApiKey(modelId);
const siteId = envVars?.SITE_ID || process.env.SITE_ID;

const response = await fetch(`https://api.gateway.com/sites/${siteId}/token`, {
headers: { Authorization: `Bearer ${token}` },
});

const { url } = await response.json();
return url;
}

async getApiKey(modelId: string): Promise<string> {
const cached = this.tokenCache.get(modelId);

if (cached && cached.expiresAt > Date.now()) {
return cached.token;
}

const token = process.env.GATEWAY_TOKEN;
if (!token) {
throw new Error('Missing GATEWAY_TOKEN');
}

// Cache token for 1 hour
this.tokenCache.set(modelId, {
token,
expiresAt: Date.now() + 3600000,
});

return token;
}

async resolveLanguageModel({ modelId, providerId, apiKey }: {
modelId: string;
providerId: string;
apiKey: string;
}): Promise<LanguageModelV2> {
const baseURL = await this.buildUrl(`${providerId}/${modelId}`);

return createOpenAICompatible({
name: providerId,
apiKey,
baseURL,
supportsStructuredOutputs: true,
}).chatModel(modelId);
}
}

错误处理
Direct link to 错误处理

🌐 Error Handling

为常见故障情况提供描述性错误:

🌐 Provide descriptive errors for common failure scenarios:

class RobustGateway extends MastraModelGateway {
// ... properties

async getApiKey(modelId: string): Promise<string> {
const apiKey = process.env.MY_API_KEY;

if (!apiKey) {
throw new Error(
`Missing MY_API_KEY environment variable for model: ${modelId}. ` +
`Please set MY_API_KEY in your environment.`
);
}

return apiKey;
}

async buildUrl(modelId: string, envVars?: Record<string, string>): Promise<string> {
const baseUrl = envVars?.BASE_URL || process.env.BASE_URL;

if (!baseUrl) {
throw new Error(
`No base URL configured for model: ${modelId}. ` +
`Set BASE_URL environment variable or pass it in envVars.`
);
}

return baseUrl;
}
}

测试自定义网关
Direct link to 测试自定义网关

🌐 Testing Custom Gateways

示例测试结构:

🌐 Example test structure:

import { describe, it, expect, beforeEach } from 'vitest';
import { Mastra } from '@mastra/core';

describe('MyPrivateGateway', () => {
beforeEach(() => {
process.env.MY_API_KEY = 'test-key';
});

it('should fetch providers', async () => {
const gateway = new MyPrivateGateway();
const providers = await gateway.fetchProviders();

expect(providers['my-provider']).toBeDefined();
expect(providers['my-provider'].models).toContain('model-1');
});

it('should integrate with Mastra', () => {
const mastra = new Mastra({
gateways: {
private: new MyPrivateGateway(),
},
});

const gateway = mastra.getGateway('private');
expect(gateway.name).toBe('My Private Gateway');
});

it('should resolve models by ID', () => {
const mastra = new Mastra({
gateways: {
key: new MyPrivateGateway(),
},
});

const gateway = mastra.getGatewayById('my-private-gateway');
expect(gateway).toBeDefined();
});
});

最佳实践
Direct link to 最佳实践

🌐 Best Practices

  1. 使用描述性ID进行版本管理:当需要对网关进行版本控制时,设置明确的 id

    readonly id = 'my-gateway-v1';
  2. 实现适当的错误处理:抛出带有可操作信息的描述性错误

  3. 缓存高开销操作:在适当情况下缓存令牌、URL 或提供商配置

  4. 验证环境变量:检查 getApiKeybuildUrl 中所需的环境变量

  5. 记录你的网关:添加 JSDoc 注释,解释网关的用途和配置

  6. 遵循命名规范:为提供商和模型使用清晰、一致的命名

  7. 处理异步操作:使用 async/await 进行网络请求和 I/O 操作

  8. 彻底测试:为所有网关方法编写单元测试

内置网关
Direct link to 内置网关

🌐 Built-in Gateways

Mastra 包含内置网关作为参考实现:

🌐 Mastra includes built-in gateways as reference implementations:

  • Netlify 网关:Netlify AI 网关与令牌交换的集成
  • ModelsDevGateway:来自 models.dev 的 OpenAI 兼容提供商注册表

请参见 NetlifyOpenRouterVercel 了解网关使用示例。

🌐 See Netlify, OpenRouter, and Vercel for examples of gateway usage.