genkit-plugin-firebase 0.3.2__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genkit-plugin-firebase
3
- Version: 0.3.2
3
+ Version: 0.5.0
4
4
  Summary: Genkit Firebase Plugin
5
+ Project-URL: Bug Tracker, https://github.com/firebase/genkit/issues
6
+ Project-URL: Documentation, https://firebase.google.com/docs/genkit
7
+ Project-URL: Homepage, https://github.com/firebase/genkit
8
+ Project-URL: Repository, https://github.com/firebase/genkit/tree/main/py
5
9
  Author: Google
6
- License: Apache-2.0
10
+ License-Expression: Apache-2.0
7
11
  License-File: LICENSE
12
+ Keywords: ai,artificial-intelligence,firebase,firestore,generative-ai,genkit,google,llm,machine-learning,telemetry
8
13
  Classifier: Development Status :: 3 - Alpha
9
14
  Classifier: Environment :: Console
10
15
  Classifier: Environment :: Web Environment
@@ -17,8 +22,10 @@ Classifier: Programming Language :: Python :: 3.10
17
22
  Classifier: Programming Language :: Python :: 3.11
18
23
  Classifier: Programming Language :: Python :: 3.12
19
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
20
26
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
27
  Classifier: Topic :: Software Development :: Libraries
28
+ Classifier: Typing :: Typed
22
29
  Requires-Python: >=3.10
23
30
  Requires-Dist: genkit
24
31
  Requires-Dist: google-cloud-firestore
