From 1687048f0a8474701d99197e70ed208202ae16e6 Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Mon, 10 Feb 2025 08:57:59 -0500 Subject: [PATCH] fix(js/plugins/google-cloud): pass projectId through to GoogleAuth client --- js/plugins/google-cloud/src/auth.ts | 34 ++++--- js/plugins/google-cloud/src/gcpLogger.ts | 4 +- .../google-cloud/src/gcpOpenTelemetry.ts | 14 +-- js/plugins/google-cloud/src/index.ts | 2 +- js/plugins/google-cloud/src/utils.ts | 19 ++-- js/testapps/dev-ui-gallery/src/genkit.ts | 94 ++++++++----------- 6 files changed, 83 insertions(+), 84 deletions(-) diff --git a/js/plugins/google-cloud/src/auth.ts b/js/plugins/google-cloud/src/auth.ts index 61aced229..fa525466b 100644 --- a/js/plugins/google-cloud/src/auth.ts +++ b/js/plugins/google-cloud/src/auth.ts @@ -29,16 +29,23 @@ import { GcpPrincipal, GcpTelemetryConfig } from './types.js'; * searches for credential files in standard locations, before using this * method. * - * See also: https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables + * @see https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables + * + * @param projectId if provided, will take precendence over projectId from credential */ -export async function credentialsFromEnvironment(): Promise< - Partial -> { +export async function credentialsFromEnvironment( + projectId?: string +): Promise> { let authClient: GoogleAuth; let options: Partial = {}; + if (projectId !== undefined) { + logger.debug(`Using Google Cloud projectId=${projectId}`); + options.projectId = projectId; + } + if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) { - logger.debug('Retrieving credentials from GCLOUD_SERVICE_ACCOUNT_CREDS'); + logger.debug('Using credentials from GCLOUD_SERVICE_ACCOUNT_CREDS'); const serviceAccountCreds = JSON.parse( process.env.GCLOUD_SERVICE_ACCOUNT_CREDS ); @@ -48,14 +55,15 @@ export async function credentialsFromEnvironment(): Promise< } else { authClient = new GoogleAuth(); } + try { - const projectId = await authClient.getProjectId(); - if (projectId && projectId.length > 0) { - options.projectId = projectId; - } + options.projectId ||= await authClient.getProjectId(); } catch (error) { logger.warn(error); } + + logger.debug(`Final projectId=${options.projectId}`); + return options; } @@ -68,8 +76,10 @@ export async function credentialsFromEnvironment(): Promise< * can be handy to get access to the current credential for logging debugging * information or other purposes. **/ -export async function resolveCurrentPrincipal(): Promise { - const envCredentials = await credentialsFromEnvironment(); +export async function resolveCurrentPrincipal( + projectId?: string +): Promise { + const envCredentials = await credentialsFromEnvironment(projectId); let adcCredentials = {} as CredentialBody; try { adcCredentials = await auth.getCredentials(); @@ -83,7 +93,7 @@ export async function resolveCurrentPrincipal(): Promise { envCredentials.credentials?.client_email ?? adcCredentials.client_email; return { - projectId: envCredentials.projectId, + projectId: projectId ?? envCredentials.projectId, serviceAccountEmail, }; } diff --git a/js/plugins/google-cloud/src/gcpLogger.ts b/js/plugins/google-cloud/src/gcpLogger.ts index ff95b821b..fbde368cd 100644 --- a/js/plugins/google-cloud/src/gcpLogger.ts +++ b/js/plugins/google-cloud/src/gcpLogger.ts @@ -55,10 +55,10 @@ export class GcpLogger { transports.push( this.shouldExport(env) ? new LoggingWinston({ - projectId: this.config.projectId, labels: { module: 'genkit' }, prefix: 'genkit', logName: 'genkit_log', + projectId: this.config.projectId, credentials: this.config.credentials, autoRetry: true, defaultCallback: await this.getErrorHandler(), @@ -80,7 +80,7 @@ export class GcpLogger { private async getErrorHandler(): Promise<(err: Error | null) => void> { // only log the first time let instructionsLogged = false; - let helpInstructions = await loggingDeniedHelpText(); + let helpInstructions = await loggingDeniedHelpText(this.config.projectId); return async (err: Error | null) => { // Use the defaultLogger so that logs don't get swallowed by diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index 5b641fbda..bfc3ae0cd 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -123,8 +123,9 @@ export class GcpOpenTelemetry { spanExporter = new AdjustingTraceExporter( this.shouldExportTraces() ? new TraceExporter({ - // Creds for non-GCP environments; otherwise credentials will be - // automatically detected via ADC + // provided projectId should take precedence over env vars, etc + projectId: this.config.projectId, + // creds for non-GCP environments, in lieu of using ADC. credentials: this.config.credentials, }) : new InMemorySpanExporter(), @@ -134,7 +135,7 @@ export class GcpOpenTelemetry { (err) => { return tracingDenied(err); }, - await tracingDeniedHelpText() + await tracingDeniedHelpText(this.config.projectId) ) ); return spanExporter; @@ -186,15 +187,16 @@ export class GcpOpenTelemetry { product: 'genkit', version: GENKIT_VERSION, }, - // Creds for non-GCP environments; otherwise credentials will be - // automatically detected via ADC + // provided projectId should take precedence over env vars, etc + projectId: this.config.projectId, + // creds for non-GCP environments, in lieu of using ADC. credentials: this.config.credentials, }, getErrorHandler( (err) => { return metricsDenied(err); }, - await metricsDeniedHelpText() + await metricsDeniedHelpText(this.config.projectId) ) ) : new InMemoryMetricExporter(AggregationTemporality.DELTA); diff --git a/js/plugins/google-cloud/src/index.ts b/js/plugins/google-cloud/src/index.ts index 14c24cd4c..58fe7aa7e 100644 --- a/js/plugins/google-cloud/src/index.ts +++ b/js/plugins/google-cloud/src/index.ts @@ -46,7 +46,7 @@ export function enableGoogleCloudTelemetry( async function configureGcpPlugin( options?: GcpTelemetryConfigOptions ): Promise { - const envOptions = await credentialsFromEnvironment(); + const envOptions = await credentialsFromEnvironment(options?.projectId); return { projectId: options?.projectId || envOptions.projectId, credentials: options?.credentials || envOptions.credentials, diff --git a/js/plugins/google-cloud/src/utils.ts b/js/plugins/google-cloud/src/utils.ts index 3235ac898..c09bb064e 100644 --- a/js/plugins/google-cloud/src/utils.ts +++ b/js/plugins/google-cloud/src/utils.ts @@ -155,19 +155,22 @@ export function metricsDenied( return requestDenied(err); } -export async function permissionDeniedHelpText(role: string) { - const principal = await resolveCurrentPrincipal(); +export async function permissionDeniedHelpText( + role: string, + projectId?: string +) { + const principal = await resolveCurrentPrincipal(projectId); return `Add the role '${role}' to your Service Account in the IAM & Admin page on the Google Cloud console, or use the following command:\n\ngcloud projects add-iam-policy-binding ${principal.projectId ?? '${PROJECT_ID}'} \\\n --member=serviceAccount:${principal.serviceAccountEmail || '${SERVICE_ACCT}'} \\\n --role=${role}`; } -export async function loggingDeniedHelpText() { - return permissionDeniedHelpText('roles/logging.logWriter'); +export async function loggingDeniedHelpText(projectId?: string) { + return permissionDeniedHelpText('roles/logging.logWriter', projectId); } -export async function tracingDeniedHelpText() { - return permissionDeniedHelpText('roles/cloudtrace.agent'); +export async function tracingDeniedHelpText(projectId?: string) { + return permissionDeniedHelpText('roles/cloudtrace.agent', projectId); } -export async function metricsDeniedHelpText() { - return permissionDeniedHelpText('roles/monitoring.metricWriter'); +export async function metricsDeniedHelpText(projectId?: string) { + return permissionDeniedHelpText('roles/monitoring.metricWriter', projectId); } diff --git a/js/testapps/dev-ui-gallery/src/genkit.ts b/js/testapps/dev-ui-gallery/src/genkit.ts index e61aa9920..3ffff86d4 100644 --- a/js/testapps/dev-ui-gallery/src/genkit.ts +++ b/js/testapps/dev-ui-gallery/src/genkit.ts @@ -18,34 +18,18 @@ import { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; import { genkitEval, GenkitMetric } from '@genkit-ai/evaluator'; import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; -import { textEmbedding004, vertexAI } from '@genkit-ai/vertexai'; -import { - vertexAIEvaluation, - VertexAIEvaluationMetricType, -} from '@genkit-ai/vertexai/evaluation'; -import { - claude35Sonnet, - claude35SonnetV2, - claude3Haiku, - claude3Opus, - claude3Sonnet, - codestral, - llama31, - llama32, - mistralLarge, - mistralNemo, - vertexAIModelGarden, -} from '@genkit-ai/vertexai/modelgarden'; +import { textEmbedding004 } from '@genkit-ai/vertexai'; import { genkit } from 'genkit'; import { logger } from 'genkit/logging'; import { chroma } from 'genkitx-chromadb'; import { ollama } from 'genkitx-ollama'; import { pinecone } from 'genkitx-pinecone'; -logger.setLogLevel('info'); +logger.setLogLevel('debug'); enableFirebaseTelemetry({ - forceDevExport: false, + projectId: 'weather-gen-test-next', + forceDevExport: true, metricExportIntervalMillis: 5_000, metricExportTimeoutMillis: 5_000, autoInstrumentation: true, @@ -95,41 +79,25 @@ export const ai = genkit({ ], serverAddress: 'http://127.0.0.1:11434', // default local address }), - vertexAI({ - location: 'us-central1', - }), - vertexAIModelGarden({ - location: 'us-central1', // gemini, llama - // location: 'us-east5', // anthropic - models: [ - claude35Sonnet, - claude35SonnetV2, - claude3Haiku, - claude3Opus, - claude3Sonnet, - codestral, - llama31, - llama32, - mistralLarge, - mistralNemo, - ], - }), - vertexAIEvaluation({ - location: 'us-central1', - metrics: [ - VertexAIEvaluationMetricType.BLEU, - VertexAIEvaluationMetricType.GROUNDEDNESS, - VertexAIEvaluationMetricType.SAFETY, - { - type: VertexAIEvaluationMetricType.ROUGE, - metricSpec: { - rougeType: 'rougeLsum', - useStemmer: true, - splitSummaries: 'true', - }, - }, - ], - }), + // vertexAI({ + // location: 'us-central1', + // }), + // vertexAIModelGarden({ + // location: 'us-central1', // gemini, llama + // // location: 'us-east5', // anthropic + // models: [ + // claude35Sonnet, + // claude35SonnetV2, + // claude3Haiku, + // claude3Opus, + // claude3Sonnet, + // codestral, + // llama31, + // llama32, + // mistralLarge, + // mistralNemo, + // ], + // }), // vector stores chroma([ @@ -165,5 +133,21 @@ export const ai = genkit({ GenkitMetric.MALICIOUSNESS, ], }), + // vertexAIEvaluation({ + // location: 'us-central1', + // metrics: [ + // VertexAIEvaluationMetricType.BLEU, + // VertexAIEvaluationMetricType.GROUNDEDNESS, + // VertexAIEvaluationMetricType.SAFETY, + // { + // type: VertexAIEvaluationMetricType.ROUGE, + // metricSpec: { + // rougeType: 'rougeLsum', + // useStemmer: true, + // splitSummaries: 'true', + // }, + // }, + // ], + // }), ], });