Skip to content

Commit 7bb750c

Browse files
committed
Add test for instance identifier helpers
1 parent 7b06106 commit 7bb750c

File tree

7 files changed

+164
-11
lines changed

7 files changed

+164
-11
lines changed

packages/vertexai/src/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function getVertexAI(
7575

7676
const identifier = encodeInstanceIdentifier({
7777
backendType: BackendType.VERTEX_AI,
78-
location: options?.location ?? ''
78+
location: options?.location ?? DEFAULT_LOCATION
7979
});
8080
return genAIProvider.getImmediate({
8181
identifier

packages/vertexai/src/errors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ export class GenAIError extends FirebaseError {
3838
readonly customErrorData?: CustomErrorData
3939
) {
4040
// Match error format used by FirebaseError from ErrorFactory
41-
const service = VERTEX_TYPE;
42-
const serviceName = 'VertexAI'; // TODO: Rename to GenAI on breaking release.
41+
const service = VERTEX_TYPE; // TODO (v12): Rename to GENAI_TYPE
42+
const serviceName = 'VertexAI'; // TODO (v12): Rename to GenAI on breaking release.
4343
const fullCode = `${service}/${code}`;
4444
const fullMessage = `${serviceName}: ${message} (${fullCode})`;
4545
super(code, fullMessage);
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { expect } from 'chai';
18+
import { GENAI_TYPE } from './constants';
19+
import {
20+
encodeInstanceIdentifier,
21+
decodeInstanceIdentifier
22+
} from './helpers';
23+
import { GenAIError } from './errors';
24+
import { BackendType, InstanceIdentifier } from './public-types';
25+
import { GenAIErrorCode } from './types';
26+
27+
describe('Identifier Encoding/Decoding', () => {
28+
describe('encodeInstanceIdentifier', () => {
29+
it('should encode Vertex AI identifier with a specific location', () => {
30+
const identifier: InstanceIdentifier = {
31+
backendType: BackendType.VERTEX_AI,
32+
location: 'us-central1'
33+
};
34+
console.log(identifier);
35+
const expected = `${GENAI_TYPE}/vertexai/us-central1`;
36+
expect(encodeInstanceIdentifier(identifier)).to.equal(expected);
37+
});
38+
39+
it('should encode Vertex AI identifier using empty location', () => {
40+
const identifier: InstanceIdentifier = {
41+
backendType: BackendType.VERTEX_AI,
42+
location: ""
43+
};
44+
const expected = `${GENAI_TYPE}/vertexai/`;
45+
expect(encodeInstanceIdentifier(identifier)).to.equal(expected);
46+
});
47+
48+
it('should encode Google AI identifier', () => {
49+
const identifier: InstanceIdentifier = {
50+
backendType: BackendType.GOOGLE_AI
51+
};
52+
const expected = `${GENAI_TYPE}/googleai`;
53+
expect(encodeInstanceIdentifier(identifier)).to.equal(expected);
54+
});
55+
56+
it('should throw GenAIError for unknown backend type', () => {
57+
const identifier = {
58+
backendType: 'some-future-backend'
59+
} as any; // bypass type checking for the test
60+
61+
expect(() => encodeInstanceIdentifier(identifier)).to.throw(GenAIError);
62+
63+
try {
64+
encodeInstanceIdentifier(identifier);
65+
expect.fail('Expected encodeInstanceIdentifier to throw');
66+
} catch (e) {
67+
expect(e).to.be.instanceOf(GenAIError);
68+
const error = e as GenAIError;
69+
expect(error.message).to.contain(`Unknown backend`);
70+
expect(error.code).to.equal(GenAIErrorCode.ERROR);
71+
}
72+
});
73+
});
74+
75+
describe('decodeInstanceIdentifier', () => {
76+
it('should decode Vertex AI identifier with location', () => {
77+
const encoded = `${GENAI_TYPE}/vertexai/europe-west1`;
78+
const expected: InstanceIdentifier = {
79+
backendType: BackendType.VERTEX_AI,
80+
location: 'europe-west1'
81+
};
82+
expect(decodeInstanceIdentifier(encoded)).to.deep.equal(expected);
83+
});
84+
85+
it('should throw an error if Vertex AI identifier string without explicit location part', () => {
86+
const encoded = `${GENAI_TYPE}/vertexai`;
87+
expect(() => decodeInstanceIdentifier(encoded)).to.throw(GenAIError);
88+
89+
try {
90+
decodeInstanceIdentifier(encoded);
91+
expect.fail('Expected encodeInstanceIdentifier to throw');
92+
} catch (e) {
93+
expect(e).to.be.instanceOf(GenAIError);
94+
const error = e as GenAIError;
95+
expect(error.message).to.contain(`Invalid instance identifier, unknown location`);
96+
expect(error.code).to.equal(GenAIErrorCode.ERROR);
97+
}
98+
});
99+
100+
it('should decode Google AI identifier', () => {
101+
const encoded = `${GENAI_TYPE}/googleai`;
102+
const expected: InstanceIdentifier = {
103+
backendType: BackendType.GOOGLE_AI
104+
};
105+
expect(decodeInstanceIdentifier(encoded)).to.deep.equal(expected);
106+
});
107+
108+
it('should throw GenAIError for invalid backend string', () => {
109+
const encoded = `${GENAI_TYPE}/someotherbackend/location`;
110+
expect(() => decodeInstanceIdentifier(encoded)).to.throw(
111+
GenAIError,
112+
`Invalid instance identifier string: '${encoded}'`
113+
);
114+
try {
115+
decodeInstanceIdentifier(encoded);
116+
expect.fail('Expected decodeInstanceIdentifier to throw');
117+
} catch (e) {
118+
expect(e).to.be.instanceOf(GenAIError);
119+
expect((e as GenAIError).code).to.equal(GenAIErrorCode.ERROR);
120+
}
121+
});
122+
123+
it('should throw GenAIError for malformed identifier string (too few parts)', () => {
124+
const encoded = GENAI_TYPE;
125+
expect(() => decodeInstanceIdentifier(encoded)).to.throw(
126+
GenAIError,
127+
`Invalid instance identifier string: '${encoded}'`
128+
);
129+
});
130+
131+
it('should throw GenAIError for malformed identifier string (incorrect prefix)', () => {
132+
const encoded = 'firebase/vertexai/location';
133+
// This will also hit the default case in the switch statement
134+
expect(() => decodeInstanceIdentifier(encoded)).to.throw(
135+
GenAIError,
136+
`Invalid instance identifier, unknown prefix 'firebase'`
137+
);
138+
});
139+
});
140+
});

packages/vertexai/src/helpers.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,53 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { DEFAULT_LOCATION } from './constants';
18+
import { GENAI_TYPE } from './constants';
1919
import { GenAIError } from './errors';
2020
import { BackendType, InstanceIdentifier } from './public-types';
2121
import { GenAIErrorCode } from './types';
2222

2323
/**
24+
* Encodes an {@link InstanceIdentifier} into a string.
25+
*
26+
* This string is used to identify unique {@link GenAI} instances by backend type.
27+
*
2428
* @internal
2529
*/
2630
export function encodeInstanceIdentifier(
2731
instanceIdentifier: InstanceIdentifier
2832
): string {
2933
switch (instanceIdentifier.backendType) {
3034
case BackendType.VERTEX_AI:
31-
return `genai/vertexai/${location || DEFAULT_LOCATION}`;
35+
return `${GENAI_TYPE}/vertexai/${instanceIdentifier.location}`;
3236
case BackendType.GOOGLE_AI:
33-
return 'genai/googleai';
37+
return `${GENAI_TYPE}/googleai`;
3438
default:
3539
throw new GenAIError(
3640
GenAIErrorCode.ERROR,
37-
`An internal error occured: Unknown Backend ${instanceIdentifier}. Please submit an issue at https://github.com/firebase/firebase-js-sdk.`
41+
`Unknown backend '${instanceIdentifier}'`
3842
);
3943
}
4044
}
4145

4246
/**
47+
* Decodes an instance identifier string into an {@link InstanceIdentifier}.
48+
*
4349
* @internal
4450
*/
4551
export function decodeInstanceIdentifier(
4652
instanceIdentifier: string
4753
): InstanceIdentifier {
4854
const identifierParts = instanceIdentifier.split('/');
55+
if (identifierParts[0] !== GENAI_TYPE) {
56+
throw new GenAIError(GenAIErrorCode.ERROR, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
57+
}
4958
const backend = identifierParts[1];
5059
switch (backend) {
5160
case 'vertexai':
52-
const location: string | undefined = identifierParts[1]; // The location may not be a part of the instance identifier
61+
const location: string | undefined = identifierParts[2];
62+
if (!location) {
63+
throw new GenAIError(GenAIErrorCode.ERROR, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
64+
}
5365
return {
5466
backendType: BackendType.VERTEX_AI,
5567
location
@@ -61,7 +73,7 @@ export function decodeInstanceIdentifier(
6173
default:
6274
throw new GenAIError(
6375
GenAIErrorCode.ERROR,
64-
`An internal error occured: Invalid instance identifier: ${instanceIdentifier}. Please submit an issue at https://github.com/firebase/firebase-js-sdk`
76+
`Invalid instance identifier string: '${instanceIdentifier}'`
6577
);
6678
}
6779
}

packages/vertexai/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { GENAI_TYPE } from './constants';
99
import { Component, ComponentType } from '@firebase/component';
1010
import { name, version } from '../package.json';
1111
import { decodeInstanceIdentifier } from './helpers';
12-
import { GenAIError, VertexAIError } from './api';
12+
import { GenAIError } from './api';
1313
import { GenAIErrorCode } from './types';
1414

1515
declare global {

packages/vertexai/src/models/genai-model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export abstract class GenAIModel {
122122

123123
private static normalizeDeveloperApiModelName(modelName: string): string {
124124
return `models/${modelName}`;
125+
// TODO (dlarocque): rename this to be projects/project-id/models/model-name
125126
}
126127

127128
private static normalizeVertexAIModelName(modelName: string): string {

packages/vertexai/src/types/developerAPI/requests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { GenerationConfig, Tool } from '../requests';
2020

2121
export interface DeveloperAPICountTokensRequest {
2222
generateContentRequest: {
23-
// model: string;
23+
// model: string; // models/model-name
2424
contents: Content[];
2525
systemInstruction?: string | Part | Content;
2626
tools?: Tool[];

0 commit comments

Comments
 (0)