@@ -0,0 +1,73 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ [project]
18
+ authors = [{ name = "Google" }]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Environment :: Console",
22
+ "Environment :: Web Environment",
23
+ "Intended Audience :: Developers",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python",
26
+ "Programming Language :: Python :: 3 :: Only",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
32
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
33
+ "Topic :: Software Development :: Libraries",
34
+ "Typing :: Typed",
35
+ "License :: OSI Approved :: Apache Software License",
36
+ ]
37
+ dependencies = [
38
+ "genkit",
39
+ "google-cloud-firestore",
40
+ "strenum>=0.4.15; python_version < '3.11'",
41
+ ]
42
+ description = "Genkit Firebase Plugin"
43
+ keywords = [
44
+ "genkit",
45
+ "ai",
46
+ "llm",
47
+ "machine-learning",
48
+ "artificial-intelligence",
49
+ "generative-ai",
50
+ "firebase",
51
+ "google",
52
+ "firestore",
53
+ "telemetry",
54
+ ]
55
+ license = "Apache-2.0"
56
+ name = "genkit-plugin-firebase"
57
+ readme = "README.md"
58
+ requires-python = ">=3.10"
59
+ version = "0.5.0"
60
+
61
+ [project.urls]
62
+ "Bug Tracker" = "https://github.com/firebase/genkit/issues"
63
+ "Documentation" = "https://firebase.google.com/docs/genkit"
64
+ "Homepage" = "https://github.com/firebase/genkit"
65
+ "Repository" = "https://github.com/firebase/genkit/tree/main/py"
66
+
67
+ [build-system]
68
+ build-backend = "hatchling.build"
69
+ requires = ["hatchling"]
70
+
71
+ [tool.hatch.build.targets.wheel]
72
+ only-include = ["src/genkit/plugins/firebase"]
73
+ sources = ["src"]
@@ -0,0 +1,205 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ """Firebase plugin for Genkit.
18
+
19
+ This plugin provides Firebase integrations for Genkit, including Firestore
20
+ vector stores for RAG and Firebase telemetry export to Google Cloud.
21
+
22
+ Key Concepts (ELI5)::
23
+
24
+ ┌─────────────────────┬────────────────────────────────────────────────────┐
25
+ │ Concept │ ELI5 Explanation │
26
+ ├─────────────────────┼────────────────────────────────────────────────────┤
27
+ │ Firebase │ Google's app development platform. Like a │
28
+ │ │ toolbox for building apps with database, auth. │
29
+ ├─────────────────────┼────────────────────────────────────────────────────┤
30
+ │ Firestore │ A NoSQL database that syncs in real-time. │
31
+ │ │ Store data as flexible documents. │
32
+ ├─────────────────────┼────────────────────────────────────────────────────┤
33
+ │ Vector Store │ A database that can find "similar" items. │
34
+ │ │ Like Google but for YOUR documents. │
35
+ ├─────────────────────┼────────────────────────────────────────────────────┤
36
+ │ RAG │ Retrieval-Augmented Generation. AI looks up │
37
+ │ │ your docs before answering. Fewer hallucinations! │
38
+ ├─────────────────────┼────────────────────────────────────────────────────┤
39
+ │ Embeddings │ Convert text to numbers that capture meaning. │
40
+ │ │ "Cat" and "kitten" become similar numbers. │
41
+ ├─────────────────────┼────────────────────────────────────────────────────┤
42
+ │ Indexer │ Stores documents with their embeddings. │
43
+ │ │ Like adding books to a library catalog. │
44
+ ├─────────────────────┼────────────────────────────────────────────────────┤
45
+ │ Retriever │ Finds documents matching a query. │
46
+ │ │ Like a librarian finding relevant books. │
47
+ └─────────────────────┴────────────────────────────────────────────────────┘
48
+
49
+ Data Flow (RAG with Firestore)::
50
+
51
+ ┌─────────────────────────────────────────────────────────────────────────┐
52
+ │ HOW FIRESTORE VECTOR SEARCH WORKS │
53
+ │ │
54
+ │ STEP 1: INDEXING (Store your documents) │
55
+ │ ──────────────────────────────────────── │
56
+ │ Your Documents │
57
+ │ ["How to reset password", "Billing FAQ", ...] │
58
+ │ │ │
59
+ │ │ (1) Convert text to embeddings │
60
+ │ ▼ │
61
+ │ ┌─────────────────┐ │
62
+ │ │ Embedder │ "Reset password" → [0.12, -0.34, ...] │
63
+ │ │ (Gemini, etc.) │ │
64
+ │ └────────┬────────┘ │
65
+ │ │ │
66
+ │ │ (2) Store in Firestore with vectors │
67
+ │ ▼ │
68
+ │ ┌─────────────────┐ │
69
+ │ │ Firestore │ Document + embedding stored together │
70
+ │ │ (Vector Index) │ │
71
+ │ └─────────────────┘ │
72
+ │ │
73
+ │ STEP 2: RETRIEVAL (Find relevant documents) │
74
+ │ ───────────────────────────────────────────── │
75
+ │ User Query: "How do I change my password?" │
76
+ │ │ │
77
+ │ │ (3) Convert query to embedding │
78
+ │ ▼ │
79
+ │ ┌─────────────────┐ │
80
+ │ │ Embedder │ Query → [0.11, -0.33, ...] (similar!) │
81
+ │ └────────┬────────┘ │
82
+ │ │ │
83
+ │ │ (4) Find nearest neighbors │
84
+ │ ▼ │
85
+ │ ┌─────────────────┐ │
86
+ │ │ Firestore │ "Reset password" doc is 95% match! │
87
+ │ │ Vector Search │ │
88
+ │ └────────┬────────┘ │
89
+ │ │ │
90
+ │ │ (5) Return matching documents │
91
+ │ ▼ │
92
+ │ ┌─────────────────┐ │
93
+ │ │ Your App │ AI uses these docs to answer accurately │
94
+ │ └─────────────────┘ │
95
+ └─────────────────────────────────────────────────────────────────────────┘
96
+
97
+ Architecture Overview::
98
+
99
+ ┌─────────────────────────────────────────────────────────────────────────┐
100
+ │ Firebase Plugin │
101
+ ├─────────────────────────────────────────────────────────────────────────┤
102
+ │ Plugin Entry Point (__init__.py) │
103
+ │ ├── define_firestore_vector_store() - Create Firestore vector store │
104
+ │ └── add_firebase_telemetry() - Enable Cloud observability │
105
+ ├─────────────────────────────────────────────────────────────────────────┤
106
+ │ firestore.py - Firestore Vector Store │
107
+ │ ├── define_firestore_vector_store() - Main factory function │
108
+ │ ├── Firestore indexer implementation │
109
+ │ └── Firestore retriever implementation │
110
+ ├─────────────────────────────────────────────────────────────────────────┤
111
+ │ retriever.py - Retriever Implementation │
112
+ │ └── FirestoreRetriever (vector similarity search) │
113
+ └─────────────────────────────────────────────────────────────────────────┘
114
+
115
+ ┌─────────────────────────────────────────────────────────────────────────┐
116
+ │ Firestore Vector Store Flow │
117
+ │ │
118
+ │ Documents ──► Embedder ──► Firestore (with vector index) │
119
+ │ │
120
+ │ Query ──► Embedder ──► Firestore Vector Search ──► Results │
121
+ └─────────────────────────────────────────────────────────────────────────┘
122
+
123
+ Overview:
124
+ The Firebase plugin enables:
125
+ - Firestore as a vector store for document retrieval (RAG)
126
+ - Telemetry export to Google Cloud Trace and Monitoring
127
+
128
+ Key Components:
129
+ ┌─────────────────────────────────────────────────────────────────────────┐
130
+ │ Component │ Purpose │
131
+ ├──────────────────────────────┼──────────────────────────────────────────┤
132
+ │ define_firestore_vector_store│ Create a Firestore-backed vector store │
133
+ │ add_firebase_telemetry() │ Enable Cloud Trace/Monitoring export │
134
+ └──────────────────────────────┴──────────────────────────────────────────┘
135
+
136
+ Example:
137
+ Using Firestore vector store:
138
+
139
+ ```python
140
+ from genkit import Genkit
141
+ from genkit.plugins.firebase import define_firestore_vector_store
142
+
143
+ ai = Genkit(...)
144
+
145
+ # Define a Firestore vector store
146
+ store = define_firestore_vector_store(
147
+ ai,
148
+ name='my_store',
149
+ collection='documents',
150
+ embedder='vertexai/text-embedding-005',
151
+ )
152
+
153
+ # Index documents
154
+ await ai.index(indexer=store.indexer, documents=[...])
155
+
156
+ # Retrieve documents
157
+ docs = await ai.retrieve(retriever=store.retriever, query='...')
158
+ ```
159
+
160
+ Enabling telemetry:
161
+
162
+ ```python
163
+ from genkit.plugins.firebase import add_firebase_telemetry
164
+
165
+ # Export traces to Cloud Trace (disabled in dev mode by default)
166
+ add_firebase_telemetry()
167
+ ```
168
+
169
+ Caveats:
170
+ - Requires firebase-admin SDK and Google Cloud credentials
171
+ - Telemetry is disabled by default in development mode (GENKIT_ENV=dev)
172
+
173
+ See Also:
174
+ - Firestore: https://firebase.google.com/docs/firestore
175
+ - Genkit documentation: https://genkit.dev/
176
+ """
177
+
178
+ from genkit.plugins.google_cloud.telemetry.tracing import add_gcp_telemetry
179
+
180
+ from .firestore import define_firestore_vector_store
181
+
182
+
183
+ def package_name() -> str:
184
+ """Get the package name for the Firebase plugin.
185
+
186
+ Returns:
187
+ The fully qualified package name as a string.
188
+ """
189
+ return 'genkit.plugins.firebase'
190
+
191
+
192
+ def add_firebase_telemetry() -> None:
193
+ """Add Firebase telemetry export to Google Cloud Observability.
194
+
195
+ Exports traces to Cloud Trace and metrics to Cloud Monitoring.
196
+ In development (GENKIT_ENV=dev), telemetry is disabled by default.
197
+ """
198
+ add_gcp_telemetry(force_export=False)
199
+
200
+
201
+ __all__ = [
202
+ 'add_firebase_telemetry',
203
+ 'define_firestore_vector_store',
204
+ 'package_name',
205
+ ]
@@ -0,0 +1,103 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+
18
+ """Firestore vector store operations for Genkit."""
19
+
20
+ from collections.abc import Callable
21
+
22
+ from google.cloud import firestore
23
+ from google.cloud.firestore_v1 import DocumentSnapshot
24
+ from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
25
+
26
+ from genkit.ai import Genkit
27
+ from genkit.blocks.retriever import RetrieverOptions, retriever_action_metadata
28
+ from genkit.core.action.types import ActionKind
29
+ from genkit.core.typing import DocumentPart
30
+ from genkit.plugins.firebase.retriever import FirestoreRetriever
31
+
32
+ from .constant import MetadataTransformFn
33
+
34
+
35
+ def firestore_action_name(name: str) -> str:
36
+ """Create a firestore action name.
37
+
38
+ Args:
39
+ name: Base name for the action
40
+
41
+ Returns:
42
+ str: Firestore action name.
43
+
44
+ """
45
+ return f'firestore/{name}'
46
+
47
+
48
+ def define_firestore_vector_store(
49
+ ai: Genkit,
50
+ *,
51
+ name: str,
52
+ embedder: str,
53
+ embedder_options: dict[str, object] | None = None,
54
+ collection: str,
55
+ vector_field: str,
56
+ content_field: str | Callable[[DocumentSnapshot], list['DocumentPart']],
57
+ firestore_client: firestore.Client,
58
+ distance_measure: DistanceMeasure = DistanceMeasure.COSINE,
59
+ metadata_fields: list[str] | MetadataTransformFn | None = None,
60
+ ) -> str:
61
+ """Define and register a Firestore vector store retriever.
62
+
63
+ Args:
64
+ ai: The Genkit instance to register the retriever with.
65
+ name: Name of the retriever.
66
+ embedder: The embedder to use (e.g., 'vertexai/text-embedding-004').
67
+ embedder_options: Optional configuration to pass to the embedder.
68
+ collection: The name of the Firestore collection to query.
69
+ vector_field: The name of the field containing the vector embeddings.
70
+ content_field: The name of the field containing the document content, you wish to return.
71
+ firestore_client: The Firestore database instance from which to query.
72
+ distance_measure: The distance measure to use when comparing vectors. Defaults to 'COSINE'.
73
+ metadata_fields: Optional list of metadata fields to include.
74
+
75
+ Returns:
76
+ The registered retriever name.
77
+ """
78
+ retriever = FirestoreRetriever(
79
+ ai=ai,
80
+ name=name,
81
+ embedder=embedder,
82
+ embedder_options=embedder_options,
83
+ firestore_client=firestore_client,
84
+ collection=collection,
85
+ vector_field=vector_field,
86
+ content_field=content_field,
87
+ distance_measure=distance_measure,
88
+ metadata_fields=metadata_fields,
89
+ )
90
+
91
+ action_name = firestore_action_name(name)
92
+
93
+ ai.registry.register_action(
94
+ kind=ActionKind.RETRIEVER,
95
+ name=action_name,
96
+ fn=retriever.retrieve,
97
+ metadata=retriever_action_metadata(
98
+ name=action_name,
99
+ options=RetrieverOptions(label=name),
100
+ ).metadata,
101
+ )
102
+
103
+ return action_name
@@ -14,14 +14,18 @@
14
14
  #
