Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions firestore-translate-text/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.1.31

feat - add Gemini 3.6 Flash, 3.5 Flash, 3.5 Flash Lite, 3.1 Flash Lite, and 3.1 Pro Preview model options; default to Gemini 3.6 Flash. Gemini 2.5 models retire in October 2026.

fix - send Vertex AI translation requests to the `global` endpoint instead of the Cloud Functions location. Gemini 3.x is only served from the `global`, `us` and `eu` endpoints, so a single-region endpoint fails for the new default model.

## Version 0.1.30

chore: remove unused rimraf dependency
Expand Down
2 changes: 1 addition & 1 deletion firestore-translate-text/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ To install an extension, your project must be on the [Blaze (pay as you go) plan
* Translation Provider: Choose the translation provider to use for this extension. "Cloud Translation API" uses the standard Google Cloud Translation service (fast, cost-effective). "Gemini (Google AI)" leverages Google's Gemini models via Google AI Studio for more accurate and context-aware translations (requires Gemini API access and API key). "Gemini (Vertex AI)" uses Gemini models through Vertex AI in your Google Cloud project (requires Vertex AI access).


* Gemini Model: Choose the Gemini model to use for translations. Consider model pricing, performance, and availability in your selected provider. This is only required if you select "AI Translations Using Gemini" as your translation model. By default, the extension uses Gemini 2.5 Flash for a balance of speed and cost.
* Gemini Model: Choose the Gemini model to use for translations. Consider model pricing, performance, and availability in your selected provider. This is only required if you select "AI Translations Using Gemini" as your translation model. By default, the extension uses Gemini 3.6 Flash for a balance of speed and cost. Gemini 2.5 models retire in October 2026. Note: with the Vertex AI provider, translation requests are sent to the Vertex AI 'global' endpoint rather than the Cloud Functions location, because Gemini 3.x models are only served from the 'global', 'us' and 'eu' endpoints.


* Google AI API Key: If you selected "AI Translations Using Gemini" and "Google AI" as the provider, provide your Google AI API key here. You can create an API key at: https://ai.google.dev/gemini-api/docs/api-key. This is not required if you use Vertex AI as the provider.
Expand Down
33 changes: 24 additions & 9 deletions firestore-translate-text/extension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

name: firestore-translate-text
version: 0.1.30
version: 0.1.31
specVersion: v1beta

tags: [ai]
Expand Down Expand Up @@ -158,21 +158,36 @@ params:
Choose the Gemini model to use for translations. Consider model pricing,
performance, and availability in your selected provider. This is only
required if you select "AI Translations Using Gemini" as your translation
model. By default, the extension uses Gemini 2.5 Flash for a balance of
speed and cost.
model. By default, the extension uses Gemini 3.6 Flash for a balance of
speed and cost. Gemini 2.5 models retire in October 2026. Note: with the
Vertex AI provider, translation requests are sent to the Vertex AI 'global'
endpoint rather than the Cloud Functions location, because Gemini 3.x
models are only served from the 'global', 'us' and 'eu' endpoints.
type: select
required: false
options:
- label:
Gemini 2.5 Pro (highest quality, expensive, large max output size)
value: gemini-2.5-pro
Gemini 3.1 Pro Preview (highest quality, expensive, large max output
size)
value: gemini-3.1-pro-preview
- label:
Gemini 2.5 Flash (cost-effective, high quality, large max output size)
value: gemini-2.5-flash
Gemini 3.6 Flash (cost-effective, high quality, large max output size)
value: gemini-3.6-flash
- label: Gemini 3.5 Flash (high quality, large max output size)
value: gemini-3.5-flash
- label:
Gemini 3.5 Flash Lite (cheap, good quality, large max output size)
value: gemini-3.5-flash-lite
- label:
Gemini 2.5 Flash Lite (cheap, good quality, large max output size)
Gemini 3.1 Flash Lite (cheap, good quality, large max output size)
value: gemini-3.1-flash-lite
- label: Gemini 2.5 Pro (retires October 2026)
value: gemini-2.5-pro
- label: Gemini 2.5 Flash (retires October 2026)
value: gemini-2.5-flash
- label: Gemini 2.5 Flash Lite (retires October 2026)
value: gemini-2.5-flash-lite
default: gemini-2.5-flash
default: gemini-3.6-flash

- param: GOOGLE_AI_API_KEY
label: Google AI API Key
Expand Down
2 changes: 1 addition & 1 deletion firestore-translate-text/functions/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ export default {
languagesFieldName: process.env.LANGUAGES_FIELD_NAME,
useGenkit: process.env.TRANSLATION_PROVIDER.includes("gemini"),
geminiProvider: process.env.TRANSLATION_PROVIDER.split("-")[1],
geminiModel: process.env.GEMINI_MODEL || "gemini-2.5-flash",
geminiModel: process.env.GEMINI_MODEL || "gemini-3.6-flash",
googleAIAPIKey: process.env.GOOGLE_AI_API_KEY,
};
13 changes: 12 additions & 1 deletion firestore-translate-text/functions/src/translate/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ export class GoogleTranslator implements ITranslator {
}
}

