diff --git a/packages/cli/src/ui/hooks/usePrivacySettings.ts b/packages/cli/src/ui/hooks/usePrivacySettings.ts index 64a9673812..7bf5a5ff1b 100644 --- a/packages/cli/src/ui/hooks/usePrivacySettings.ts +++ b/packages/cli/src/ui/hooks/usePrivacySettings.ts @@ -10,6 +10,7 @@ import { type CodeAssistServer, UserTierId, getCodeAssistServer, + debugLogger, } from '@google/gemini-cli-core'; export interface PrivacyState { @@ -103,7 +104,12 @@ async function getRemoteDataCollectionOptIn( ): Promise { try { const resp = await server.getCodeAssistGlobalUserSetting(); - return resp.freeTierDataCollectionOptin; + if (resp.freeTierDataCollectionOptin === undefined) { + debugLogger.warn( + 'Warning: Code Assist API did not return freeTierDataCollectionOptin. Defaulting to true.', + ); + } + return resp.freeTierDataCollectionOptin ?? true; } catch (error: unknown) { if (error && typeof error === 'object' && 'response' in error) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion @@ -128,5 +134,10 @@ async function setRemoteDataCollectionOptIn( cloudaicompanionProject: server.projectId, freeTierDataCollectionOptin: optIn, }); - return resp.freeTierDataCollectionOptin; + if (resp.freeTierDataCollectionOptin === undefined) { + debugLogger.warn( + `Warning: Code Assist API did not return freeTierDataCollectionOptin. Defaulting to ${optIn}.`, + ); + } + return resp.freeTierDataCollectionOptin ?? optIn; } diff --git a/packages/core/src/code_assist/converter.test.ts b/packages/core/src/code_assist/converter.test.ts index 21fecec547..674bbaf70e 100644 --- a/packages/core/src/code_assist/converter.test.ts +++ b/packages/core/src/code_assist/converter.test.ts @@ -246,7 +246,7 @@ describe('converter', () => { }; const genaiRes = fromGenerateContentResponse(codeAssistRes); expect(genaiRes).toBeInstanceOf(GenerateContentResponse); - expect(genaiRes.candidates).toEqual(codeAssistRes.response.candidates); + expect(genaiRes.candidates).toEqual(codeAssistRes.response!.candidates); }); it('should handle prompt feedback and usage metadata', () => { @@ -266,10 +266,10 @@ describe('converter', () => { }; const genaiRes = fromGenerateContentResponse(codeAssistRes); expect(genaiRes.promptFeedback).toEqual( - codeAssistRes.response.promptFeedback, + codeAssistRes.response!.promptFeedback, ); expect(genaiRes.usageMetadata).toEqual( - codeAssistRes.response.usageMetadata, + codeAssistRes.response!.usageMetadata, ); }); @@ -296,7 +296,7 @@ describe('converter', () => { }; const genaiRes = fromGenerateContentResponse(codeAssistRes); expect(genaiRes.automaticFunctionCallingHistory).toEqual( - codeAssistRes.response.automaticFunctionCallingHistory, + codeAssistRes.response!.automaticFunctionCallingHistory, ); }); diff --git a/packages/core/src/code_assist/converter.ts b/packages/core/src/code_assist/converter.ts index 1d41101f31..fc163a8f02 100644 --- a/packages/core/src/code_assist/converter.ts +++ b/packages/core/src/code_assist/converter.ts @@ -27,6 +27,7 @@ import type { ToolConfig, } from '@google/genai'; import { GenerateContentResponse } from '@google/genai'; +import { debugLogger } from '../utils/debugLogger.js'; export interface CAGenerateContentRequest { model: string; @@ -72,12 +73,12 @@ interface VertexGenerationConfig { } export interface CaGenerateContentResponse { - response: VertexGenerateContentResponse; + response?: VertexGenerateContentResponse; traceId?: string; } interface VertexGenerateContentResponse { - candidates: Candidate[]; + candidates?: Candidate[]; automaticFunctionCallingHistory?: Content[]; promptFeedback?: GenerateContentResponsePromptFeedback; usageMetadata?: GenerateContentResponseUsageMetadata; @@ -94,7 +95,7 @@ interface VertexCountTokenRequest { } export interface CaCountTokenResponse { - totalTokens: number; + totalTokens?: number; } export function toCountTokenRequest( @@ -111,8 +112,13 @@ export function toCountTokenRequest( export function fromCountTokenResponse( res: CaCountTokenResponse, ): CountTokensResponse { + if (res.totalTokens === undefined) { + debugLogger.warn( + 'Warning: Code Assist API did not return totalTokens. Defaulting to 0.', + ); + } return { - totalTokens: res.totalTokens, + totalTokens: res.totalTokens ?? 0, }; } diff --git a/packages/core/src/code_assist/setup.ts b/packages/core/src/code_assist/setup.ts index 895fabb6bc..dce96b9cdd 100644 --- a/packages/core/src/code_assist/setup.ts +++ b/packages/core/src/code_assist/setup.ts @@ -18,6 +18,7 @@ import type { AuthClient } from 'google-auth-library'; import type { ValidationHandler } from '../fallback/types.js'; import { ChangeAuthRequestedError } from '../utils/errors.js'; import { ValidationRequiredError } from '../utils/googleQuotaErrors.js'; +import { debugLogger } from '../utils/debugLogger.js'; export class ProjectIdRequiredError extends Error { constructor() { @@ -130,11 +131,20 @@ export async function setupUser( } if (loadRes.currentTier) { + if (!loadRes.paidTier?.id && !loadRes.currentTier.id) { + debugLogger.warn( + 'Warning: Code Assist API did not return a user tier ID. Defaulting to STANDARD tier.', + ); + } + if (!loadRes.cloudaicompanionProject) { if (projectId) { return { projectId, - userTier: loadRes.paidTier?.id ?? loadRes.currentTier.id, + userTier: + loadRes.paidTier?.id ?? + loadRes.currentTier.id ?? + UserTierId.STANDARD, userTierName: loadRes.paidTier?.name ?? loadRes.currentTier.name, }; } @@ -144,13 +154,20 @@ export async function setupUser( } return { projectId: loadRes.cloudaicompanionProject, - userTier: loadRes.paidTier?.id ?? loadRes.currentTier.id, + userTier: + loadRes.paidTier?.id ?? loadRes.currentTier.id ?? UserTierId.STANDARD, userTierName: loadRes.paidTier?.name ?? loadRes.currentTier.name, }; } const tier = getOnboardTier(loadRes); + if (!tier.id) { + debugLogger.warn( + 'Warning: Code Assist API did not return an onboarding tier ID. Defaulting to STANDARD tier.', + ); + } + let onboardReq: OnboardUserRequest; if (tier.id === UserTierId.FREE) { // The free tier uses a managed google cloud project. Setting a project in the `onboardUser` request causes a `Precondition Failed` error. @@ -183,7 +200,7 @@ export async function setupUser( if (projectId) { return { projectId, - userTier: tier.id, + userTier: tier.id ?? UserTierId.STANDARD, userTierName: tier.name, }; } @@ -193,7 +210,7 @@ export async function setupUser( return { projectId: lroRes.response.cloudaicompanionProject.id, - userTier: tier.id, + userTier: tier.id ?? UserTierId.STANDARD, userTierName: tier.name, }; } diff --git a/packages/core/src/code_assist/types.ts b/packages/core/src/code_assist/types.ts index 0e2f353aa3..79932efc02 100644 --- a/packages/core/src/code_assist/types.ts +++ b/packages/core/src/code_assist/types.ts @@ -60,7 +60,7 @@ export interface LoadCodeAssistResponse { * GeminiUserTier reflects the structure received from the CodeAssist when calling LoadCodeAssist. */ export interface GeminiUserTier { - id: UserTierId; + id?: UserTierId; name?: string; description?: string; // This value is used to declare whether a given tier requires the user to configure the project setting on the IDE settings or not. @@ -79,10 +79,10 @@ export interface GeminiUserTier { * @param tierName name of the tier. */ export interface IneligibleTier { - reasonCode: IneligibleTierReasonCode; - reasonMessage: string; - tierId: UserTierId; - tierName: string; + reasonCode?: IneligibleTierReasonCode; + reasonMessage?: string; + tierId?: UserTierId; + tierName?: string; validationErrorMessage?: string; validationUrl?: string; validationUrlLinkText?: string; @@ -127,7 +127,7 @@ export type UserTierId = (typeof UserTierId)[keyof typeof UserTierId] | string; * privacy notice. */ export interface PrivacyNotice { - showNotice: boolean; + showNotice?: boolean; noticeText?: string; } @@ -145,7 +145,7 @@ export interface OnboardUserRequest { * http://google3/google/longrunning/operations.proto;rcl=698857719;l=107 */ export interface LongRunningOperationResponse { - name: string; + name?: string; done?: boolean; response?: OnboardUserResponse; } @@ -157,8 +157,8 @@ export interface LongRunningOperationResponse { export interface OnboardUserResponse { // tslint:disable-next-line:enforce-name-casing This is the name of the field in the proto. cloudaicompanionProject?: { - id: string; - name: string; + id?: string; + name?: string; }; } @@ -195,7 +195,7 @@ export interface SetCodeAssistGlobalUserSettingRequest { export interface CodeAssistGlobalUserSettingResponse { cloudaicompanionProject?: string; - freeTierDataCollectionOptin: boolean; + freeTierDataCollectionOptin?: boolean; } /**