UNPKG

@mastra/core

Version:
1 lines 64.1 kB
{"version":3,"file":"provider-registry-Bv8eMxuW.cjs","names":["MastraModelGateway","MastraError","createAnthropic","MASTRA_USER_AGENT","GATEWAY_AUTH_HEADER","createOpenRouter","staticRegistryJson","shouldEnableGateway","getGatewayId","ModelsDevGateway","NetlifyGateway","getCapabilityFileName","shouldWriteToSrc"],"sources":["../src/llm/model/gateways/mastra.ts","../src/llm/model/provider-registry.json","../src/llm/model/provider-registry.ts"],"sourcesContent":["import { createAnthropic } from '@ai-sdk/anthropic-v6';\nimport { createOpenRouter } from '@openrouter/ai-sdk-provider-v6';\nimport { MastraError } from '../../../error/index.js';\nimport { PROVIDER_REGISTRY } from '../provider-registry.js';\nimport { MastraModelGateway } from './base.js';\nimport type { ProviderConfig, GatewayLanguageModel } from './base.js';\nimport { GATEWAY_AUTH_HEADER, MASTRA_USER_AGENT } from './constants.js';\n\nexport interface MastraGatewayConfig {\n apiKey?: string;\n baseUrl?: string;\n customFetch?: typeof globalThis.fetch;\n}\n\nexport class MastraGateway extends MastraModelGateway {\n readonly id = 'mastra';\n readonly name = 'Gateway';\n\n constructor(private config?: MastraGatewayConfig) {\n super();\n }\n\n private getBaseUrl(): string {\n const raw = this.config?.baseUrl ?? process.env['MASTRA_GATEWAY_URL'] ?? 'https://gateway-api.mastra.ai';\n return raw.replace(/\\/+$/, '').replace(/\\/v1$/, '');\n }\n\n override shouldEnable(): boolean {\n return !!(this.config?.apiKey ?? process.env['MASTRA_GATEWAY_API_KEY']);\n }\n\n async fetchProviders(): Promise<Record<string, ProviderConfig>> {\n if (!this.shouldEnable()) {\n return {};\n }\n\n const openrouterConfig = PROVIDER_REGISTRY['openrouter'];\n const models = openrouterConfig?.models ?? [];\n\n const providers = {\n mastra: {\n apiKeyEnvVar: 'MASTRA_GATEWAY_API_KEY',\n apiKeyHeader: 'Authorization',\n name: 'Gateway',\n gateway: 'mastra',\n models: [...models],\n docUrl: 'https://mastra.ai/docs/gateway',\n },\n };\n\n return providers;\n }\n\n async buildUrl(_modelId: string): Promise<string> {\n return `${this.getBaseUrl()}/v1`;\n }\n\n async getApiKey(): Promise<string> {\n const apiKey = this.config?.apiKey ?? process.env['MASTRA_GATEWAY_API_KEY'];\n if (!apiKey) {\n throw new MastraError({\n id: 'MASTRA_GATEWAY_NO_API_KEY',\n domain: 'LLM',\n category: 'UNKNOWN',\n text: 'Missing MASTRA_GATEWAY_API_KEY environment variable',\n });\n }\n return apiKey;\n }\n\n resolveLanguageModel({\n modelId,\n providerId,\n apiKey,\n headers,\n }: {\n modelId: string;\n providerId: string;\n apiKey: string;\n headers?: Record<string, string>;\n }): GatewayLanguageModel {\n const baseURL = `${this.getBaseUrl()}/v1`;\n const fullModelId = `${providerId}/${modelId}`;\n\n if (this.config?.customFetch && providerId === 'anthropic') {\n // Anthropic OAuth path: use native Anthropic SDK (sends /messages, not /chat/completions)\n return createAnthropic({\n apiKey: 'oauth-gateway-placeholder',\n baseURL,\n headers: {\n 'User-Agent': MASTRA_USER_AGENT,\n [GATEWAY_AUTH_HEADER]: `Bearer ${apiKey}`,\n ...headers,\n },\n fetch: this.config.customFetch as any,\n })(modelId) as unknown as GatewayLanguageModel;\n }\n\n if (this.config?.customFetch) {\n // Non-Anthropic OAuth path: gateway key in GATEWAY_AUTH_HEADER, customFetch owns Authorization\n return createOpenRouter({\n apiKey: 'oauth-gateway-placeholder',\n baseURL,\n headers: {\n 'User-Agent': MASTRA_USER_AGENT,\n [GATEWAY_AUTH_HEADER]: `Bearer ${apiKey}`,\n ...headers,\n },\n fetch: this.config.customFetch,\n }).chat(fullModelId) as unknown as GatewayLanguageModel;\n }\n\n // API key path: gateway key goes via Authorization (standard flow)\n return createOpenRouter({\n apiKey,\n baseURL,\n headers: {\n 'User-Agent': MASTRA_USER_AGENT,\n ...headers,\n },\n }).chat(fullModelId) as unknown as GatewayLanguageModel;\n }\n}\n","","/**\n * Runtime provider registry loader\n * Loads provider data from JSON file and exports typed interfaces\n */\n\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { getCapabilityFileName } from './capability-file.js';\nimport type { ProviderConfig, MastraModelGatewayInterface } from './gateways/base.js';\nimport { getGatewayId, shouldEnableGateway } from './gateways/gateway-helpers.js';\nimport { MastraGateway } from './gateways/mastra.js';\nimport { ModelsDevGateway } from './gateways/models-dev.js';\nimport { NetlifyGateway } from './gateways/netlify.js';\nimport staticRegistryJson from './provider-registry.json';\nimport type { Provider, ModelForProvider, ModelRouterModelId, ProviderModels } from './provider-types.generated.js';\n\n// Re-export types for convenience\nexport type { Provider, ModelForProvider, ModelRouterModelId, ProviderModels };\nexport type { AttachmentCapabilities } from './gateways/base.js';\n\ninterface RegistryData {\n providers: Record<string, ProviderConfig>;\n models: Record<string, string[]>;\n version: string;\n}\n\n// JSON imports widen string literals to `string`, so fields like\n// `modelOverrides[*].shape` don't match their literal-union types.\nconst staticRegistry = staticRegistryJson as RegistryData;\n\n/**\n * Check if running in offline/air-gapped mode.\n * When MASTRA_OFFLINE is set to 'true' or '1', all network fetches for provider data are skipped.\n */\nexport function isOfflineMode(): boolean {\n const value = process.env.MASTRA_OFFLINE;\n return value === 'true' || value === '1';\n}\n\nfunction getEnabledGatewayIds(gateways: MastraModelGatewayInterface[]): Set<string> {\n const enabledGatewayIds = new Set<string>();\n\n for (const gateway of gateways) {\n const enabled = shouldEnableGateway(gateway);\n if (enabled) {\n enabledGatewayIds.add(getGatewayId(gateway));\n }\n }\n\n return enabledGatewayIds;\n}\n\nfunction sanitizeRegistryDataForRuntime(data: RegistryData, enabledGatewayIds: Set<string>): RegistryData {\n const providers = Object.fromEntries(\n Object.entries(data.providers).filter(([, config]) => enabledGatewayIds.has(config.gateway)),\n );\n\n const models = Object.fromEntries(Object.entries(data.models).filter(([providerId]) => providerId in providers));\n\n return {\n ...data,\n providers,\n models,\n };\n}\n\n// In-memory cache for dynamic loading mode\nlet registryData: RegistryData | null = null;\n\n// Cache file helpers (dev mode only)\n// Use functions so we don't call os.homedir() at top level, which\n// causes an error in sandboxed environments when you merely\n// import @mastra/core. In those sandboxes, if you just don't use these\n// functions then you don't hit these errors.\nconst CACHE_DIR = () => path.join(os.homedir(), '.cache', 'mastra');\nconst CACHE_FILE = () => path.join(CACHE_DIR(), 'gateway-refresh-time');\nconst GLOBAL_PROVIDER_REGISTRY_JSON = () => path.join(CACHE_DIR(), 'provider-registry.json');\nconst GLOBAL_PROVIDER_TYPES_DTS = () => path.join(CACHE_DIR(), 'provider-types.generated.d.ts');\nconst GLOBAL_CAPABILITIES_DIR = () => path.join(CACHE_DIR(), 'capabilities');\n\nlet modelRouterCacheFailed = false;\n\n/**\n * Write a file atomically using the write-to-temp-then-rename pattern (synchronous version).\n * This prevents file corruption when multiple processes write to the same file concurrently.\n *\n * @param filePath - The target file path\n * @param content - The content to write\n * @param encoding - The encoding to use (default: 'utf-8')\n */\nfunction atomicWriteFileSync(filePath: string, content: string, encoding: BufferEncoding = 'utf-8'): void {\n // Use random suffix to avoid collisions between concurrent writes\n const randomSuffix = Math.random().toString(36).substring(2, 15);\n const tempPath = `${filePath}.${process.pid}.${Date.now()}.${randomSuffix}.tmp`;\n\n try {\n fs.writeFileSync(tempPath, content, encoding);\n fs.renameSync(tempPath, filePath);\n } catch (error) {\n try {\n fs.unlinkSync(tempPath);\n } catch {\n // Ignore cleanup errors\n }\n throw error;\n }\n}\n\n/**\n * Syncs provider files from global cache to local dist/ directory if needed.\n * Compares file contents to determine if copy is necessary.\n * Validates JSON before copying to prevent propagating corrupted files.\n */\nfunction syncGlobalCacheToLocal(): void {\n try {\n // Check if global cache files exist\n const globalJsonExists = fs.existsSync(GLOBAL_PROVIDER_REGISTRY_JSON());\n const globalDtsExists = fs.existsSync(GLOBAL_PROVIDER_TYPES_DTS());\n\n if (!globalJsonExists && !globalDtsExists) {\n // No global cache, nothing to sync\n return;\n }\n\n // Use getPackageRoot() to find the correct location in node_modules or local dev\n const packageRoot = getPackageRoot();\n const localJsonPath = path.join(packageRoot, 'dist', 'provider-registry.json');\n const localDtsPath = path.join(packageRoot, 'dist', 'llm', 'model', 'provider-types.generated.d.ts');\n\n // Ensure local dist directory exists\n fs.mkdirSync(path.dirname(localJsonPath), { recursive: true });\n fs.mkdirSync(path.dirname(localDtsPath), { recursive: true });\n\n // Sync JSON file if global exists and differs from local\n if (globalJsonExists) {\n const globalJsonContent = fs.readFileSync(GLOBAL_PROVIDER_REGISTRY_JSON(), 'utf-8');\n\n // Validate JSON before copying to prevent propagating corrupted files.\n // Silently delete on corruption — the next gateway sync will rewrite a\n // valid file, so logging here just creates noise when an older mastra\n // version (without the digit-quoting fix) shares the global cache.\n try {\n JSON.parse(globalJsonContent);\n } catch {\n try {\n fs.unlinkSync(GLOBAL_PROVIDER_REGISTRY_JSON());\n } catch {\n // Ignore deletion errors\n }\n return;\n }\n\n let shouldCopyJson = true;\n\n if (fs.existsSync(localJsonPath)) {\n const localJsonContent = fs.readFileSync(localJsonPath, 'utf-8');\n shouldCopyJson = globalJsonContent !== localJsonContent;\n }\n\n if (shouldCopyJson) {\n // Use atomic write to prevent corruption from concurrent writes\n atomicWriteFileSync(localJsonPath, globalJsonContent, 'utf-8');\n }\n }\n\n // Capabilities are loaded lazily per-provider by loadProviderAttachmentModels().\n // The global cache dir is included in findCapabilitiesDirs() so no bulk sync is needed.\n\n // Sync .d.ts file if global exists and differs from local\n if (globalDtsExists) {\n const globalDtsContent = fs.readFileSync(GLOBAL_PROVIDER_TYPES_DTS(), 'utf-8');\n\n // Validate .d.ts content: check for unquoted provider names that start with a digit\n // (e.g. \"readonly 302ai:\" instead of \"readonly '302ai':\"), which produces invalid TypeScript.\n // This can happen if the global cache was written by an older version without the quoting fix.\n // Silently delete on corruption — the next gateway sync will rewrite a valid file.\n if (/readonly\\s+\\d/.test(globalDtsContent)) {\n try {\n fs.unlinkSync(GLOBAL_PROVIDER_TYPES_DTS());\n } catch {\n // Ignore deletion errors\n }\n // Don't sync corrupted .d.ts file; fall through to keep existing local file\n } else {\n let shouldCopyDts = true;\n\n if (fs.existsSync(localDtsPath)) {\n const localDtsContent = fs.readFileSync(localDtsPath, 'utf-8');\n shouldCopyDts = globalDtsContent !== localDtsContent;\n }\n\n if (shouldCopyDts) {\n // Use atomic write to prevent corruption from concurrent writes\n atomicWriteFileSync(localDtsPath, globalDtsContent, 'utf-8');\n }\n }\n }\n } catch {\n // Silent fail - fall back to existing files. Sync errors are recoverable\n // on the next call and don't need to be surfaced to users.\n }\n}\n\nfunction getLastRefreshTimeFromDisk(): Date | null {\n try {\n if (!fs.existsSync(CACHE_FILE())) {\n return null;\n }\n const timestamp = fs.readFileSync(CACHE_FILE(), 'utf-8').trim();\n return new Date(parseInt(timestamp, 10));\n } catch (err) {\n console.warn('[GatewayRegistry] Failed to read cache file:', err);\n modelRouterCacheFailed = true;\n return null;\n }\n}\n\nfunction saveLastRefreshTimeToDisk(date: Date): void {\n try {\n if (!fs.existsSync(CACHE_DIR())) {\n fs.mkdirSync(CACHE_DIR(), { recursive: true });\n }\n fs.writeFileSync(CACHE_FILE(), date.getTime().toString(), 'utf-8');\n } catch (err) {\n modelRouterCacheFailed = true;\n console.warn('[GatewayRegistry] Failed to write cache file:', err);\n }\n}\n\nfunction getPackageRoot(): string {\n try {\n // Use require.resolve to find the package root reliably\n const require = createRequire(import.meta.url || 'file://');\n const packageJsonPath = require.resolve('@mastra/core/package.json');\n return path.dirname(packageJsonPath);\n } catch {\n // Fallback to cwd if we can't resolve the package\n return process.cwd();\n }\n}\n\nfunction loadRegistry(useDynamicLoading: boolean, customGateways: MastraModelGatewayInterface[] = []): RegistryData {\n const enabledGatewayIds = getEnabledGatewayIds([\n new ModelsDevGateway({}),\n new NetlifyGateway(),\n new MastraGateway(),\n ...customGateways,\n ]);\n\n // Production: use static import (bundled at build time)\n if (!useDynamicLoading) {\n return sanitizeRegistryDataForRuntime(staticRegistry, enabledGatewayIds);\n }\n\n // Dynamic loading mode: sync global cache to local before loading\n syncGlobalCacheToLocal();\n\n // Dynamic loading mode: check in-memory cache first\n if (registryData) {\n return registryData;\n }\n\n // Dynamic loading mode: load from file system for live updates\n const packageRoot = getPackageRoot();\n const possiblePaths: string[] = [\n // Built: in dist/ relative to package root (first priority - what gets distributed)\n path.join(packageRoot, 'dist', 'provider-registry.json'),\n // Development: in src/ relative to package root\n path.join(packageRoot, 'src', 'llm', 'model', 'provider-registry.json'),\n // Fallback: relative to cwd (for monorepo setups)\n path.join(process.cwd(), 'packages/core/src/llm/model/provider-registry.json'),\n path.join(process.cwd(), 'src/llm/model/provider-registry.json'),\n ];\n\n const errors: string[] = [];\n\n for (const jsonPath of possiblePaths) {\n try {\n const content = fs.readFileSync(jsonPath, 'utf-8');\n const parsed = JSON.parse(content) as RegistryData;\n registryData = sanitizeRegistryDataForRuntime(parsed, enabledGatewayIds);\n return registryData;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n errors.push(`${jsonPath}: ${errorMessage}`);\n\n // If the file exists but has corrupted JSON (not ENOENT), delete it and fall back to static registry\n // This handles cases where concurrent writes corrupted the file before the atomic write fix\n const isFileNotFound = err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n const isJsonParseError = err instanceof SyntaxError;\n\n if (!isFileNotFound && isJsonParseError) {\n console.warn(\n `[GatewayRegistry] Detected corrupted provider-registry.json at ${jsonPath}. ` +\n `Deleting corrupted file and falling back to static registry.`,\n );\n try {\n fs.unlinkSync(jsonPath);\n } catch {\n // Ignore deletion errors\n }\n // Fall back to static registry (bundled at build time)\n registryData = sanitizeRegistryDataForRuntime(staticRegistry, enabledGatewayIds);\n return registryData;\n }\n\n continue;\n }\n }\n\n // If all paths failed, fall back to static registry instead of throwing\n // This provides a more graceful degradation\n console.warn(\n `[GatewayRegistry] Could not load provider registry from any path. Falling back to static registry.\\n` +\n `Tried paths:\\n${errors.join('\\n')}`,\n );\n registryData = sanitizeRegistryDataForRuntime(staticRegistry, enabledGatewayIds);\n return registryData;\n}\n\n// Export registry data via Proxy for lazy loading\nexport const PROVIDER_REGISTRY = new Proxy({} as Record<string, ProviderConfig>, {\n get(_target, prop: string) {\n const registry = GatewayRegistry.getInstance();\n const providers = registry.getProviders();\n return providers[prop];\n },\n ownKeys() {\n const registry = GatewayRegistry.getInstance();\n const providers = registry.getProviders();\n return Object.keys(providers);\n },\n has(_target, prop: string) {\n const registry = GatewayRegistry.getInstance();\n const providers = registry.getProviders();\n return prop in providers;\n },\n getOwnPropertyDescriptor(_target, prop) {\n const registry = GatewayRegistry.getInstance();\n const providers = registry.getProviders();\n if (prop in providers) {\n return {\n enumerable: true,\n configurable: true,\n };\n }\n return undefined;\n },\n}) as Record<Provider, ProviderConfig>;\n\nexport const PROVIDER_MODELS = new Proxy({} as ProviderModels, {\n get(_target, prop: string) {\n const registry = GatewayRegistry.getInstance();\n const models = registry.getModels();\n return models[prop];\n },\n ownKeys() {\n const registry = GatewayRegistry.getInstance();\n const models = registry.getModels();\n return Object.keys(models);\n },\n has(_target, prop: string) {\n const registry = GatewayRegistry.getInstance();\n const models = registry.getModels();\n return prop in models;\n },\n getOwnPropertyDescriptor(_target, prop) {\n const registry = GatewayRegistry.getInstance();\n const models = registry.getModels();\n if (prop in models) {\n return {\n enumerable: true,\n configurable: true,\n };\n }\n return undefined;\n },\n});\n\n/**\n * Parse a model string to extract provider and model ID\n * Examples:\n * \"openai/gpt-4o\" -> { provider: \"openai\", modelId: \"gpt-4o\" }\n * \"fireworks/accounts/etc/model\" -> { provider: \"fireworks\", modelId: \"accounts/etc/model\" }\n * \"gpt-4o\" -> { provider: null, modelId: \"gpt-4o\" }\n */\nexport function parseModelString(modelString: string): { provider: string | null; modelId: string } {\n const firstSlashIndex = modelString.indexOf('/');\n\n if (firstSlashIndex !== -1) {\n // Has at least one slash - extract everything before first slash as provider\n const provider = modelString.substring(0, firstSlashIndex);\n const modelId = modelString.substring(firstSlashIndex + 1);\n\n if (provider && modelId) {\n return {\n provider,\n modelId,\n };\n }\n }\n\n // No slash or invalid format\n return {\n provider: null,\n modelId: modelString,\n };\n}\n\n/**\n * Get provider configuration by provider ID\n */\nexport function getProviderConfig(providerId: string): ProviderConfig | undefined {\n const registry = GatewayRegistry.getInstance();\n return registry.getProviderConfig(providerId);\n}\n\n/**\n * Check if a provider is registered\n */\nexport function isProviderRegistered(providerId: string): boolean {\n const registry = GatewayRegistry.getInstance();\n return registry.isProviderRegistered(providerId);\n}\n\n/**\n * Get all registered provider IDs\n */\nexport function getRegisteredProviders(): string[] {\n const registry = GatewayRegistry.getInstance();\n const providers = registry.getProviders();\n return Object.keys(providers);\n}\n\n// ---------------------------------------------------------------------------\n// Provider capabilities (per-model attachment / modality metadata)\n// ---------------------------------------------------------------------------\n\ninterface ProviderCapabilityFile {\n attachment?: string[];\n temperature?: string[];\n structuredOutput?: string[];\n}\n\ntype CapabilityDimension = keyof ProviderCapabilityFile;\n\nconst providerCapCaches: Record<CapabilityDimension, Map<string, string[] | null>> = {\n attachment: new Map(),\n temperature: new Map(),\n structuredOutput: new Map(),\n};\n\nconst capabilityOverrides: Partial<Record<CapabilityDimension, Record<string, boolean>>> = {\n // DeepSeek's native endpoint rejects response_format for this routed model even\n // though models.dev currently reports structured_output support.\n structuredOutput: {\n 'deepseek/deepseek-v4-pro': false,\n },\n};\n\nfunction isDirectory(dir: string): boolean {\n try {\n return fs.existsSync(dir) && fs.statSync(dir).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction findCapabilitiesDirs(useDynamicLoading: boolean): string[] {\n const packageRoot = getPackageRoot();\n const distCapabilitiesDir = path.join(packageRoot, 'dist', 'capabilities');\n const sourceCapabilitiesDir = path.join(packageRoot, 'src', 'llm', 'model', 'capabilities');\n const workspaceSourceCapabilitiesDir = path.join(process.cwd(), 'packages/core/src/llm/model/capabilities');\n\n const dirs: string[] = [];\n\n // In dynamic mode, prefer the global cache so fresher gateway-synced data wins.\n if (useDynamicLoading) {\n const globalCapDir = GLOBAL_CAPABILITIES_DIR();\n if (isDirectory(globalCapDir)) dirs.push(globalCapDir);\n }\n\n if (isDirectory(distCapabilitiesDir)) dirs.push(distCapabilitiesDir);\n\n // Published packages only include dist/. Source fallbacks are for local workspace/dev\n // runs where @mastra/core may resolve through a stale partial dist while checked-in\n // source capability files are available.\n if (isDirectory(sourceCapabilitiesDir)) dirs.push(sourceCapabilitiesDir);\n if (workspaceSourceCapabilitiesDir !== sourceCapabilitiesDir && isDirectory(workspaceSourceCapabilitiesDir)) {\n dirs.push(workspaceSourceCapabilitiesDir);\n }\n\n return dirs;\n}\n\nlet capabilitiesDirCache: string[] | undefined;\n\n/** Parsed capability file cache — avoids re-reading JSON per dimension. */\nconst parsedCapFileCache = new Map<string, ProviderCapabilityFile | null>();\n\nfunction loadProviderCapabilityFile(provider: string, useDynamicLoading: boolean): ProviderCapabilityFile | null {\n if (parsedCapFileCache.has(provider)) return parsedCapFileCache.get(provider)!;\n\n if (capabilitiesDirCache === undefined) {\n capabilitiesDirCache = findCapabilitiesDirs(useDynamicLoading);\n }\n\n for (const capabilitiesDir of capabilitiesDirCache) {\n const filePath = path.join(capabilitiesDir, getCapabilityFileName(provider));\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const data = JSON.parse(content) as ProviderCapabilityFile;\n parsedCapFileCache.set(provider, data);\n return data;\n } catch {\n continue;\n }\n }\n\n parsedCapFileCache.set(provider, null);\n return null;\n}\n\nfunction loadProviderCapability(\n provider: string,\n dimension: CapabilityDimension,\n useDynamicLoading: boolean,\n): string[] | null {\n const cache = providerCapCaches[dimension];\n if (cache.has(provider)) return cache.get(provider)!;\n\n const file = loadProviderCapabilityFile(provider, useDynamicLoading);\n const models = file?.[dimension] ?? null;\n cache.set(provider, models);\n return models;\n}\n\nfunction getProviderCapabilitySupport(\n provider: string,\n modelId: string,\n dimension: CapabilityDimension,\n useDynamicLoading: boolean,\n): boolean | undefined {\n const models = loadProviderCapability(provider, dimension, useDynamicLoading);\n if (!models) return undefined;\n return models.includes(modelId);\n}\n\nfunction modelSupportsCapability(modelRouterId: string, dimension: CapabilityDimension): boolean | undefined {\n const override = capabilityOverrides[dimension]?.[modelRouterId];\n if (override !== undefined) return override;\n\n const { provider, modelId } = parseModelString(modelRouterId);\n if (!provider) return undefined;\n\n const registry = GatewayRegistry.getInstance();\n const useDynamicLoading = registry['useDynamicLoading'];\n const directSupport = getProviderCapabilitySupport(provider, modelId, dimension, useDynamicLoading);\n\n // Positive direct match wins immediately.\n if (directSupport === true) return true;\n\n // For nested model IDs (e.g. `openrouter/anthropic/claude-sonnet-4-6`), the\n // outer gateway's capability list may not enumerate every nested model. Fall\n // back to the underlying provider's authoritative capability file before\n // trusting a `false` from the gateway.\n const nestedProviderDelimiter = modelId.indexOf('/');\n if (nestedProviderDelimiter !== -1) {\n const nestedProvider = modelId.substring(0, nestedProviderDelimiter);\n const nestedModelId = modelId.substring(nestedProviderDelimiter + 1);\n if (nestedProvider && nestedModelId) {\n const nestedSupport = getProviderCapabilitySupport(nestedProvider, nestedModelId, dimension, useDynamicLoading);\n if (nestedSupport !== undefined) return nestedSupport;\n }\n }\n\n return directSupport;\n}\n\n/** @internal Reset capability caches. For testing only. */\nexport function _resetCapabilityCaches(): void {\n for (const cache of Object.values(providerCapCaches)) cache.clear();\n parsedCapFileCache.clear();\n capabilitiesDirCache = undefined;\n}\n\n/**\n * Check whether a model supports image/file attachments.\n * Returns `true` if the model is listed, `false` if the provider is known but\n * the model isn't listed, or `undefined` when no data exists for the provider.\n */\nexport function modelSupportsAttachments(modelRouterId: string): boolean | undefined {\n return modelSupportsCapability(modelRouterId, 'attachment');\n}\n\n/**\n * Check whether a model supports the `temperature` sampling parameter.\n * Returns `true` if the model is listed, `false` if the provider is known but\n * the model isn't listed, or `undefined` when no data exists for the provider.\n */\nexport function modelSupportsTemperature(modelRouterId: string): boolean | undefined {\n return modelSupportsCapability(modelRouterId, 'temperature');\n}\n\n/**\n * Check whether a model supports native structured output.\n * Returns `true` if the model is listed, `false` if the provider is known but\n * the model isn't listed, or `undefined` when no data exists for the provider.\n */\nexport function modelSupportsStructuredOutput(modelRouterId: string): boolean | undefined {\n return modelSupportsCapability(modelRouterId, 'structuredOutput');\n}\n\n/**\n * Type guard to check if a string is a valid OpenAI-compatible model ID\n */\nexport function isValidModelId(modelId: string): modelId is ModelRouterModelId {\n const { provider } = parseModelString(modelId);\n return provider !== null && isProviderRegistered(provider);\n}\n\nexport interface GatewayRegistryOptions {\n /**\n * Enable dynamic loading from file system instead of using static bundled registry.\n * Required for syncGateways() and auto-refresh to work.\n * Defaults to true when MASTRA_DEV=true, false otherwise.\n */\n useDynamicLoading?: boolean;\n}\n\n/**\n * GatewayRegistry - Manages dynamic loading and refreshing of provider data from gateways\n * Singleton class that handles runtime updates to the provider registry\n */\nexport class GatewayRegistry {\n private static instance: GatewayRegistry | null = null;\n private lastRefreshTime: Date | null = null;\n private refreshInterval: NodeJS.Timeout | null = null;\n private isRefreshing = false;\n private useDynamicLoading: boolean;\n private customGateways: MastraModelGatewayInterface[] = [];\n\n private constructor(options: GatewayRegistryOptions = {}) {\n const isDev = process.env.MASTRA_DEV === 'true' || process.env.MASTRA_DEV === '1';\n this.useDynamicLoading = options.useDynamicLoading ?? isDev;\n }\n\n /**\n * Get the singleton instance\n */\n static getInstance(options?: GatewayRegistryOptions): GatewayRegistry {\n if (!GatewayRegistry.instance) {\n GatewayRegistry.instance = new GatewayRegistry(options);\n return GatewayRegistry.instance;\n }\n\n if (options?.useDynamicLoading === true) {\n GatewayRegistry.instance.useDynamicLoading = true;\n }\n\n return GatewayRegistry.instance;\n }\n\n /**\n * Register custom gateways for type generation\n * @param gateways - Array of custom gateway instances\n */\n registerCustomGateways(gateways: MastraModelGatewayInterface[]): void {\n this.customGateways = gateways;\n }\n\n /**\n * Get all registered custom gateways\n */\n getCustomGateways(): MastraModelGatewayInterface[] {\n return this.customGateways;\n }\n\n /**\n * Sync providers from all gateways\n * Requires dynamic loading to be enabled (useDynamicLoading=true).\n * @param forceRefresh - Force refresh even if recently synced\n * @param writeToSrc - Write to src/ directory in addition to dist/ (useful for manual generation in repo)\n */\n async syncGateways(forceRefresh = false, writeToSrc = false): Promise<void> {\n // Only allow sync when dynamic loading is enabled or when explicitly writing to src (build script)\n if (!this.useDynamicLoading && !writeToSrc) {\n // console.debug('[GatewayRegistry] Skipping sync (dynamic loading disabled, registry is static)');\n return;\n }\n\n // Skip all network fetches when running in offline/air-gapped mode\n if (isOfflineMode()) {\n return;\n }\n\n if (this.isRefreshing && !forceRefresh) {\n // console.debug('[GatewayRegistry] Sync already in progress, skipping...');\n return;\n }\n\n this.isRefreshing = true;\n\n try {\n // console.debug('[GatewayRegistry] Starting gateway sync...');\n\n // Import gateway classes and generation functions\n const { ModelsDevGateway } = await import('./gateways/models-dev.js');\n const { NetlifyGateway } = await import('./gateways/netlify.js');\n const { MastraGateway } = await import('./gateways/mastra.js');\n const { fetchProvidersFromGateways, writeRegistryFiles } = await import('./registry-generator.js');\n\n // Initialize default gateways. Mastra Gateway is dynamic-only and should not be written into checked-in static artifacts.\n const defaultGateways = [\n new ModelsDevGateway({}),\n new NetlifyGateway(),\n ...(writeToSrc ? [] : [new MastraGateway()]),\n ];\n\n // Combine default and custom gateways\n const gateways = [...defaultGateways, ...this.customGateways];\n\n // Fetch provider data\n const {\n providers,\n models,\n attachmentCapabilities,\n temperatureCapabilities,\n structuredOutputCapabilities,\n failedGateways,\n } = await fetchProvidersFromGateways(gateways);\n\n // If any gateway failed, skip writing to prevent partial results from\n // overwriting the complete bundled registry. The existing static registry\n // already contains all provider data, so a partial write would only\n // remove providers (e.g. writing only Netlify providers when models.dev\n // is down strips all direct providers like openai, anthropic, etc.).\n if (failedGateways.length > 0) {\n return;\n }\n\n // Get package root for file paths\n const packageRoot = getPackageRoot();\n\n // Write to global cache first (so all projects can benefit)\n try {\n fs.mkdirSync(CACHE_DIR(), { recursive: true });\n await writeRegistryFiles(\n GLOBAL_PROVIDER_REGISTRY_JSON(),\n GLOBAL_PROVIDER_TYPES_DTS(),\n providers,\n models,\n attachmentCapabilities,\n temperatureCapabilities,\n structuredOutputCapabilities,\n );\n // console.debug(`[GatewayRegistry] ✅ Updated global cache at ${CACHE_DIR()}`);\n } catch (error) {\n console.warn('[GatewayRegistry] Failed to write to global cache:', error);\n }\n\n // Write to dist/ (the bundled location that gets distributed)\n const distJsonPath = path.join(packageRoot, 'dist', 'provider-registry.json');\n const distTypesPath = path.join(packageRoot, 'dist', 'llm', 'model', 'provider-types.generated.d.ts');\n\n await writeRegistryFiles(\n distJsonPath,\n distTypesPath,\n providers,\n models,\n attachmentCapabilities,\n temperatureCapabilities,\n structuredOutputCapabilities,\n );\n // console.debug(`[GatewayRegistry] ✅ Updated registry files in dist/`);\n\n // Copy to src/ only when explicitly requested (e.g., running the generation script)\n const shouldWriteToSrc = writeToSrc;\n if (shouldWriteToSrc) {\n const srcJsonPath = path.join(packageRoot, 'src', 'llm', 'model', 'provider-registry.json');\n const srcTypesPath = path.join(packageRoot, 'src', 'llm', 'model', 'provider-types.generated.d.ts');\n\n // Copy the already-generated files\n await fs.promises.copyFile(distJsonPath, srcJsonPath);\n await fs.promises.copyFile(distTypesPath, srcTypesPath);\n\n const distCapDir = path.join(packageRoot, 'dist', 'capabilities');\n const srcCapDir = path.join(packageRoot, 'src', 'llm', 'model', 'capabilities');\n if (fs.existsSync(distCapDir)) {\n await fs.promises.mkdir(srcCapDir, { recursive: true });\n const capFiles = fs.readdirSync(distCapDir).filter(f => f.endsWith('.json'));\n for (const file of capFiles) {\n await fs.promises.copyFile(path.join(distCapDir, file), path.join(srcCapDir, file));\n }\n }\n // console.debug(`[GatewayRegistry] ✅ Copied registry files to src/ (${writeToSrc ? 'manual' : 'dynamic loading'})`);\n }\n\n // Clear the in-memory cache to force reload (dynamic loading only)\n if (this.useDynamicLoading) {\n registryData = null;\n for (const cache of Object.values(providerCapCaches)) cache.clear();\n parsedCapFileCache.clear();\n capabilitiesDirCache = undefined;\n }\n\n this.lastRefreshTime = new Date();\n saveLastRefreshTimeToDisk(this.lastRefreshTime);\n // console.debug(`[GatewayRegistry] ✅ Gateway sync completed at ${this.lastRefreshTime.toISOString()}`);\n } catch {\n // Silently ignore — the bundled registry already contains all\n // model data so a failed sync is non-critical.\n } finally {\n this.isRefreshing = false;\n }\n }\n\n /**\n * Get the last refresh time (from memory or disk cache)\n */\n getLastRefreshTime(): Date | null {\n return this.lastRefreshTime || getLastRefreshTimeFromDisk();\n }\n\n /**\n * Start auto-refresh on an interval\n * Requires dynamic loading to be enabled (useDynamicLoading=true).\n * @param intervalMs - Interval in milliseconds (default: 1 hour)\n */\n startAutoRefresh(intervalMs = 60 * 60 * 1000): void {\n // Only allow auto-refresh when dynamic loading is enabled\n if (!this.useDynamicLoading) {\n // console.debug('[GatewayRegistry] Skipping auto-refresh (dynamic loading disabled, registry is static)');\n return;\n }\n\n // Skip auto-refresh when running in offline/air-gapped mode\n if (isOfflineMode()) {\n return;\n }\n\n if (this.refreshInterval) {\n // console.debug('[GatewayRegistry] Auto-refresh already running');\n return;\n }\n\n // console.debug(`[GatewayRegistry] Starting auto-refresh (interval: ${intervalMs}ms)`);\n\n // Check if we need to run an immediate sync\n const lastRefresh = getLastRefreshTimeFromDisk();\n const now = Date.now();\n const shouldRefresh = !modelRouterCacheFailed && (!lastRefresh || now - lastRefresh.getTime() > intervalMs);\n\n if (shouldRefresh) {\n this.syncGateways().catch(() => {});\n }\n\n this.refreshInterval = setInterval(() => {\n if (modelRouterCacheFailed && this.refreshInterval) {\n clearInterval(this.refreshInterval);\n this.refreshInterval = null;\n return;\n }\n this.syncGateways().catch(() => {});\n }, intervalMs);\n\n // Prevent the interval from keeping the process alive\n if (this.refreshInterval.unref) {\n this.refreshInterval.unref();\n }\n }\n\n /**\n * Stop auto-refresh\n */\n stopAutoRefresh(): void {\n if (this.refreshInterval) {\n clearInterval(this.refreshInterval);\n this.refreshInterval = null;\n // console.debug('[GatewayRegistry] Auto-refresh stopped');\n }\n }\n\n /**\n * Get provider configuration by ID\n */\n getProviderConfig(providerId: string): ProviderConfig | undefined {\n const data = loadRegistry(this.useDynamicLoading, this.customGateways);\n return data.providers[providerId];\n }\n\n /**\n * Check if a provider is registered\n */\n isProviderRegistered(providerId: string): boolean {\n const data = loadRegistry(this.useDynamicLoading, this.customGateways);\n return providerId in data.providers;\n }\n\n /**\n * Get all registered providers\n */\n getProviders(): Record<string, ProviderConfig> {\n const data = loadRegistry(this.useDynamicLoading, this.customGateways);\n return data.providers;\n }\n\n /**\n * Get all models\n */\n getModels(): Record<string, string[]> {\n return loadRegistry(this.useDynamicLoading, this.customGateways).models;\n }\n}\n\n// Auto-start refresh if enabled\n// Defaults to enabled when MASTRA_DEV=true (which enables dynamic loading by default)\n// Disabled entirely when MASTRA_OFFLINE is set (air-gapped/offline environments)\nconst isDev = process.env.MASTRA_DEV === 'true' || process.env.MASTRA_DEV === '1';\nconst autoRefreshEnabled =\n !isOfflineMode() &&\n (process.env.MASTRA_AUTO_REFRESH_PROVIDERS === 'true' ||\n (process.env.MASTRA_AUTO_REFRESH_PROVIDERS !== 'false' && isDev));\n\nif (autoRefreshEnabled) {\n // console.debug('[GatewayRegistry] Auto-refresh enabled');\n GatewayRegistry.getInstance({ useDynamicLoading: isDev }).startAutoRefresh();\n}\n"],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;