15
15
  # SPDX-License-Identifier: Apache-2.0
16
16
 
17
+
18
+ """Firestore retriever implementation for Genkit."""
19
+
17
20
  from collections.abc import Callable
18
- from typing import Any
19
21
 
22
+ from google.cloud import firestore
20
23
  from google.cloud.firestore_v1 import DocumentSnapshot
21
24
  from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
22
25
  from google.cloud.firestore_v1.vector import Vector
23
26
 
24
27
  from genkit.ai import Genkit
28
+ from genkit.core.typing import DocumentPart, TextPart
25
29
  from genkit.types import ActionRunContext, Document, GenkitError, RetrieverRequest, RetrieverResponse
26
30
 
27
31
  from .constant import MetadataTransformFn
@@ -31,45 +35,58 @@ class FirestoreRetriever:
31
35
  """Retrieves documents from Google Cloud Firestore using vector similarity search.
32
36
 
33
37
  Attributes:
34
- ai: An instance of the Genkit AI registry.
35
- params: A FirestoreRetrieverConfig object containing the configuration
36
- for the retriever
38
+ ai: Genkit instance used to embed queries.
39
+ name: Name of the retriever.
40
+ embedder: The embedder to use for query embeddings.
41
+ embedder_options: Optional configuration to pass to the embedder.
37
42
  firestore_client: The initialized Firestore client from the configuration.
43
+ collection: The name of the Firestore collection to query.
44
+ vector_field: The name of the field containing the vector embeddings.
45
+ content_field: The name of the field containing the document content.
46
+ distance_measure: The distance measure to use when comparing vectors.
47
+ metadata_fields: Optional list of metadata fields to include.
38
48
  """
