diff --git a/src/deploy/functions/services/ailogic.spec.ts b/src/deploy/functions/services/ailogic.spec.ts index e47c053107c..13735020962 100644 --- a/src/deploy/functions/services/ailogic.spec.ts +++ b/src/deploy/functions/services/ailogic.spec.ts @@ -1,4 +1,8 @@ -import { AILogicService } from "./ailogic"; +import { + AILogicService, + AI_LOGIC_BEFORE_GENERATE_CONTENT, + AI_LOGIC_AFTER_GENERATE_CONTENT, +} from "./ailogic"; import * as backend from "../backend"; import { expect } from "chai"; import * as ailogicApi from "../../../gcp/ailogic"; @@ -12,9 +16,6 @@ const BASE_EP = { platform: "gcfv2" as const, }; -const BEFORE_GENERATE = "firebase.vertexai.v1beta.beforeGenerateContent"; -const AFTER_GENERATE = "firebase.vertexai.v1beta.afterGenerateContent"; - describe("AILogicService", () => { const service = new AILogicService(); let upsertStub: sinon.SinonStub; @@ -35,7 +36,7 @@ describe("AILogicService", () => { ...BASE_EP, id: "func1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, options: { regionalWebhook: true }, }, }; @@ -43,13 +44,13 @@ describe("AILogicService", () => { ...BASE_EP, id: "func2", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, options: { regionalWebhook: true }, }, }; expect(() => service.validateTrigger(ep1, backend.of(ep1, ep2))).to.throw( - `Can only create at most one regional AI Logic Trigger for ${BEFORE_GENERATE} in region us-central1`, + `Can only create at most one regional AI Logic Trigger for ${AI_LOGIC_BEFORE_GENERATE_CONTENT} in region us-central1`, ); }); @@ -59,7 +60,7 @@ describe("AILogicService", () => { id: "func1", region: "us-central1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, options: { regionalWebhook: true }, }, }; @@ -68,7 +69,7 @@ describe("AILogicService", () => { id: "func2", region: "us-east1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, options: { regionalWebhook: true }, }, }; @@ -81,19 +82,19 @@ describe("AILogicService", () => { ...BASE_EP, id: "func1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, }, }; const ep2: backend.Endpoint = { ...BASE_EP, id: "func2", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, }, }; expect(() => service.validateTrigger(ep1, backend.of(ep1, ep2))).to.throw( - `Can only create at most one global AI Logic Trigger for ${BEFORE_GENERATE}`, + `Can only create at most one global AI Logic Trigger for ${AI_LOGIC_BEFORE_GENERATE_CONTENT}`, ); }); @@ -102,7 +103,7 @@ describe("AILogicService", () => { ...BASE_EP, id: "func1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, options: { regionalWebhook: true }, }, }; @@ -110,7 +111,7 @@ describe("AILogicService", () => { ...BASE_EP, id: "func2", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, }, }; @@ -122,14 +123,14 @@ describe("AILogicService", () => { ...BASE_EP, id: "func1", blockingTrigger: { - eventType: BEFORE_GENERATE, + eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT, }, }; const ep2: backend.Endpoint = { ...BASE_EP, id: "func2", blockingTrigger: { - eventType: AFTER_GENERATE, + eventType: AI_LOGIC_AFTER_GENERATE_CONTENT, }, }; @@ -141,7 +142,7 @@ describe("AILogicService", () => { it("should call upsertBlockingFunction", async () => { const ep: backend.Endpoint = { ...BASE_EP, - blockingTrigger: { eventType: BEFORE_GENERATE }, + blockingTrigger: { eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT }, }; await service.registerTrigger(ep); @@ -154,7 +155,7 @@ describe("AILogicService", () => { it("should call deleteBlockingFunction", async () => { const ep: backend.Endpoint = { ...BASE_EP, - blockingTrigger: { eventType: BEFORE_GENERATE }, + blockingTrigger: { eventType: AI_LOGIC_BEFORE_GENERATE_CONTENT }, }; await service.unregisterTrigger(ep); diff --git a/src/deploy/functions/services/ailogic.ts b/src/deploy/functions/services/ailogic.ts index cf691a34720..321bbc2a2f9 100644 --- a/src/deploy/functions/services/ailogic.ts +++ b/src/deploy/functions/services/ailogic.ts @@ -2,11 +2,11 @@ import * as backend from "../backend"; import { FirebaseError } from "../../../error"; import { Name, Service } from "./index"; import * as ailogicApi from "../../../gcp/ailogic"; - -export const AI_LOGIC_BEFORE_GENERATE_CONTENT = - "firebase.vertexai.v1beta.beforeGenerateContent" as const; -export const AI_LOGIC_AFTER_GENERATE_CONTENT = - "firebase.vertexai.v1beta.afterGenerateContent" as const; +import { + AI_LOGIC_BEFORE_GENERATE_CONTENT, + AI_LOGIC_AFTER_GENERATE_CONTENT, +} from "../../../gcp/ailogic"; +export { AI_LOGIC_BEFORE_GENERATE_CONTENT, AI_LOGIC_AFTER_GENERATE_CONTENT }; export const AI_LOGIC_EVENTS = [ AI_LOGIC_BEFORE_GENERATE_CONTENT, diff --git a/src/deploy/functions/services/index.ts b/src/deploy/functions/services/index.ts index 50a0b5f62f9..b133e17e9a7 100644 --- a/src/deploy/functions/services/index.ts +++ b/src/deploy/functions/services/index.ts @@ -174,8 +174,8 @@ const EVENT_SERVICE_MAPPING: Record = { "google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService, "google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService, "google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService, - "firebase.vertexai.v1beta.beforeGenerateContent": aiLogicService, - "firebase.vertexai.v1beta.afterGenerateContent": aiLogicService, + "google.firebase.ailogic.v1.beforeGenerate": aiLogicService, + "google.firebase.ailogic.v1.afterGenerate": aiLogicService, }; /** diff --git a/src/deploy/functions/validate.spec.ts b/src/deploy/functions/validate.spec.ts index 3f9aa5b7266..877787b2bd8 100644 --- a/src/deploy/functions/validate.spec.ts +++ b/src/deploy/functions/validate.spec.ts @@ -464,6 +464,24 @@ describe("validate", () => { expect(() => validate.endpointsAreValid(want)).to.not.throw(); }); + it("disallows unrecognized blocking trigger types", () => { + const ep: backend.Endpoint = { + platform: "gcfv2", + id: "id", + region: "us-east1", + project: "project", + entryPoint: "func", + runtime: "nodejs16", + blockingTrigger: { + eventType: "google.firebase.ailogic.v1.invalidEvent", + }, + }; + + expect(() => validate.endpointsAreValid(backend.of(ep))).to.throw( + /Unrecognized blocking trigger type: google.firebase.ailogic.v1.invalidEvent. Please update your CLI/, + ); + }); + it("errors for scheduled functions with timeout > 1800s", () => { const ep: backend.Endpoint = { ...ENDPOINT_BASE, diff --git a/src/deploy/functions/validate.ts b/src/deploy/functions/validate.ts index 967377f4736..4f9fee8cce0 100644 --- a/src/deploy/functions/validate.ts +++ b/src/deploy/functions/validate.ts @@ -89,7 +89,16 @@ export function endpointsAreValid(wantBackend: backend.Backend): void { validateTimeoutConfig(endpoints); for (const ep of endpoints) { validateScheduledTimeout(ep); - serviceForEndpoint(ep).validateTrigger(ep, wantBackend); + const service = serviceForEndpoint(ep); + if (backend.isBlockingTriggered(ep)) { + if (service.name === "noop") { + throw new FirebaseError( + `Unrecognized blocking trigger type: ${ep.blockingTrigger.eventType}. Please update your CLI with ${clc.bold("npm install -g firebase-tools@latest")}.`, + { exit: 1 }, + ); + } + } + service.validateTrigger(ep, wantBackend); } // Our SDK doesn't let people articulate this, but it's theoretically possible in the manifest syntax. diff --git a/src/functions/events/v2.ts b/src/functions/events/v2.ts index d846d60f5f6..8dd1fe9d691 100644 --- a/src/functions/events/v2.ts +++ b/src/functions/events/v2.ts @@ -1,4 +1,8 @@ -import { AI_LOGIC_EVENTS } from "../../deploy/functions/services/ailogic"; +import { + AI_LOGIC_EVENTS, + AI_LOGIC_BEFORE_GENERATE_CONTENT, + AI_LOGIC_AFTER_GENERATE_CONTENT, +} from "../../deploy/functions/services/ailogic"; export const PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished"; @@ -62,3 +66,13 @@ export const CONVERTABLE_EVENTS: Partial> = { "google.cloud.firestore.document.v1.written": "google.cloud.firestore.document.v1.written.withAuthContext", }; + +export const AI_LOGIC_EVENTS_TO_TRIGGER = { + [AI_LOGIC_BEFORE_GENERATE_CONTENT]: "before-generate-content", + [AI_LOGIC_AFTER_GENERATE_CONTENT]: "after-generate-content", +} as const; + +export const AI_LOGIC_TRIGGERS_TO_EVENTS = { + "before-generate-content": AI_LOGIC_BEFORE_GENERATE_CONTENT, + "after-generate-content": AI_LOGIC_AFTER_GENERATE_CONTENT, +} as const; diff --git a/src/gcp/ailogic.ts b/src/gcp/ailogic.ts index 26326cc696b..ef19d8fb35f 100644 --- a/src/gcp/ailogic.ts +++ b/src/gcp/ailogic.ts @@ -1,14 +1,14 @@ import { Client } from "../apiv2"; import { aiLogicProxyOrigin } from "../api"; import { DeepOmit } from "../metaprogramming"; -import { - AI_LOGIC_BEFORE_GENERATE_CONTENT, - AI_LOGIC_AFTER_GENERATE_CONTENT, - type AILogicEndpoint, -} from "../deploy/functions/services/ailogic"; +import type { AILogicEndpoint } from "../deploy/functions/services/ailogic"; export const API_VERSION = "v1beta"; +export const AI_LOGIC_BEFORE_GENERATE_CONTENT = + "google.firebase.ailogic.v1.beforeGenerate" as const; +export const AI_LOGIC_AFTER_GENERATE_CONTENT = "google.firebase.ailogic.v1.afterGenerate" as const; + export const AI_LOGIC_EVENTS_TO_TRIGGER = { [AI_LOGIC_BEFORE_GENERATE_CONTENT]: "before-generate-content", [AI_LOGIC_AFTER_GENERATE_CONTENT]: "after-generate-content",