Skip to content

Commit 1a9df8f

Browse files
ScottMansfieldcopybara-github
authored andcommitted
feat: Firestore support
END_PUBLIC Merge #5088 Adding support for Firestore for both session and memory storage. This started by copying the Firestore support from the Java ADK into the Python ADK and also takes inspiration from @anmolg1997's PR google/adk-python-community#104. It does things differently from both. Firestore contains a hierarchical set of data for sessions: ``` Hierarchy for sessions: adk-session ↳ <app name> ↳ users ↳ <user ID> ↳ sessions ↳ <session ID> ↳ events ↳ <event ID> Hierarchy for shared App/User state configurations: app_states ↳ <app name> user_states ↳ <app name> ↳ users ↳ <user ID> Hierarchy for memory: memories ↳ <memory ID> ``` The firestore memory service creates a top-level collection that hold indexed memories when sessions are added. ### Link to Issue or Description of Change This is from an existing customer request for firestore support. ### Testing Plan **Unit Tests:** - [x] I have added or updated unit tests for my change. - [x] All unit tests pass locally. ``` $ pytest tests/unittests/integrations/firestore/ ===================================== test session starts ====================================== platform darwin -- Python 3.11.14, pytest-9.0.2, pluggy-1.6.0 rootdir: /Users/scottmansfield/projects/adk-python configfile: pyproject.toml plugins: mock-3.15.1, xdist-3.8.0, langsmith-0.7.23, asyncio-1.3.0, anyio-4.13.0 asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function collected 29 items tests/unittests/integrations/firestore/test_firestore_memory_service.py ............. [ 44%] tests/unittests/integrations/firestore/test_firestore_session_service.py ................ [100%] ======================================= warnings summary ======================================= src/google/adk/features/_feature_decorator.py:72 /Users/scottmansfield/projects/adk-python/src/google/adk/features/_feature_decorator.py:72: UserWarning: [EXPERIMENTAL] feature FeatureName.PLUGGABLE_AUTH is enabled. check_feature_enabled() -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ================================ 29 passed, 1 warning in 1.44s ================================= ``` **Manual End-to-End (E2E) Tests:** I created a demo app locally to test the session and memory storage with a real firebase instance. It successfully records sessions and memories, verified by manually checking the cloud console. Memory did require an index, which will be created by the user the first time the memory session is used. Firestore has a specific deep link that it will create to give the exact index needed. After that, memory worked fine. ### Checklist - [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document. - [x] I have performed a self-review of my own code. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have added tests that prove my fix is effective or that my feature works. - [x] New and existing unit tests pass locally with my changes. - [x] I have manually tested my changes end-to-end. - [x] Any dependent changes have been merged and published in downstream modules. COPYBARA_INTEGRATE_REVIEW=#5088 from ScottMansfield:feat/firestore 99efca5 PiperOrigin-RevId: 899328760
1 parent 5195ba7 commit 1a9df8f

File tree

8 files changed

+2098
-7
lines changed

8 files changed

+2098
-7
lines changed

