UNPKG

@genkit-ai/firebase

Version:

Genkit AI framework plugin for Firebase including Firestore trace/state store and deployment helpers for Cloud Functions for Firebase.

1 lines 6.78 kB
{"version":3,"sources":["../../src/stream-manager/firestore.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomUUID } from 'crypto';\nimport { App } from 'firebase-admin/app';\nimport {\n DocumentReference,\n FieldValue,\n getFirestore,\n type Firestore,\n} from 'firebase-admin/firestore';\nimport {\n GenkitError,\n StreamNotFoundError,\n type ActionStreamInput,\n type ActionStreamSubscriber,\n type StreamManager,\n} from 'genkit/beta';\n\nexport interface FirestoreStreamManagerOptions {\n firebaseApp?: App;\n db?: Firestore;\n collection: string;\n timeout?: number;\n}\n\nclass FirestoreActionStream<S, O> implements ActionStreamInput<S, O> {\n private streamDoc;\n\n constructor(streamDoc: DocumentReference) {\n this.streamDoc = streamDoc;\n }\n\n private async update(data: any): Promise<void> {\n await this.streamDoc.update({\n ...data,\n updatedAt: FieldValue.serverTimestamp(),\n });\n }\n\n async write(chunk: S): Promise<void> {\n await this.update({\n // We add a random ID to the chunk to prevent Firestore from deduplicating chunks\n // that have the same content.\n stream: FieldValue.arrayUnion({\n type: 'chunk',\n chunk,\n uuid: randomUUID(),\n }),\n });\n }\n\n async done(output: O): Promise<void> {\n await this.update({\n stream: FieldValue.arrayUnion({ type: 'done', output }),\n });\n }\n\n async error(err: any): Promise<void> {\n const serializableError = {\n message: err.message,\n stack: err.stack,\n ...err,\n };\n await this.update({\n stream: FieldValue.arrayUnion({ type: 'error', err: serializableError }),\n });\n }\n}\n\nexport class FirestoreStreamManager implements StreamManager {\n private db: Firestore;\n private collection: string;\n private timeout: number;\n\n constructor(opts: FirestoreStreamManagerOptions) {\n this.collection = opts.collection;\n this.db =\n opts.db ??\n (opts.firebaseApp ? getFirestore(opts.firebaseApp) : getFirestore());\n this.timeout = opts.timeout ?? 60000;\n }\n\n async open<S, O>(streamId: string): Promise<ActionStreamInput<S, O>> {\n const streamDoc = this.db.collection(this.collection).doc(streamId);\n await streamDoc.set({\n createdAt: FieldValue.serverTimestamp(),\n stream: [],\n });\n return new FirestoreActionStream(streamDoc);\n }\n\n async subscribe<S, O>(\n streamId: string,\n callbacks: ActionStreamSubscriber<S, O>\n ): Promise<{\n unsubscribe: () => void;\n }> {\n const streamDoc = this.db.collection(this.collection).doc(streamId);\n const snapshot = await streamDoc.get();\n if (!snapshot.exists) {\n throw new StreamNotFoundError(`Stream ${streamId} not found.`);\n }\n let lastIndex = -1;\n let timeoutId: NodeJS.Timeout;\n const resetTimeout = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n callbacks.onError?.(\n new GenkitError({\n status: 'DEADLINE_EXCEEDED',\n message: 'Stream timed out.',\n })\n );\n unsubscribe();\n }, this.timeout);\n };\n const unsubscribe = streamDoc.onSnapshot((snapshot) => {\n resetTimeout();\n const data = snapshot.data();\n if (!data) {\n return;\n }\n const stream = data.stream || [];\n for (let i = lastIndex + 1; i < stream.length; i++) {\n const event = stream[i];\n if (event.type === 'chunk') {\n callbacks.onChunk?.(event.chunk);\n } else if (event.type === 'done') {\n clearTimeout(timeoutId);\n callbacks.onDone?.(event.output);\n unsubscribe();\n } else if (event.type === 'error') {\n clearTimeout(timeoutId);\n callbacks.onError?.(event.err);\n unsubscribe();\n }\n }\n lastIndex = stream.length - 1;\n });\n resetTimeout();\n return { unsubscribe };\n }\n}\n"],"mappings":"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","names":["snapshot"]}