39
49
 
40
50
  def __init__(
41
51
  self,
42
52
  ai: Genkit,
43
53
  name: str,
44
- firestore_client: Any,
54
+ embedder: str,
55
+ embedder_options: dict[str, object] | None,
56
+ firestore_client: firestore.Client,
45
57
  collection: str,
46
58
  vector_field: str,
47
- content_field: str | Callable[[DocumentSnapshot], list[dict[str, str]]],
48
- embedder: str,
49
- embedder_options: dict[str, Any] | None = None,
59
+ content_field: str | Callable[[DocumentSnapshot], list[DocumentPart]],
50
60
  distance_measure: DistanceMeasure = DistanceMeasure.COSINE,
51
61
  metadata_fields: list[str] | MetadataTransformFn | None = None,
52
- ):
62
+ ) -> None:
53
63
  """Initialize the FirestoreRetriever.
54
64
 
55
65
  Args:
56
- ai: An instance of the Genkit AI registry.
57
- params: A FirestoreRetrieverConfig object containing the configuration
58
- for the retriever
66
+ ai: Genkit instance used to embed queries.
67
+ name: Name of the retriever.
68
+ embedder: The embedder to use for query embeddings.
69
+ embedder_options: Optional configuration to pass to the embedder.
70
+ firestore_client: The Firestore database instance from which to query.
71
+ collection: The name of the Firestore collection to query.
72
+ vector_field: The name of the field containing the vector embeddings.
73
+ content_field: The name of the field containing the document content.
74
+ distance_measure: The distance measure to use when comparing vectors.
75
+ metadata_fields: Optional list of metadata fields to include.
59
76
  """
