@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
955 lines (954 loc) • 84.8 kB
JavaScript
import { createVertex, } from "@ai-sdk/google-vertex";
import { createVertexAnthropic, } from "@ai-sdk/google-vertex/anthropic";
import { streamText, Output, } from "ai";
import { BaseProvider } from "../core/baseProvider.js";
import { logger } from "../utils/logger.js";
import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
import { DEFAULT_MAX_TOKENS, DEFAULT_MAX_STEPS } from "../core/constants.js";
import { ModelConfigurationManager } from "../core/modelConfiguration.js";
import { validateApiKey, createVertexProjectConfig, createGoogleAuthConfig, } from "../utils/providerConfig.js";
import fs from "fs";
import path from "path";
import os from "os";
import dns from "dns";
import { buildMessagesArray } from "../utils/messageBuilder.js";
import { createProxyFetch } from "../proxy/proxyFetch.js";
// Enhanced Anthropic support with direct imports
// Using the dual provider architecture from Vercel AI SDK
const hasAnthropicSupport = () => {
try {
// Verify the anthropic module is available
return typeof createVertexAnthropic === "function";
}
catch {
return false;
}
};
// Configuration helpers - now using consolidated utility
const getVertexProjectId = () => {
return validateApiKey(createVertexProjectConfig());
};
const getVertexLocation = () => {
return (process.env.GOOGLE_CLOUD_LOCATION ||
process.env.VERTEX_LOCATION ||
process.env.GOOGLE_VERTEX_LOCATION ||
"us-central1");
};
const getDefaultVertexModel = () => {
// Use gemini-2.5-flash as default - latest and best price-performance model
// Override with VERTEX_MODEL environment variable if needed
return process.env.VERTEX_MODEL || "gemini-2.5-flash";
};
const hasGoogleCredentials = () => {
return !!(process.env.GOOGLE_APPLICATION_CREDENTIALS ||
process.env.GOOGLE_SERVICE_ACCOUNT_KEY ||
(process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
process.env.GOOGLE_AUTH_PRIVATE_KEY));
};
// Enhanced Vertex settings creation with authentication fallback and proxy support
const createVertexSettings = async () => {
const baseSettings = {
project: getVertexProjectId(),
location: getVertexLocation(),
fetch: createProxyFetch(),
};
// 🎯 OPTION 2: Create credentials file from environment variables at runtime
// This solves the problem where GOOGLE_APPLICATION_CREDENTIALS exists in ZSHRC locally
// but the file doesn't exist on production servers
// First, try to create credentials file from individual environment variables
const requiredEnvVarsForFile = {
type: process.env.GOOGLE_AUTH_TYPE,
project_id: process.env.GOOGLE_AUTH_BREEZE_PROJECT_ID,
private_key: process.env.GOOGLE_AUTH_PRIVATE_KEY,
client_email: process.env.GOOGLE_AUTH_CLIENT_EMAIL,
client_id: process.env.GOOGLE_AUTH_CLIENT_ID,
auth_uri: process.env.GOOGLE_AUTH_AUTH_URI,
token_uri: process.env.GOOGLE_AUTH_TOKEN_URI,
auth_provider_x509_cert_url: process.env.GOOGLE_AUTH_AUTH_PROVIDER_CERT_URL,
client_x509_cert_url: process.env.GOOGLE_AUTH_CLIENT_CERT_URL,
universe_domain: process.env.GOOGLE_AUTH_UNIVERSE_DOMAIN,
};
// If we have the essential fields, create a runtime credentials file
if (requiredEnvVarsForFile.client_email &&
requiredEnvVarsForFile.private_key) {
try {
// Build complete service account credentials object
const serviceAccountCredentials = {
type: requiredEnvVarsForFile.type || "service_account",
project_id: requiredEnvVarsForFile.project_id || getVertexProjectId(),
private_key: requiredEnvVarsForFile.private_key.replace(/\\n/g, "\n"),
client_email: requiredEnvVarsForFile.client_email,
client_id: requiredEnvVarsForFile.client_id || "",
auth_uri: requiredEnvVarsForFile.auth_uri ||
"https://accounts.google.com/o/oauth2/auth",
token_uri: requiredEnvVarsForFile.token_uri ||
"https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: requiredEnvVarsForFile.auth_provider_x509_cert_url ||
"https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: requiredEnvVarsForFile.client_x509_cert_url || "",
universe_domain: requiredEnvVarsForFile.universe_domain || "googleapis.com",
};
// Create temporary credentials file
const tmpDir = os.tmpdir();
const credentialsFileName = `google-credentials-${Date.now()}-${Math.random().toString(36).substring(2, 11)}.json`;
const credentialsFilePath = path.join(tmpDir, credentialsFileName);
fs.writeFileSync(credentialsFilePath, JSON.stringify(serviceAccountCredentials, null, 2));
// Set the environment variable to point to our runtime-created file
process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsFilePath;
// Now continue with the normal flow - check if the file exists
const fileExists = fs.existsSync(credentialsFilePath);
if (fileExists) {
return baseSettings;
}
}
catch {
// Silent error handling for runtime credentials file creation
}
}
// 🎯 OPTION 1: Check for principal account authentication (Accept any valid GOOGLE_APPLICATION_CREDENTIALS file (service account OR ADC))
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
// Check if the credentials file exists
let fileExists = false;
try {
fileExists = fs.existsSync(credentialsPath);
}
catch {
fileExists = false;
}
if (fileExists) {
return baseSettings;
}
}
// Fallback to explicit credentials for development and production
// Enhanced to check ALL required fields from the .env file configuration
const requiredEnvVars = {
type: process.env.GOOGLE_AUTH_TYPE,
project_id: process.env.GOOGLE_AUTH_BREEZE_PROJECT_ID,
private_key: process.env.GOOGLE_AUTH_PRIVATE_KEY,
client_email: process.env.GOOGLE_AUTH_CLIENT_EMAIL,
client_id: process.env.GOOGLE_AUTH_CLIENT_ID,
auth_uri: process.env.GOOGLE_AUTH_AUTH_URI,
token_uri: process.env.GOOGLE_AUTH_TOKEN_URI,
auth_provider_x509_cert_url: process.env.GOOGLE_AUTH_AUTH_PROVIDER_CERT_URL,
client_x509_cert_url: process.env.GOOGLE_AUTH_CLIENT_CERT_URL,
universe_domain: process.env.GOOGLE_AUTH_UNIVERSE_DOMAIN,
};
// Check if we have the minimal required fields (client_email and private_key are essential)
if (requiredEnvVars.client_email && requiredEnvVars.private_key) {
logger.debug("Using explicit service account credentials authentication", {
authMethod: "explicit_service_account_credentials",
hasType: !!requiredEnvVars.type,
hasProjectId: !!requiredEnvVars.project_id,
hasClientEmail: !!requiredEnvVars.client_email,
hasPrivateKey: !!requiredEnvVars.private_key,
hasClientId: !!requiredEnvVars.client_id,
hasAuthUri: !!requiredEnvVars.auth_uri,
hasTokenUri: !!requiredEnvVars.token_uri,
hasAuthProviderCertUrl: !!requiredEnvVars.auth_provider_x509_cert_url,
hasClientCertUrl: !!requiredEnvVars.client_x509_cert_url,
hasUniverseDomain: !!requiredEnvVars.universe_domain,
credentialsCompleteness: "using_individual_env_vars_as_fallback",
});
// Build complete service account credentials object
const serviceAccountCredentials = {
type: requiredEnvVars.type || "service_account",
project_id: requiredEnvVars.project_id || getVertexProjectId(),
private_key: requiredEnvVars.private_key.replace(/\\n/g, "\n"),
client_email: requiredEnvVars.client_email,
client_id: requiredEnvVars.client_id || "",
auth_uri: requiredEnvVars.auth_uri ||
"https://accounts.google.com/o/oauth2/auth",
token_uri: requiredEnvVars.token_uri || "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: requiredEnvVars.auth_provider_x509_cert_url ||
"https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: requiredEnvVars.client_x509_cert_url || "",
universe_domain: requiredEnvVars.universe_domain || "googleapis.com",
};
return {
...baseSettings,
googleAuthOptions: {
credentials: serviceAccountCredentials,
},
};
}
// Log comprehensive warning if no valid authentication is available
logger.warn("No valid authentication found for Google Vertex AI", {
authMethod: "none",
authenticationAttempts: {
principalAccountFile: {
envVarSet: !!process.env.GOOGLE_APPLICATION_CREDENTIALS,
filePath: process.env.GOOGLE_APPLICATION_CREDENTIALS || "NOT_SET",
fileExists: false, // We already checked above
},
explicitCredentials: {
hasClientEmail: !!requiredEnvVars.client_email,
hasPrivateKey: !!requiredEnvVars.private_key,
hasProjectId: !!requiredEnvVars.project_id,
hasType: !!requiredEnvVars.type,
missingFields: Object.entries(requiredEnvVars)
.filter(([_key, value]) => !value)
.map(([key]) => key),
},
},
troubleshooting: [
"1. Ensure GOOGLE_APPLICATION_CREDENTIALS points to an existing file, OR",
"2. Set individual environment variables: GOOGLE_AUTH_CLIENT_EMAIL and GOOGLE_AUTH_PRIVATE_KEY",
],
});
return baseSettings;
};
// Create Anthropic-specific Vertex settings with the same authentication and proxy support
const createVertexAnthropicSettings = async () => {
const baseVertexSettings = await createVertexSettings();
// GoogleVertexAnthropicProviderSettings extends GoogleVertexProviderSettings
// so we can use the same settings with proper typing
return {
project: baseVertexSettings.project,
location: baseVertexSettings.location,
fetch: baseVertexSettings.fetch,
...(baseVertexSettings.googleAuthOptions && {
googleAuthOptions: baseVertexSettings.googleAuthOptions,
}),
};
};
// Helper function to determine if a model is an Anthropic model
const isAnthropicModel = (modelName) => {
return modelName.toLowerCase().includes("claude");
};
/**
* Google Vertex AI Provider v2 - BaseProvider Implementation
*
* Features:
* - Extends BaseProvider for shared functionality
* - Preserves existing Google Cloud authentication
* - Maintains Anthropic model support via dynamic imports
* - Fresh model creation for each request
* - Enhanced error handling with setup guidance
* - Tool registration and context management
*/
export class GoogleVertexProvider extends BaseProvider {
projectId;
location;
registeredTools = new Map();
toolContext = {};
// Memory-managed cache for model configuration lookups to avoid repeated calls
// Uses WeakMap for automatic cleanup and bounded LRU for recently used models
static modelConfigCache = new Map();
static modelConfigCacheTime = 0;
static CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
static MAX_CACHE_SIZE = 50; // Prevent memory leaks by limiting cache size
// Memory-managed cache for maxTokens handling decisions to optimize streaming performance
static maxTokensCache = new Map();
static maxTokensCacheTime = 0;
constructor(modelName, _providerName, sdk) {
super(modelName, "vertex", sdk);
// Validate Google Cloud credentials - now using consolidated utility
if (!hasGoogleCredentials()) {
validateApiKey(createGoogleAuthConfig());
}
// Initialize Google Cloud configuration
this.projectId = getVertexProjectId();
this.location = getVertexLocation();
logger.debug("Google Vertex AI BaseProvider v2 initialized", {
modelName: this.modelName,
projectId: this.projectId,
location: this.location,
provider: this.providerName,
});
}
getProviderName() {
return "vertex";
}
getDefaultModel() {
return getDefaultVertexModel();
}
/**
* Returns the Vercel AI SDK model instance for Google Vertex
* Creates fresh model instances for each request
*/
async getAISDKModel() {
const model = await this.getModel();
return model;
}
/**
* Initialize model creation logging and tracking
*/
initializeModelCreationLogging() {
const modelCreationId = `vertex-model-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
const modelCreationStartTime = Date.now();
const modelCreationHrTimeStart = process.hrtime.bigint();
const modelName = this.modelName || getDefaultVertexModel();
logger.debug(`[GoogleVertexProvider] 🏭 LOG_POINT_V001_MODEL_CREATION_START`, {
logPoint: "V001_MODEL_CREATION_START",
modelCreationId,
timestamp: new Date().toISOString(),
modelCreationStartTime,
modelCreationHrTimeStart: modelCreationHrTimeStart.toString(),
requestedModel: this.modelName,
resolvedModel: modelName,
defaultModel: getDefaultVertexModel(),
projectId: this.projectId,
location: this.location,
// Environment analysis for network issues
environmentAnalysis: {
httpProxy: process.env.HTTP_PROXY || process.env.http_proxy || "NOT_SET",
httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy || "NOT_SET",
googleAppCreds: process.env.GOOGLE_APPLICATION_CREDENTIALS || "NOT_SET",
googleServiceKey: process.env.GOOGLE_SERVICE_ACCOUNT_KEY
? "SET"
: "NOT_SET",
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
},
// Memory and performance baseline
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage(),
message: "Starting model creation with comprehensive environment analysis",
});
return {
modelCreationId,
modelCreationStartTime,
modelCreationHrTimeStart,
modelName,
};
}
/**
* Check if model is Anthropic-based and attempt creation
*/
async attemptAnthropicModelCreation(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart) {
const anthropicCheckStartTime = process.hrtime.bigint();
const isAnthropic = isAnthropicModel(modelName);
logger.debug(`[GoogleVertexProvider] 🤖 LOG_POINT_V002_ANTHROPIC_CHECK`, {
logPoint: "V002_ANTHROPIC_CHECK",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
anthropicCheckStartTimeNs: anthropicCheckStartTime.toString(),
modelName,
isAnthropicModel: isAnthropic,
modelNameLowerCase: modelName.toLowerCase(),
containsClaude: modelName.toLowerCase().includes("claude"),
anthropicModelPatterns: ["claude"],
message: "Checking if model is Anthropic-based",
});
if (!isAnthropic) {
return null;
}
const anthropicModelStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🧠 LOG_POINT_V003_ANTHROPIC_MODEL_START`, {
logPoint: "V003_ANTHROPIC_MODEL_START",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
anthropicModelStartTimeNs: anthropicModelStartTime.toString(),
modelName,
hasAnthropicSupport: hasAnthropicSupport(),
message: "Creating Anthropic model using vertexAnthropic provider",
});
logger.debug("Creating Anthropic model using vertexAnthropic provider", {
modelName,
});
if (!hasAnthropicSupport()) {
logger.warn(`[GoogleVertexProvider] Anthropic support not available, falling back to Google model`);
return null;
}
try {
const anthropicModel = await this.createAnthropicModel(modelName);
if (anthropicModel) {
const anthropicModelSuccessTime = process.hrtime.bigint();
const anthropicModelDurationNs = anthropicModelSuccessTime - anthropicModelStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_V004_ANTHROPIC_MODEL_SUCCESS`, {
logPoint: "V004_ANTHROPIC_MODEL_SUCCESS",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
anthropicModelDurationNs: anthropicModelDurationNs.toString(),
anthropicModelDurationMs: Number(anthropicModelDurationNs) / 1000000,
modelName,
hasAnthropicModel: !!anthropicModel,
anthropicModelType: typeof anthropicModel,
memoryUsageAfterAnthropicCreation: process.memoryUsage(),
message: "Anthropic model created successfully via vertexAnthropic",
});
return anthropicModel;
}
// Anthropic model creation returned null
const anthropicModelNullTime = process.hrtime.bigint();
const anthropicModelDurationNs = anthropicModelNullTime - anthropicModelStartTime;
logger.warn(`[GoogleVertexProvider] ⚠️ LOG_POINT_V005_ANTHROPIC_MODEL_NULL`, {
logPoint: "V005_ANTHROPIC_MODEL_NULL",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
anthropicModelDurationNs: anthropicModelDurationNs.toString(),
anthropicModelDurationMs: Number(anthropicModelDurationNs) / 1000000,
modelName,
hasAnthropicModel: false,
fallbackToGoogle: true,
message: "Anthropic model creation returned null - falling back to Google model",
});
}
catch (error) {
const anthropicModelErrorTime = process.hrtime.bigint();
const anthropicModelDurationNs = anthropicModelErrorTime - anthropicModelStartTime;
logger.error(`[GoogleVertexProvider] ❌ LOG_POINT_V006_ANTHROPIC_MODEL_ERROR`, {
logPoint: "V006_ANTHROPIC_MODEL_ERROR",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
anthropicModelDurationNs: anthropicModelDurationNs.toString(),
anthropicModelDurationMs: Number(anthropicModelDurationNs) / 1000000,
modelName,
error: error instanceof Error ? error.message : String(error),
errorName: error instanceof Error ? error.name : "UnknownError",
errorStack: error instanceof Error ? error.stack : undefined,
fallbackToGoogle: true,
message: "Anthropic model creation failed - falling back to Google model",
});
}
// Fall back to regular model if Anthropic not available
logger.warn(`Anthropic model ${modelName} requested but not available, falling back to Google model`);
return null;
}
/**
* Create Google Vertex model with comprehensive logging and error handling
*/
async createGoogleVertexModel(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart, isAnthropic) {
const googleModelStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🌐 LOG_POINT_V007_GOOGLE_MODEL_START`, {
logPoint: "V007_GOOGLE_MODEL_START",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
googleModelStartTimeNs: googleModelStartTime.toString(),
modelName,
projectId: this.projectId,
location: this.location,
reason: isAnthropic ? "ANTHROPIC_FALLBACK" : "DIRECT_GOOGLE_MODEL",
message: "Creating fresh Google Vertex model with current settings",
});
logger.debug("Creating Google Vertex model", {
modelName,
project: this.projectId,
location: this.location,
});
const vertexSettingsStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] ⚙️ LOG_POINT_V008_VERTEX_SETTINGS_START`, {
logPoint: "V008_VERTEX_SETTINGS_START",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
vertexSettingsStartTimeNs: vertexSettingsStartTime.toString(),
// Network configuration analysis
networkConfig: {
projectId: this.projectId,
location: this.location,
expectedEndpoint: `https://${this.location}-aiplatform.googleapis.com`,
httpProxy: process.env.HTTP_PROXY || process.env.http_proxy,
httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy,
noProxy: process.env.NO_PROXY || process.env.no_proxy,
proxyConfigured: !!(process.env.HTTP_PROXY ||
process.env.HTTPS_PROXY ||
process.env.http_proxy ||
process.env.https_proxy),
},
message: "Starting Vertex settings creation with network configuration analysis",
});
try {
const vertexSettings = await createVertexSettings();
const vertexSettingsEndTime = process.hrtime.bigint();
const vertexSettingsDurationNs = vertexSettingsEndTime - vertexSettingsStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_V009_VERTEX_SETTINGS_SUCCESS`, {
logPoint: "V009_VERTEX_SETTINGS_SUCCESS",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
vertexSettingsDurationNs: vertexSettingsDurationNs.toString(),
vertexSettingsDurationMs: Number(vertexSettingsDurationNs) / 1000000,
// Settings analysis
vertexSettingsAnalysis: {
hasSettings: !!vertexSettings,
settingsType: typeof vertexSettings,
settingsKeys: vertexSettings ? Object.keys(vertexSettings) : [],
projectId: vertexSettings?.project,
location: vertexSettings?.location,
hasFetch: !!vertexSettings?.fetch,
hasGoogleAuthOptions: !!vertexSettings?.googleAuthOptions,
settingsSize: vertexSettings
? JSON.stringify(vertexSettings).length
: 0,
},
message: "Vertex settings created successfully",
});
return await this.createVertexInstance(vertexSettings, modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart);
}
catch (error) {
const vertexSettingsErrorTime = process.hrtime.bigint();
const vertexSettingsDurationNs = vertexSettingsErrorTime - vertexSettingsStartTime;
const totalErrorDurationNs = vertexSettingsErrorTime - modelCreationHrTimeStart;
logger.error(`[GoogleVertexProvider] ❌ LOG_POINT_V014_VERTEX_SETTINGS_ERROR`, {
logPoint: "V014_VERTEX_SETTINGS_ERROR",
modelCreationId,
timestamp: new Date().toISOString(),
totalElapsedMs: Date.now() - modelCreationStartTime,
totalElapsedNs: totalErrorDurationNs.toString(),
totalErrorDurationMs: Number(totalErrorDurationNs) / 1000000,
vertexSettingsDurationNs: vertexSettingsDurationNs.toString(),
vertexSettingsDurationMs: Number(vertexSettingsDurationNs) / 1000000,
// Comprehensive error analysis
error: error instanceof Error ? error.message : String(error),
errorName: error instanceof Error ? error.name : "UnknownError",
errorStack: error instanceof Error ? error.stack : undefined,
// Network diagnostic information
networkDiagnostics: {
errorCode: error?.code || "UNKNOWN",
errorErrno: error?.errno || "UNKNOWN",
errorAddress: error?.address || "UNKNOWN",
errorPort: error?.port || "UNKNOWN",
errorSyscall: error?.syscall || "UNKNOWN",
errorHostname: error?.hostname || "UNKNOWN",
isTimeoutError: error instanceof Error &&
(error.message.includes("timeout") ||
error.message.includes("ETIMEDOUT")),
isNetworkError: error instanceof Error &&
(error.message.includes("ENOTFOUND") ||
error.message.includes("ECONNREFUSED") ||
error.message.includes("ETIMEDOUT")),
isAuthError: error instanceof Error &&
(error.message.includes("PERMISSION_DENIED") ||
error.message.includes("401") ||
error.message.includes("403")),
infrastructureIssue: error instanceof Error &&
error.message.includes("ETIMEDOUT") &&
error.message.includes("aiplatform.googleapis.com"),
},
// Environment at error time
errorEnvironment: {
httpProxy: process.env.HTTP_PROXY || process.env.http_proxy,
httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy,
googleAppCreds: process.env.GOOGLE_APPLICATION_CREDENTIALS,
hasGoogleServiceKey: !!process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
nodeVersion: process.version,
memoryUsage: process.memoryUsage(),
uptime: process.uptime(),
},
message: "Vertex settings creation failed - critical network/authentication error",
});
throw error;
}
}
/**
* Create Vertex AI instance and model with comprehensive logging
*/
async createVertexInstance(vertexSettings, modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart) {
const vertexInstanceStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🏗️ LOG_POINT_V010_VERTEX_INSTANCE_START`, {
logPoint: "V010_VERTEX_INSTANCE_START",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
vertexInstanceStartTimeNs: vertexInstanceStartTime.toString(),
// Pre-creation network environment
networkEnvironment: {
dnsServers: (() => {
try {
return dns.getServers ? dns.getServers() : "NOT_AVAILABLE";
}
catch {
return "NOT_AVAILABLE";
}
})(),
networkInterfaces: (() => {
try {
return Object.keys(os.networkInterfaces());
}
catch {
return [];
}
})(),
hostname: (() => {
try {
return os.hostname();
}
catch {
return "UNKNOWN";
}
})(),
platform: (() => {
try {
return os.platform();
}
catch {
return "UNKNOWN";
}
})(),
release: (() => {
try {
return os.release();
}
catch {
return "UNKNOWN";
}
})(),
},
message: "Creating Vertex AI instance",
});
const vertex = createVertex(vertexSettings);
const vertexInstanceEndTime = process.hrtime.bigint();
const vertexInstanceDurationNs = vertexInstanceEndTime - vertexInstanceStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_V011_VERTEX_INSTANCE_SUCCESS`, {
logPoint: "V011_VERTEX_INSTANCE_SUCCESS",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
vertexInstanceDurationNs: vertexInstanceDurationNs.toString(),
vertexInstanceDurationMs: Number(vertexInstanceDurationNs) / 1000000,
hasVertexInstance: !!vertex,
vertexInstanceType: typeof vertex,
message: "Vertex AI instance created successfully",
});
const modelInstanceStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🎯 LOG_POINT_V012_MODEL_INSTANCE_START`, {
logPoint: "V012_MODEL_INSTANCE_START",
modelCreationId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - modelCreationStartTime,
elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
modelInstanceStartTimeNs: modelInstanceStartTime.toString(),
modelName,
hasVertexInstance: !!vertex,
message: "Creating model instance from Vertex AI instance",
});
const model = vertex(modelName);
const modelInstanceEndTime = process.hrtime.bigint();
const modelInstanceDurationNs = modelInstanceEndTime - modelInstanceStartTime;
const totalModelCreationDurationNs = modelInstanceEndTime - modelCreationHrTimeStart;
logger.info(`[GoogleVertexProvider] 🏁 LOG_POINT_V013_MODEL_CREATION_COMPLETE`, {
logPoint: "V013_MODEL_CREATION_COMPLETE",
modelCreationId,
timestamp: new Date().toISOString(),
totalElapsedMs: Date.now() - modelCreationStartTime,
totalElapsedNs: totalModelCreationDurationNs.toString(),
totalDurationMs: Number(totalModelCreationDurationNs) / 1000000,
modelInstanceDurationNs: modelInstanceDurationNs.toString(),
modelInstanceDurationMs: Number(modelInstanceDurationNs) / 1000000,
// Final model analysis
finalModel: {
hasModel: !!model,
modelType: typeof model,
modelName,
isAnthropicModel: isAnthropicModel(modelName),
projectId: this.projectId,
location: this.location,
},
// Performance summary
performanceSummary: {
vertexSettingsDurationMs: Number(vertexInstanceDurationNs) / 1000000,
vertexInstanceDurationMs: Number(vertexInstanceDurationNs) / 1000000,
modelInstanceDurationMs: Number(modelInstanceDurationNs) / 1000000,
totalDurationMs: Number(totalModelCreationDurationNs) / 1000000,
},
// Memory usage
finalMemoryUsage: process.memoryUsage(),
message: "Model creation completed successfully - ready for API calls",
});
return model;
}
/**
* Gets the appropriate model instance (Google or Anthropic)
* Uses dual provider architecture for proper model routing
* Creates fresh instances for each request to ensure proper authentication
*/
async getModel() {
// Initialize logging and setup
const { modelCreationId, modelCreationStartTime, modelCreationHrTimeStart, modelName, } = this.initializeModelCreationLogging();
// Check if this is an Anthropic model and attempt creation
const anthropicModel = await this.attemptAnthropicModelCreation(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart);
if (anthropicModel) {
return anthropicModel;
}
// Fall back to Google Vertex model creation
return await this.createGoogleVertexModel(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart, isAnthropicModel(modelName));
}
// executeGenerate removed - BaseProvider handles all generation with tools
/**
* Log stream execution start with comprehensive analysis
*/
logStreamExecutionStart(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, functionTag, options, analysisSchema) {
logger.info(`[GoogleVertexProvider] 🎬 LOG_POINT_S001_STREAM_EXECUTION_START`, {
logPoint: "S001_STREAM_EXECUTION_START",
streamExecutionId,
timestamp: new Date().toISOString(),
streamExecutionStartTime,
streamExecutionHrTimeStart: streamExecutionHrTimeStart.toString(),
functionTag,
// Input analysis
inputAnalysis: {
hasOptions: !!options,
optionsType: typeof options,
optionsKeys: options ? Object.keys(options) : [],
hasInputText: !!options?.input?.text,
inputTextLength: options?.input?.text?.length || 0,
inputTextPreview: options?.input?.text?.substring(0, 200) || "NO_TEXT",
hasAnalysisSchema: !!analysisSchema,
schemaType: analysisSchema ? typeof analysisSchema : "NO_SCHEMA",
disableTools: options?.disableTools || false,
temperature: options?.temperature,
maxTokens: options?.maxTokens,
},
// Provider context
providerContext: {
modelName: this.modelName,
providerName: this.providerName,
projectId: this.projectId,
location: this.location,
defaultTimeout: this.defaultTimeout,
},
// Network environment
networkEnvironment: {
httpProxy: process.env.HTTP_PROXY || process.env.http_proxy || "NOT_SET",
httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy || "NOT_SET",
googleAppCreds: process.env.GOOGLE_APPLICATION_CREDENTIALS || "NOT_SET",
hasGoogleServiceKey: !!process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
expectedEndpoint: `https://${this.location}-aiplatform.googleapis.com`,
proxyConfigured: !!(process.env.HTTP_PROXY ||
process.env.HTTPS_PROXY ||
process.env.http_proxy ||
process.env.https_proxy),
},
// Performance baseline
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage(),
message: "Stream execution starting with comprehensive analysis",
});
}
/**
* Log timeout setup process
*/
logTimeoutSetup(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, timeoutSetupStartTime, timeout) {
logger.debug(`[GoogleVertexProvider] ⏰ LOG_POINT_S002_TIMEOUT_SETUP`, {
logPoint: "S002_TIMEOUT_SETUP",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
timeoutSetupStartTimeNs: timeoutSetupStartTime.toString(),
timeout,
providerName: this.providerName,
streamType: "stream",
message: "Setting up timeout controller for stream execution",
});
}
/**
* Log successful timeout setup
*/
logTimeoutSetupSuccess(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, timeoutSetupStartTime, timeoutController, timeout) {
const timeoutSetupEndTime = process.hrtime.bigint();
const timeoutSetupDurationNs = timeoutSetupEndTime - timeoutSetupStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_S003_TIMEOUT_SETUP_SUCCESS`, {
logPoint: "S003_TIMEOUT_SETUP_SUCCESS",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
timeoutSetupDurationNs: timeoutSetupDurationNs.toString(),
timeoutSetupDurationMs: Number(timeoutSetupDurationNs) / 1000000,
hasTimeoutController: !!timeoutController,
timeoutValue: timeout,
message: "Timeout controller setup completed",
});
}
/**
* Log and perform stream options validation
*/
logAndValidateStreamOptions(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, options) {
const validationStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] ✔️ LOG_POINT_S004_VALIDATION_START`, {
logPoint: "S004_VALIDATION_START",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
validationStartTimeNs: validationStartTime.toString(),
message: "Starting stream options validation",
});
this.validateStreamOptions(options);
const validationEndTime = process.hrtime.bigint();
const validationDurationNs = validationEndTime - validationStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_S005_VALIDATION_SUCCESS`, {
logPoint: "S005_VALIDATION_SUCCESS",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
validationDurationNs: validationDurationNs.toString(),
validationDurationMs: Number(validationDurationNs) / 1000000,
message: "Stream options validation successful",
});
}
/**
* Log start of message building process
*/
logMessageBuildStart(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart) {
const messagesBuildStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 📝 LOG_POINT_S006_MESSAGES_BUILD_START`, {
logPoint: "S006_MESSAGES_BUILD_START",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
messagesBuildStartTimeNs: messagesBuildStartTime.toString(),
message: "Starting message array building",
});
return messagesBuildStartTime;
}
/**
* Log successful message building
*/
logMessageBuildSuccess(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, messagesBuildStartTime, messages) {
const messagesBuildEndTime = process.hrtime.bigint();
const messagesBuildDurationNs = messagesBuildEndTime - messagesBuildStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_S007_MESSAGES_BUILD_SUCCESS`, {
logPoint: "S007_MESSAGES_BUILD_SUCCESS",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
messagesBuildDurationNs: messagesBuildDurationNs.toString(),
messagesBuildDurationMs: Number(messagesBuildDurationNs) / 1000000,
messagesCount: Array.isArray(messages) ? messages.length : 0,
messagesType: typeof messages,
hasMessages: !!messages,
message: "Message array built successfully",
});
}
async executeStream(options, analysisSchema) {
// Initialize stream execution tracking
const streamExecutionId = `vertex-stream-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
const streamExecutionStartTime = Date.now();
const streamExecutionHrTimeStart = process.hrtime.bigint();
const functionTag = "GoogleVertexProvider.executeStream";
let chunkCount = 0;
// Log stream execution start
this.logStreamExecutionStart(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, functionTag, options, analysisSchema);
// Setup timeout controller
const timeoutSetupStartTime = process.hrtime.bigint();
const timeout = this.getTimeout(options);
this.logTimeoutSetup(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, timeoutSetupStartTime, timeout);
const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
this.logTimeoutSetupSuccess(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, timeoutSetupStartTime, timeoutController, timeout);
try {
// Validate stream options with logging
this.logAndValidateStreamOptions(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, options);
// Build messages with logging
const messagesBuildStartTime = this.logMessageBuildStart(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart);
// Build message array from options
const messages = buildMessagesArray(options);
this.logMessageBuildSuccess(streamExecutionId, streamExecutionStartTime, streamExecutionHrTimeStart, messagesBuildStartTime, messages);
// Log stream request details
logger.debug(`[GoogleVertexProvider] 🚀 LOG_POINT_S008_STREAM_REQUEST_DETAILS`, {
logPoint: "S008_STREAM_REQUEST_DETAILS",
streamExecutionId,
streamRequestDetails: {
modelName: this.modelName,
promptLength: options.input.text.length,
hasSchema: !!analysisSchema,
messagesCount: Array.isArray(messages) ? messages.length : 0,
temperature: options?.temperature,
maxTokens: options?.maxTokens,
disableTools: options?.disableTools || false,
},
message: "Starting comprehensive stream request processing",
});
// 🚀 EXHAUSTIVE LOGGING POINT S009: MODEL CREATION FOR STREAM
const modelCreationStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🏭 LOG_POINT_S009_MODEL_CREATION_FOR_STREAM`, {
logPoint: "S009_MODEL_CREATION_FOR_STREAM",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
modelCreationStartTimeNs: modelCreationStartTime.toString(),
requestedModel: this.modelName,
message: "Starting model creation for stream execution (this will include network setup)",
});
const model = await this.getModel(); // This is where network connection happens!
const modelCreationEndTime = process.hrtime.bigint();
const modelCreationDurationNs = modelCreationEndTime - modelCreationStartTime;
logger.info(`[GoogleVertexProvider] ✅ LOG_POINT_S010_MODEL_CREATION_SUCCESS`, {
logPoint: "S010_MODEL_CREATION_SUCCESS",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
modelCreationDurationNs: modelCreationDurationNs.toString(),
modelCreationDurationMs: Number(modelCreationDurationNs) / 1000000,
hasModel: !!model,
modelType: typeof model,
message: "Model creation completed successfully - network connection established",
});
// 🚀 EXHAUSTIVE LOGGING POINT S011: TOOLS SETUP FOR STREAMING
const toolsSetupStartTime = process.hrtime.bigint();
logger.debug(`[GoogleVertexProvider] 🛠️ LOG_POINT_S011_TOOLS_SETUP_START`, {
logPoint: "S011_TOOLS_SETUP_START",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
toolsSetupStartTimeNs: toolsSetupStartTime.toString(),
disableTools: options?.disableTools || false,
supportsTools: this.supportsTools(),
message: "Setting up tools for streaming",
});
// Get all available tools (direct + MCP + external) for streaming
const shouldUseTools = !options.disableTools && this.supportsTools();
const tools = shouldUseTools ? await this.getAllTools() : {};
const toolsSetupEndTime = process.hrtime.bigint();
const toolsSetupDurationNs = toolsSetupEndTime - toolsSetupStartTime;
logger.debug(`[GoogleVertexProvider] ✅ LOG_POINT_S012_TOOLS_SETUP_SUCCESS`, {
logPoint: "S012_TOOLS_SETUP_SUCCESS",
streamExecutionId,
timestamp: new Date().toISOString(),
elapsedMs: Date.now() - streamExecutionStartTime,
elapsedNs: (process.hrtime.bigint() - streamExecutionHrTimeStart).toString(),
toolsSetupDurationNs: toolsSetupDurationNs.toString(),
toolsSetupDurationMs: Number(toolsSetupDurationNs) / 1000000,
shouldUseTools,
toolCount: Object.keys(tools).length,
toolNames: Object.keys(tools),
hasTools: Object.keys(tools).length > 0,
message: "Tools setup completed for streaming",
});
logger.debug(`${functionTag}: Tools for streaming`, {
shouldUseTools,
toolCount: Object.keys(tools).length,
toolNames: Object.keys(tools),
});
// Model-specific maxTokens handling
const modelName = this.modelName || getDefaultVertexModel();
// Use cached model configuration to determine maxTokens handling for streaming performance
// This avoids hardcoded model-specific logic and repeated config lookups
const shouldSetMaxTokens = this.shouldSetMaxTokensCached(modelName);
const maxTokens = shouldSetMaxTokens
? options.maxTokens || DEFAULT_MAX_TOKENS
: undefined;
// Build complete stream options with proper typing
let streamOptions = {
model: model,
messages: messages,
temperature: options.temperature,
...(maxTokens && { maxTokens }),
// Add tools support for streaming
...(shouldUseTools &&