/**
* Vertex AI location used for Gemini translations.
*
* Gemini 3.x is served from the `global`, `us` and `eu` endpoints only, so the
* Cloud Functions location is not a usable default: a single region such as
* `us-central1` returns a 404 for `gemini-3.6-flash`.
*
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations
*/
const VERTEX_LOCATION = "global";

/**
* Implementation of ITranslator using Genkit with either Vertex AI or Google AI
*/
Expand Down Expand Up @@ -114,7 +125,7 @@ export class GenkitTranslator implements ITranslator {

const plugins =
plugin === "vertexai"
? [vertexAI({ location: process.env.LOCATION! })]
? [vertexAI({ location: VERTEX_LOCATION })]
: [googleAI({ apiKey: config.googleAIAPIKey })];

this.client = genkit({
Expand Down
6 changes: 6 additions & 0 deletions storage-resize-images/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.3.7

chore - switch content filter from `gemini-2.5-flash` to `gemini-3.1-flash-lite` (similar price to 2.5 Flash and better quality; 2.5 Flash retires October 2026)

fix - content filter now calls Vertex at `global` via `@genkit-ai/google-genai`. `gemini-3.1-flash-lite` is not served on regional endpoints like `us-central1`, and the old `@genkit-ai/vertexai` plugin rejects `global`.

## Version 0.3.6

chore: replace temp file helper dependencies with Node.js built-ins
Expand Down
2 changes: 1 addition & 1 deletion storage-resize-images/extension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

name: storage-resize-images
version: 0.3.6
version: 0.3.7
specVersion: v1beta

displayName: Resize Images
Expand Down
36 changes: 17 additions & 19 deletions storage-resize-images/functions/__tests__/content-filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import { checkImageContent } from "../src/content-filter";
import * as path from "path";
import { HarmBlockThreshold } from "@google-cloud/vertexai";
import { ValidationError } from "@genkit-ai/core/schema";

// Mock genkit module
Expand All @@ -28,11 +27,10 @@ jest.mock("genkit", () => ({
},
}));

// Mock vertexAI module
jest.mock("@genkit-ai/vertexai", () => ({
__esModule: true,
default: jest.fn(),
gemini: jest.fn((version: string) => ({ name: `vertexai/${version}` })),
jest.mock("@genkit-ai/google-genai", () => ({
vertexAI: Object.assign(jest.fn(), {
model: jest.fn((version: string) => ({ name: `vertexai/${version}` })),
}),
}));

// Mock logs so we can assert on which filter-blocked log fired.
Expand Down Expand Up @@ -82,7 +80,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
"BLOCK_MEDIUM_AND_ABOVE",
null,
"image/png"
);
Expand All @@ -107,7 +105,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
"Is this image containing inappropriate content?",
"image/png"
);
Expand All @@ -133,7 +131,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
"BLOCK_MEDIUM_AND_ABOVE",
null,
"image/png",
1
Expand All @@ -155,7 +153,7 @@ describe("checkImageContent with mocks", () => {
await expect(
checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
"BLOCK_MEDIUM_AND_ABOVE",
null,
"image/png",
1
Expand All @@ -176,7 +174,7 @@ describe("checkImageContent with mocks", () => {

await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
"BLOCK_MEDIUM_AND_ABOVE",
null,
"image/png"
);
Expand All @@ -187,7 +185,7 @@ describe("checkImageContent with mocks", () => {

// Check basic structure without checking exact values of complex objects
expect(callArgs.model?.name ?? callArgs.model).toBe(
"vertexai/gemini-2.5-flash"
"vertexai/gemini-3.1-flash-lite"
);
expect(callArgs.messages[0].role).toBe("user");
expect(callArgs.messages[0].content[0].text).toBe(
Expand All @@ -213,7 +211,7 @@ describe("checkImageContent with mocks", () => {

await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
"BLOCK_MEDIUM_AND_ABOVE",
customPrompt,
"image/png"
);
Expand Down Expand Up @@ -244,7 +242,7 @@ describe("checkImageContent with mocks", () => {

let result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
null,
"image/png"
);
Expand All @@ -268,7 +266,7 @@ describe("checkImageContent with mocks", () => {

result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_ONLY_HIGH,
"BLOCK_ONLY_HIGH",
null,
"image/png"
);
Expand Down Expand Up @@ -305,7 +303,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
"Is this image inappropriate?",
"image/png"
);
Expand Down Expand Up @@ -334,7 +332,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
"Is this image inappropriate?",
"image/png"
);
Expand Down Expand Up @@ -362,7 +360,7 @@ describe("checkImageContent with mocks", () => {

const result = await checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
"prompt",
"image/png"
);
Expand All @@ -383,7 +381,7 @@ describe("checkImageContent with mocks", () => {
await expect(
checkImageContent(
imagePath,
HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
"BLOCK_LOW_AND_ABOVE",
"prompt",
"image/png",
3
Expand Down
Loading
Loading