60
77
  self.ai = ai
61
78
  self.name = name
79
+ self.embedder = embedder
80
+ self.embedder_options = embedder_options
62
81
  self.firestore_client = firestore_client
63
82
  self.collection = collection
64
83
  self.vector_field = vector_field
65
84
  self.content_field = content_field
66
- self.embedder = embedder
67
- self.embedder_options = embedder_options
68
85
  self.distance_measure = distance_measure
69
86
  self.metadata_fields = metadata_fields
70
87
  self._validate_config()
71
88
 
72
- def _validate_config(self):
89
+ def _validate_config(self) -> None:
73
90
  """Validate the FirestoreRetriever configuration.
74
91
 
75
92
  Raises:
@@ -84,7 +101,7 @@ class FirestoreRetriever:
84
101
  if not self.firestore_client:
85
102
  raise ValueError('Firestore Retriever config must include firestore client.')
86
103
 
87
- def _to_content(self, doc_snapshot: DocumentSnapshot) -> list[dict[str, str]]:
104
+ def _to_content(self, doc_snapshot: DocumentSnapshot) -> list[DocumentPart]:
88
105
  """Convert a Firestore document snapshot to a list of content dictionaries.
89
106
 
90
107
  Args:
@@ -98,9 +115,9 @@ class FirestoreRetriever:
98
115
  return content_field(doc_snapshot)
99
116
  else:
100
117
  content = doc_snapshot.get(content_field)
101
- return [{'text': content}] if content else []
118
+ return [DocumentPart(root=TextPart(text=str(content)))] if content else []
102
119
 
103
- def _to_metadata(self, doc_snapshot: DocumentSnapshot) -> Document:
120
+ def _to_metadata(self, doc_snapshot: DocumentSnapshot) -> dict[str, object]:
104
121
  """Convert a Firestore document snapshot to a list of metadata dictionaries.
105
122
 
106
123
  Args:
@@ -109,17 +126,19 @@ class FirestoreRetriever:
109
126
  Returns:
110
127
  A list of dictionaries containing the metadata of the document.
111
128
  """
112
- metadata: dict[str, Any] = {}
129
+ metadata: dict[str, object] = {}
113
130
  metadata_fields = self.metadata_fields
114
131
  if metadata_fields:
115
132
  if callable(metadata_fields):
133
+ # pyrefly: ignore[bad-assignment] - MetadataTransformFn returns dict[str, Any]
116
134
  metadata = metadata_fields(doc_snapshot)