pyproject.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ dependencies = [
4444
"google-cloud-spanner>=3.56.0, <4.0.0", # For Spanner database
4545
"google-cloud-speech>=2.30.0, <3.0.0", # For Audio Transcription
4646
"google-cloud-storage>=2.18.0, <4.0.0", # For GCS Artifact service
47-
"google-genai>=1.72.0, <2.0.0", # Google GenAI SDK
47+
"google-genai>=1.64.0, <2.0.0", # Google GenAI SDK
4848
"graphviz>=0.20.2, <1.0.0", # Graphviz for graph rendering
4949
"httpx>=0.27.0, <1.0.0", # HTTP client library
5050
"jsonschema>=4.23.0, <5.0.0", # Agent Builder config validation
@@ -123,7 +123,7 @@ test = [
123123
"a2a-sdk>=0.3.0,<0.4.0",
124124
"anthropic>=0.43.0", # For anthropic model tests
125125
"crewai[tools];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
126-
"google-cloud-iamconnectorcredentials>=0.1.0, <0.2.0",
126+
"google-cloud-firestore>=2.11.0",
127127
"google-cloud-parametermanager>=0.4.0, <1.0.0",
128128
"kubernetes>=29.0.0", # For GkeCodeExecutor
129129
"langchain-community>=0.3.17",
@@ -159,6 +159,7 @@ extensions = [
159159
"beautifulsoup4>=3.2.2", # For load_web_page tool.
160160
"crewai[tools];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool; chromadb/pypika fail on 3.12+
161161
"docker>=7.0.0", # For ContainerCodeExecutor
162+
"google-cloud-firestore>=2.11.0", # For Firestore services
162163
"google-cloud-parametermanager>=0.4.0, <1.0.0",
163164
"kubernetes>=29.0.0", # For GkeCodeExecutor
164165
"k8s-agent-sandbox>=0.1.1.post3", # For GkeCodeExecutor sandbox mode
@@ -177,10 +178,6 @@ toolbox = ["toolbox-adk>=1.0.0, <2.0.0"]
177178

178179
slack = ["slack-bolt>=1.22.0"]
179180

180-
agent-identity = [
181-
"google-cloud-iamconnectorcredentials>=0.1.0, <0.2.0",
182-
]
183-
184181
[tool.pyink]
185182
# Format py files following Google style-guide
186183
line-length = 80

src/google/adk/errors/already_exists_error.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
class AlreadyExistsError(Exception):
1919
"""Represents an error that occurs when an entity already exists."""
2020

21-
def __init__(self, message="The resource already exists."):
21+
def __init__(self, message: str = "The resource already exists."):
2222
"""Initializes the AlreadyExistsError exception.
2323
2424
Args:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
from __future__ import annotations
16+
17+
"""Firestore integrations for ADK."""
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
from __future__ import annotations
16+
17+
DEFAULT_STOP_WORDS = {
18+
"a",
19+
"about",
20+
"above",
21+
"after",
22+
"again",
23+
"against",
24+
"all",
25+
"am",
26+
"an",
27+
"and",
28+
"any",
29+
"are",
30+
"as",
31+
"at",
32+
"be",
33+
"because",
34+
"been",
35+
"before",
36+
"being",
37+
"below",
38+
"between",
39+
"both",
40+
"but",
41+
"by",
42+
"can",
43+
"could",
44+
"did",
45+
"do",
46+
"does",
47+
"doing",
48+
"don",
49+
"down",
50+
"during",
51+
"each",
52+
"else",
53+
"few",
54+
"for",
55+
"from",
56+
"further",
57+
"had",
58+
"has",
59+
"have",
60+
"having",
61+
"he",
62+
"her",
63+
"here",
64+
"hers",
65+
"herself",
66+
"him",
67+
"himself",
68+
"his",
69+
"how",
70+
"i",
71+
"if",
72+
"in",
73+
"into",
74+
"is",
75+
"it",
76+
"its",
77+
"itself",
78+
"just",
79+
"may",
80+
"me",
81+
"might",
82+
"more",
83+
"most",
84+
"must",
85+
"my",
86+
"myself",
87+
"no",
88+
"nor",
89+
"not",
90+
"now",
91+
"of",
92+
"off",
93+
"on",
94+
"once",
95+
"only",
96+
"or",
97+
"other",
98+
"our",
99+
"ours",
100+
"ourselves",
101+
"out",
102+
"over",
103+
"own",
104+
"s",
105+
"same",
106+
"shall",
107+
"she",
108+
"should",
109+
"so",
110+
"some",
111+
"such",
112+
"t",
113+
"than",
114+
"that",
115+
"the",
116+
"their",
117+
"theirs",
118+
"them",
119+
"themselves",
120+
"then",
121+
"there",
122+
"these",
123+
"they",
124+
"this",
125+
"those",
126+
"through",
127+
"to",
128+
"too",
129+
"under",
130+
"until",
131+
"up",
132+
"very",
133+
"was",
134+
"we",
135+
"were",
136+
"what",
137+
"when",
138+
"where",
139+
"which",
140+
"who",
141+
"whom",
142+
"why",
143+
"will",
144+
"with",
145+
"would",
146+
"you",
147+
"your",
148+
"yours",
149+
"yourself",
150+
"yourselves",
151+
}

0 commit comments

Comments
 (0)