117
135
  else:
136
+ doc_dict = doc_snapshot.to_dict() or {}
118
137
  for field in metadata_fields:
119
- if field in doc_snapshot:
120
- metadata[field] = doc_snapshot.get(field)
138
+ if field in doc_dict:
139
+ metadata[field] = doc_dict[field]
121
140
  else:
122
- metadata = doc_snapshot.to_dict()
141
+ metadata = doc_snapshot.to_dict() or {}
123
142
  vector_field = self.vector_field
124
143
  content_field = self.content_field
125
144
  if vector_field in metadata:
@@ -148,17 +167,17 @@ class FirestoreRetriever:
148
167
  Returns:
149
168
  A RetrieverResponse Object containing retrieved documents
150
169
  """
151
- query = request.query
170
+ query = Document.from_document_data(document_data=request.query)
152
171
  query_embedding_result = await self.ai.embed(
153
172
  embedder=self.embedder,
154
- documents=[query],
173
+ content=query,
155
174
  options=self.embedder_options,
156
175
  )
157
176
 
158
- if not query_embedding_result.embeddings or len(query_embedding_result.embeddings) == 0:
177
+ if not query_embedding_result:
159
178
  raise GenkitError(message='Embedder returned no embeddings')
160
179
 
161
- query_embedding = query_embedding_result.embeddings[0].embedding
180
+ query_embedding = query_embedding_result[0].embedding
162
181
  query_vector = Vector(query_embedding)
163
182
  collection = self.firestore_client.collection(self.collection)
164
183
 
@@ -0,0 +1,113 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Tests for Firebase telemetry functionality."""
16
+
17
+ from unittest.mock import MagicMock, patch
18
+
19
+ from opentelemetry.sdk.trace import ReadableSpan
20
+
21
+ from genkit.plugins.firebase import add_firebase_telemetry
22
+ from genkit.plugins.google_cloud.telemetry.metrics import record_generate_metrics
23
+
24
+
25
+ def _create_model_span(
26
+ model_name: str = 'gemini-pro',
27
+ path: str = '/{myflow,t:flow}',
28
+ output: str = '{"usage": {"inputTokens": 100, "outputTokens": 50}}',
29
+ is_ok: bool = True,
30
+ start_time: int = 1000000000,
31
+ end_time: int = 1500000000,
32
+ ) -> MagicMock:
33
+ """Helper function to create a model action span for testing.
34
+
35
+ Args:
36
+ model_name: The model name for genkit:name attribute
37
+ path: The genkit:path value
38
+ output: The genkit:output JSON string
39
+ is_ok: Whether the span status is ok
40
+ start_time: Span start time in nanoseconds
41
+ end_time: Span end time in nanoseconds
42
+
43
+ Returns:
44
+ A mocked ReadableSpan with model action attributes
45
+ """
46
+ mock_span = MagicMock(spec=ReadableSpan)
47
+ mock_span.attributes = {
48
+ 'genkit:type': 'action',
49
+ 'genkit:metadata:subtype': 'model',
50
+ 'genkit:name': model_name,
51
+ 'genkit:path': path,
52
+ 'genkit:output': output,
53
+ }
54
+ mock_span.status.is_ok = is_ok
55
+ mock_span.start_time = start_time
56
+ mock_span.end_time = end_time
57
+ return mock_span
58
+
59
+
60
+ @patch('genkit.plugins.firebase.add_gcp_telemetry')
61
+ def test_firebase_telemetry_delegates_to_gcp(mock_add_gcp_telemetry: MagicMock) -> None:
62
+ """Test that Firebase telemetry delegates to GCP telemetry."""
63
+ add_firebase_telemetry()
64
+ mock_add_gcp_telemetry.assert_called_once_with(force_export=False)
65
+
66
+
67
+ @patch('genkit.plugins.google_cloud.telemetry.metrics._output_tokens')
68
+ @patch('genkit.plugins.google_cloud.telemetry.metrics._input_tokens')
69
+ @patch('genkit.plugins.google_cloud.telemetry.metrics._latency')
70
+ @patch('genkit.plugins.google_cloud.telemetry.metrics._failures')
71
+ @patch('genkit.plugins.google_cloud.telemetry.metrics._requests')
72
+ def test_record_generate_metrics_with_model_action(
73
+ mock_requests: MagicMock,
74
+ mock_failures: MagicMock,
75
+ mock_latency: MagicMock,
76
+ mock_input_tokens: MagicMock,
77
+ mock_output_tokens: MagicMock,
78
+ ) -> None:
79
+ """Test that metrics are recorded for model action spans with usage data."""
80
+ # Setup mocks
81
+ mock_request_counter = MagicMock()
82
+ mock_latency_histogram = MagicMock()
83
+ mock_input_counter = MagicMock()
84
+ mock_output_counter = MagicMock()
85
+
86
+ mock_requests.return_value = mock_request_counter
87
+ mock_failures.return_value = MagicMock()
88
+ mock_latency.return_value = mock_latency_histogram
89
+ mock_input_tokens.return_value = mock_input_counter
90
+ mock_output_tokens.return_value = mock_output_counter
91
+
92
+ # Create test span using helper
93
+ mock_span = _create_model_span(
94
+ model_name='gemini-pro',
95
+ path='/{myflow,t:flow}',
96
+ output='{"usage": {"inputTokens": 100, "outputTokens": 50}}',
97
+ )
98
+
99
+ # Execute
100
+ record_generate_metrics(mock_span)
101
+
102
+ # Verify dimensions
103
+ expected_dimensions = {'model': 'gemini-pro', 'source': 'myflow', 'error': 'none'}
104
+
105
+ # Verify requests counter
106
+ mock_request_counter.add.assert_called_once_with(1, expected_dimensions)
107
+
108
+ # Verify latency (500ms = 1.5s - 1.0s)
109
+ mock_latency_histogram.record.assert_called_once_with(500.0, expected_dimensions)
110
+
111
+ # Verify token counts
112
+ mock_input_counter.add.assert_called_once_with(100, expected_dimensions)
113
+ mock_output_counter.add.assert_called_once_with(50, expected_dimensions)
File without changes
@@ -0,0 +1,43 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ """Tests for Firebase plugin."""
18
+
19
+ from unittest.mock import MagicMock, patch
20
+
21
+ from genkit.plugins.firebase import (
22
+ add_firebase_telemetry,
23
+ define_firestore_vector_store,
24
+ package_name,
25
+ )
26
+
27
+
28
+ def test_package_name() -> None:
29
+ """Test package_name returns correct value."""
30
+ assert package_name() == 'genkit.plugins.firebase'
31
+
32
+
33
+ @patch('genkit.plugins.firebase.add_gcp_telemetry')
34
+ def test_add_firebase_telemetry_calls_gcp_telemetry(mock_add_gcp: MagicMock) -> None:
35
+ """Test add_firebase_telemetry delegates to GCP telemetry."""
36
+ add_firebase_telemetry()
37
+ mock_add_gcp.assert_called_once_with(force_export=False)
38
+
39
+
40
+ def test_define_firestore_vector_store_exported() -> None:
41
+ """Test define_firestore_vector_store is exported."""
42
+ # Just verify the function is importable and callable
43
+ assert callable(define_firestore_vector_store)
@@ -1,36 +0,0 @@
1
- [project]
2
- authors = [{ name = "Google" }]
3
- classifiers = [
4
- "Development Status :: 3 - Alpha",
5
- "Environment :: Console",
6
- "Environment :: Web Environment",
7
- "Intended Audience :: Developers",
8
- "Operating System :: OS Independent",
9
- "License :: OSI Approved :: Apache Software License",
10
- "Programming Language :: Python",
11
- "Programming Language :: Python :: 3 :: Only",
12
- "Programming Language :: Python :: 3.10",
13
- "Programming Language :: Python :: 3.11",
14
- "Programming Language :: Python :: 3.12",
15
- "Programming Language :: Python :: 3.13",
16
- "Topic :: Scientific/Engineering :: Artificial Intelligence",
17
- "Topic :: Software Development :: Libraries",
18
- ]
19
- dependencies = [
20
- "genkit",
21
- "google-cloud-firestore",
22
- "strenum>=0.4.15; python_version < '3.11'",
23
- ]
24
- description = "Genkit Firebase Plugin"
25
- license = { text = "Apache-2.0" }
26
- name = "genkit-plugin-firebase"
27
- readme = "README.md"
28
- requires-python = ">=3.10"
29
- version = "0.3.2"
30
-
31
- [build-system]
32
- build-backend = "hatchling.build"
33
- requires = ["hatchling"]
34
-
35
- [tool.hatch.build.targets.wheel]
36
- packages = ["src/genkit", "src/genkit/plugins"]
@@ -1,30 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- #
15
- # SPDX-License-Identifier: Apache-2.0
16
-
17
-
18
- """Firebase Plugin for Genkit."""
19
-
20
-
21
- def package_name() -> str:
22
- """Get the package name for the Firebase plugin.
23
-
24
- Returns:
25
- The fully qualified package name as a string.
26
- """
27
- return 'genkit.plugins.firebase'
28
-
29
-
30
- __all__ = ['package_name']
@@ -1,112 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- #
15
- # SPDX-License-Identifier: Apache-2.0
16
-
17
- from collections.abc import Callable
18
- from typing import Any
19
-
20
- from google.cloud.firestore_v1 import DocumentSnapshot
21
- from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
22
-
23
- from genkit.ai import GenkitRegistry, Plugin
24
- from genkit.plugins.firebase.retriever import FirestoreRetriever
25
-
26
- from .constant import MetadataTransformFn
27
-
28
-
29
- def firestore_action_name(name: str) -> str:
30
- """Create a firestore action name.
31
-
32
- Args:
33
- name: Base name for the action
34
-
35
- Returns:
36
- str: Firestore action name.
37
-
38
- """
39
- return f'firestore/{name}'
40
-
41
-
42
- class FirestoreVectorStore(Plugin):
43
- """Firestore retriever plugin.
44
- Args:
45
- name: name if the retriever.
46
- collection: The name of the Firestore collection to query.
47
- vector_field: The name of the field containing the vector embeddings.
48
- content_field: The name of the field containing the document content, you wish to return.
49
- embedder: The embedder to use with this retriever.
50
- embedder_options: Optional configuration to pass to the embedder.
51
- distance_measure: The distance measure to use when comparing vectors. Defaults to 'COSINE'.
52
- firestore_client: The Firestore database instance from which to query.
53
- metadata_fields: Optional list of metadata fields to include.
54
- """
55
-
56
- name = 'firebaseFirestore'
57
-
58
- def __init__(
59
- self,
60
- name: str,
61
- firestore_client: Any,
62
- collection: str,
63
- vector_field: str,
64
- content_field: str | Callable[[DocumentSnapshot], list[dict[str, str]]],
65
- embedder: str,
66
- embedder_options: dict[str, Any] | None = None,
67
- distance_measure: DistanceMeasure = DistanceMeasure.COSINE,
68
- metadata_fields: list[str] | MetadataTransformFn | None = None,
69
- ):
70
- """Initialize the firestore plugin.
71
-
72
- Args:
73
- params: List of firestore retriever configurations.
74
- """
75
- self.name = name
76
- self.firestore_client = firestore_client
77
- self.collection = collection
78
- self.vector_field = vector_field
79
- self.content_field = content_field
80
- self.embedder = embedder
81
- self.embedder_options = embedder_options
82
- self.distance_measure = distance_measure
83
- self.metadata_fields = metadata_fields
84
-
85
- def initialize(self, ai: GenkitRegistry) -> None:
86
- """Initialize firestore plugin.
87
-
88
- Register actions with the registry making them available for use in the Genkit framework.
89
-
90
- Args:
91
- ai: The registry to register actions with.
92
-
93
- Returns:
94
- None
95
- """
96
- retriever = FirestoreRetriever(
97
- ai=ai,
98
- name=self.name,
99
- firestore_client=self.firestore_client,
100
- collection=self.collection,
101
- vector_field=self.vector_field,
102
- content_field=self.content_field,
103
- embedder=self.embedder,
104
- embedder_options=self.embedder_options,
105
- distance_measure=self.distance_measure,
106
- metadata_fields=self.metadata_fields,
107
- )
108
-
109
- return ai.define_retriever(
110
- name=firestore_action_name(self.name),
111
- fn=retriever.retrieve,
112
- )