google-adk-mongo-session-service 0.0.1__py3-none-any.whl

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.
@@ -0,0 +1,3 @@
1
+ from .mongo_session_service import MongoSessionService
2
+
3
+ __all__ = ["MongoSessionService"]
@@ -0,0 +1,185 @@
1
+ """DAL models and _id helpers for MongoSessionService.
2
+
3
+ Document _id is computed at read/write time from PK fields (not stored in model).
4
+ Collections: sessions, events, app_states, user_states, adk_internal_metadata.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime
10
+ from datetime import timezone
11
+ from typing import Any
12
+
13
+ from pydantic import BaseModel
14
+ from pydantic import Field
15
+
16
+ from google.adk.events.event import Event
17
+ from google.adk.sessions.session import Session
18
+
19
+
20
+ # --- _id helpers (deterministic, PK-derived) ---
21
+
22
+
23
+ def session_doc_id(app_name: str, user_id: str, id: str) -> str:
24
+ return f"{app_name}_{user_id}_{id}"
25
+
26
+
27
+ def event_doc_id(event_id: str, app_name: str, user_id: str, session_id: str) -> str:
28
+ return f"{event_id}_{app_name}_{user_id}_{session_id}"
29
+
30
+
31
+ def app_state_doc_id(app_name: str) -> str:
32
+ return app_name
33
+
34
+
35
+ def user_state_doc_id(app_name: str, user_id: str) -> str:
36
+ return f"{app_name}_{user_id}"
37
+
38
+
39
+ def metadata_doc_id(key: str) -> str:
40
+ return key
41
+
42
+
43
+ # --- DAL models (no _id field; _id added at serialization time) ---
44
+
45
+
46
+ class MongoSession(BaseModel):
47
+ """Document model for sessions collection."""
48
+
49
+ app_name: str
50
+ user_id: str
51
+ id: str
52
+ state: dict[str, Any] = Field(default_factory=dict)
53
+ create_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
54
+ update_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
55
+
56
+ def to_doc(self) -> dict[str, Any]:
57
+ doc = self.model_dump()
58
+ doc["_id"] = session_doc_id(self.app_name, self.user_id, self.id)
59
+ return doc
60
+
61
+ @classmethod
62
+ def from_doc(cls, doc: dict[str, Any]) -> MongoSession:
63
+ data = {k: v for k, v in doc.items() if k != "_id"}
64
+ return cls.model_validate(data)
65
+
66
+ def to_session(
67
+ self,
68
+ state: dict[str, Any] | None = None,
69
+ events: list[Event] | None = None,
70
+ ) -> Session:
71
+ if state is None:
72
+ state = {}
73
+ if events is None:
74
+ events = []
75
+ update_ts = self.update_time.timestamp()
76
+ if self.update_time.tzinfo is None:
77
+ update_ts = self.update_time.replace(tzinfo=timezone.utc).timestamp()
78
+ return Session(
79
+ app_name=self.app_name,
80
+ user_id=self.user_id,
81
+ id=self.id,
82
+ state=state,
83
+ events=events,
84
+ last_update_time=update_ts,
85
+ )
86
+
87
+
88
+ class MongoEvent(BaseModel):
89
+ """Document model for events collection."""
90
+
91
+ id: str
92
+ app_name: str
93
+ user_id: str
94
+ session_id: str
95
+ invocation_id: str = ""
96
+ timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
97
+ event_data: dict[str, Any] | None = None
98
+
99
+ def to_doc(self) -> dict[str, Any]:
100
+ doc = self.model_dump()
101
+ doc["_id"] = event_doc_id(self.id, self.app_name, self.user_id, self.session_id)
102
+ return doc
103
+
104
+ @classmethod
105
+ def from_doc(cls, doc: dict[str, Any]) -> MongoEvent:
106
+ data = {k: v for k, v in doc.items() if k != "_id"}
107
+ return cls.model_validate(data)
108
+
109
+ def to_event(self) -> Event:
110
+ ts = self.timestamp.timestamp()
111
+ if self.timestamp.tzinfo is None:
112
+ ts = self.timestamp.replace(tzinfo=timezone.utc).timestamp()
113
+ return Event.model_validate({
114
+ **(self.event_data or {}),
115
+ "id": self.id,
116
+ "invocation_id": self.invocation_id,
117
+ "timestamp": ts,
118
+ })
119
+
120
+ @classmethod
121
+ def from_event(cls, session: Session, event: Event) -> MongoEvent:
122
+ ts = datetime.fromtimestamp(event.timestamp, tz=timezone.utc)
123
+ return cls(
124
+ id=event.id,
125
+ invocation_id=event.invocation_id,
126
+ session_id=session.id,
127
+ app_name=session.app_name,
128
+ user_id=session.user_id,
129
+ timestamp=ts,
130
+ event_data=event.model_dump(exclude_none=True, mode="json"),
131
+ )
132
+
133
+
134
+ class MongoAppState(BaseModel):
135
+ """Document model for app_states collection."""
136
+
137
+ app_name: str
138
+ state: dict[str, Any] = Field(default_factory=dict)
139
+ update_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
140
+
141
+ def to_doc(self) -> dict[str, Any]:
142
+ doc = self.model_dump()
143
+ doc["_id"] = app_state_doc_id(self.app_name)
144
+ return doc
145
+
146
+ @classmethod
147
+ def from_doc(cls, doc: dict[str, Any]) -> MongoAppState:
148
+ data = {k: v for k, v in doc.items() if k != "_id"}
149
+ return cls.model_validate(data)
150
+
151
+
152
+ class MongoUserState(BaseModel):
153
+ """Document model for user_states collection."""
154
+
155
+ app_name: str
156
+ user_id: str
157
+ state: dict[str, Any] = Field(default_factory=dict)
158
+ update_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
159
+
160
+ def to_doc(self) -> dict[str, Any]:
161
+ doc = self.model_dump()
162
+ doc["_id"] = user_state_doc_id(self.app_name, self.user_id)
163
+ return doc
164
+
165
+ @classmethod
166
+ def from_doc(cls, doc: dict[str, Any]) -> MongoUserState:
167
+ data = {k: v for k, v in doc.items() if k != "_id"}
168
+ return cls.model_validate(data)
169
+
170
+
171
+ class MongoMetadata(BaseModel):
172
+ """Document model for adk_internal_metadata collection."""
173
+
174
+ key: str
175
+ value: str
176
+
177
+ def to_doc(self) -> dict[str, Any]:
178
+ doc = self.model_dump()
179
+ doc["_id"] = metadata_doc_id(self.key)
180
+ return doc
181
+
182
+ @classmethod
183
+ def from_doc(cls, doc: dict[str, Any]) -> MongoMetadata:
184
+ data = {k: v for k, v in doc.items() if k != "_id"}
185
+ return cls.model_validate(data)
@@ -0,0 +1,339 @@
1
+ """MongoSessionService: BaseSessionService implementation using MongoDB."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ import uuid
7
+ from datetime import datetime
8
+ from datetime import timezone
9
+ from typing import Any
10
+ from typing import Optional
11
+
12
+ import pymongo
13
+ from typing_extensions import override
14
+
15
+ from google.adk.errors.already_exists_error import AlreadyExistsError
16
+ from google.adk.events.event import Event
17
+ from google.adk.sessions import _session_util
18
+ from google.adk.sessions.base_session_service import BaseSessionService
19
+ from google.adk.sessions.base_session_service import GetSessionConfig
20
+ from google.adk.sessions.base_session_service import ListSessionsResponse
21
+ from google.adk.sessions.session import Session
22
+ from google.adk.sessions.state import State
23
+
24
+ from .models import (
25
+ MongoAppState,
26
+ MongoEvent,
27
+ MongoSession,
28
+ MongoUserState,
29
+ app_state_doc_id,
30
+ session_doc_id,
31
+ user_state_doc_id,
32
+ )
33
+
34
+ # Collection names (fixed, not configurable)
35
+ COLLECTION_SESSIONS = "sessions"
36
+ COLLECTION_EVENTS = "events"
37
+ COLLECTION_APP_STATES = "app_states"
38
+ COLLECTION_USER_STATES = "user_states"
39
+
40
+
41
+ def _merge_state(
42
+ app_state: dict[str, Any],
43
+ user_state: dict[str, Any],
44
+ session_state: dict[str, Any],
45
+ ) -> dict[str, Any]:
46
+ """Merge app, user, and session states into a single state dictionary."""
47
+ merged_state = copy.deepcopy(session_state)
48
+ for key in app_state.keys():
49
+ merged_state[State.APP_PREFIX + key] = app_state[key]
50
+ for key in user_state.keys():
51
+ merged_state[State.USER_PREFIX + key] = user_state[key]
52
+ return merged_state
53
+
54
+
55
+ def _update_timestamp_tz(update_time: datetime) -> float:
56
+ """Return update_time as UTC timestamp (float)."""
57
+ if update_time.tzinfo is None:
58
+ return update_time.replace(tzinfo=timezone.utc).timestamp()
59
+ return update_time.timestamp()
60
+
61
+
62
+ class MongoSessionService(BaseSessionService):
63
+ """Session service that uses MongoDB for storage."""
64
+
65
+ def __init__(self, conn_string: str, db_name: str):
66
+ self._mongo_client: pymongo.AsyncMongoClient = pymongo.AsyncMongoClient(
67
+ conn_string
68
+ )
69
+ self._database = self._mongo_client[db_name]
70
+
71
+ def _sessions(self):
72
+ return self._database[COLLECTION_SESSIONS]
73
+
74
+ def _events(self):
75
+ return self._database[COLLECTION_EVENTS]
76
+
77
+ def _app_states(self):
78
+ return self._database[COLLECTION_APP_STATES]
79
+
80
+ def _user_states(self):
81
+ return self._database[COLLECTION_USER_STATES]
82
+
83
+ @override
84
+ async def create_session(
85
+ self,
86
+ *,
87
+ app_name: str,
88
+ user_id: str,
89
+ state: Optional[dict[str, Any]] = None,
90
+ session_id: Optional[str] = None,
91
+ ) -> Session:
92
+ state = state or {}
93
+ if session_id is not None:
94
+ sid = session_doc_id(app_name, user_id, session_id)
95
+ existing = await self._sessions().find_one({"_id": sid})
96
+ if existing is not None:
97
+ raise AlreadyExistsError(
98
+ f"Session with id {session_id} already exists."
99
+ )
100
+
101
+ # Get or create app_state and user_state
102
+ app_state_id = app_state_doc_id(app_name)
103
+ app_doc = await self._app_states().find_one({"_id": app_state_id})
104
+ if app_doc is None:
105
+ app_state = MongoAppState(app_name=app_name, state={})
106
+ await self._app_states().insert_one(app_state.to_doc())
107
+ else:
108
+ app_state = MongoAppState.from_doc(app_doc)
109
+
110
+ user_state_id = user_state_doc_id(app_name, user_id)
111
+ user_doc = await self._user_states().find_one({"_id": user_state_id})
112
+ if user_doc is None:
113
+ user_state = MongoUserState(
114
+ app_name=app_name, user_id=user_id, state={}
115
+ )
116
+ await self._user_states().insert_one(user_state.to_doc())
117
+ else:
118
+ user_state = MongoUserState.from_doc(user_doc)
119
+
120
+ # Extract and apply state deltas
121
+ state_deltas = _session_util.extract_state_delta(state)
122
+ app_state_delta = state_deltas["app"]
123
+ user_state_delta = state_deltas["user"]
124
+ session_state = state_deltas["session"]
125
+ if app_state_delta:
126
+ app_state.state = app_state.state | app_state_delta
127
+ await self._app_states().replace_one(
128
+ {"_id": app_state_id}, app_state.to_doc()
129
+ )
130
+ if user_state_delta:
131
+ user_state.state = user_state.state | user_state_delta
132
+ await self._user_states().replace_one(
133
+ {"_id": user_state_id}, user_state.to_doc()
134
+ )
135
+
136
+ # Generate session id if not provided
137
+ if session_id is None:
138
+ session_id = str(uuid.uuid4())
139
+
140
+ now = datetime.now(timezone.utc)
141
+ mongo_session = MongoSession(
142
+ app_name=app_name,
143
+ user_id=user_id,
144
+ id=session_id,
145
+ state=session_state,
146
+ create_time=now,
147
+ update_time=now,
148
+ )
149
+ await self._sessions().insert_one(mongo_session.to_doc())
150
+
151
+ merged_state = _merge_state(
152
+ app_state.state, user_state.state, session_state
153
+ )
154
+ return mongo_session.to_session(state=merged_state)
155
+
156
+ @override
157
+ async def get_session(
158
+ self,
159
+ *,
160
+ app_name: str,
161
+ user_id: str,
162
+ session_id: str,
163
+ config: Optional[GetSessionConfig] = None,
164
+ ) -> Optional[Session]:
165
+ sid = session_doc_id(app_name, user_id, session_id)
166
+ session_doc = await self._sessions().find_one({"_id": sid})
167
+ if session_doc is None:
168
+ return None
169
+
170
+ mongo_session = MongoSession.from_doc(session_doc)
171
+
172
+ # Query events: app_name, user_id, session_id; filter/sort/limit
173
+ filter_query: dict[str, Any] = {
174
+ "app_name": app_name,
175
+ "user_id": user_id,
176
+ "session_id": session_id,
177
+ }
178
+ if config and config.after_timestamp is not None:
179
+ after_dt = datetime.fromtimestamp(
180
+ config.after_timestamp, tz=timezone.utc
181
+ )
182
+ filter_query["timestamp"] = {"$gte": after_dt}
183
+
184
+ cursor = (
185
+ self._events()
186
+ .find(filter_query)
187
+ .sort("timestamp", -1)
188
+ )
189
+ if config and config.num_recent_events is not None:
190
+ cursor = cursor.limit(config.num_recent_events)
191
+ event_docs = await cursor.to_list(length=None)
192
+
193
+ events = [
194
+ MongoEvent.from_doc(d).to_event()
195
+ for d in reversed(event_docs)
196
+ ]
197
+
198
+ # Load app_state and user_state
199
+ app_doc = await self._app_states().find_one(
200
+ {"_id": app_state_doc_id(app_name)}
201
+ )
202
+ user_doc = await self._user_states().find_one(
203
+ {"_id": user_state_doc_id(app_name, user_id)}
204
+ )
205
+ app_state = MongoAppState.from_doc(app_doc).state if app_doc else {}
206
+ user_state = MongoUserState.from_doc(user_doc).state if user_doc else {}
207
+ merged_state = _merge_state(
208
+ app_state, user_state, mongo_session.state
209
+ )
210
+ return mongo_session.to_session(state=merged_state, events=events)
211
+
212
+ @override
213
+ async def list_sessions(
214
+ self, *, app_name: str, user_id: Optional[str] = None
215
+ ) -> ListSessionsResponse:
216
+ filter_query: dict[str, Any] = {"app_name": app_name}
217
+ if user_id is not None:
218
+ filter_query["user_id"] = user_id
219
+ cursor = self._sessions().find(filter_query)
220
+ session_docs = await cursor.to_list(length=None)
221
+
222
+ app_doc = await self._app_states().find_one(
223
+ {"_id": app_state_doc_id(app_name)}
224
+ )
225
+ app_state = MongoAppState.from_doc(app_doc).state if app_doc else {}
226
+
227
+ user_states_map: dict[str, dict[str, Any]] = {}
228
+ if user_id is not None:
229
+ user_doc = await self._user_states().find_one(
230
+ {"_id": user_state_doc_id(app_name, user_id)}
231
+ )
232
+ if user_doc:
233
+ user_states_map[user_id] = MongoUserState.from_doc(
234
+ user_doc
235
+ ).state
236
+ else:
237
+ cursor_user = self._user_states().find({"app_name": app_name})
238
+ user_docs = await cursor_user.to_list(length=None)
239
+ for ud in user_docs:
240
+ u = MongoUserState.from_doc(ud)
241
+ user_states_map[u.user_id] = u.state
242
+
243
+ sessions = []
244
+ for doc in session_docs:
245
+ mongo_session = MongoSession.from_doc(doc)
246
+ user_state = user_states_map.get(mongo_session.user_id, {})
247
+ merged_state = _merge_state(
248
+ app_state, user_state, mongo_session.state
249
+ )
250
+ sessions.append(mongo_session.to_session(state=merged_state))
251
+ return ListSessionsResponse(sessions=sessions)
252
+
253
+ @override
254
+ async def delete_session(
255
+ self, *, app_name: str, user_id: str, session_id: str
256
+ ) -> None:
257
+ await self._events().delete_many(
258
+ {
259
+ "app_name": app_name,
260
+ "user_id": user_id,
261
+ "session_id": session_id,
262
+ }
263
+ )
264
+ sid = session_doc_id(app_name, user_id, session_id)
265
+ await self._sessions().delete_one({"_id": sid})
266
+
267
+ @override
268
+ async def append_event(self, session: Session, event: Event) -> Event:
269
+ if event.partial:
270
+ return event
271
+ event = self._trim_temp_delta_state(event)
272
+
273
+ sid = session_doc_id(session.app_name, session.user_id, session.id)
274
+ session_doc = await self._sessions().find_one({"_id": sid})
275
+ if session_doc is None:
276
+ raise ValueError(f"Session {session.id} not found.")
277
+ mongo_session = MongoSession.from_doc(session_doc)
278
+ stored_update_ts = _update_timestamp_tz(mongo_session.update_time)
279
+ if stored_update_ts > session.last_update_time:
280
+ raise ValueError(
281
+ "The last_update_time provided in the session object"
282
+ f" {datetime.fromtimestamp(session.last_update_time):'%Y-%m-%d %H:%M:%S'} is"
283
+ " earlier than the update_time in the storage_session"
284
+ f" {datetime.fromtimestamp(stored_update_ts):'%Y-%m-%d %H:%M:%S'}."
285
+ " Please check if it is a stale session."
286
+ )
287
+
288
+ app_state_id = app_state_doc_id(session.app_name)
289
+ user_state_id = user_state_doc_id(session.app_name, session.user_id)
290
+ app_doc = await self._app_states().find_one({"_id": app_state_id})
291
+ user_doc = await self._user_states().find_one({"_id": user_state_id})
292
+ app_state = (
293
+ MongoAppState.from_doc(app_doc)
294
+ if app_doc
295
+ else MongoAppState(app_name=session.app_name, state={})
296
+ )
297
+ user_state = (
298
+ MongoUserState.from_doc(user_doc)
299
+ if user_doc
300
+ else MongoUserState(
301
+ app_name=session.app_name, user_id=session.user_id, state={}
302
+ )
303
+ )
304
+
305
+ if event.actions and event.actions.state_delta:
306
+ state_deltas = _session_util.extract_state_delta(
307
+ event.actions.state_delta
308
+ )
309
+ if state_deltas["app"]:
310
+ app_state.state = app_state.state | state_deltas["app"]
311
+ await self._app_states().replace_one(
312
+ {"_id": app_state_id},
313
+ app_state.to_doc(),
314
+ upsert=True,
315
+ )
316
+ if state_deltas["user"]:
317
+ user_state.state = user_state.state | state_deltas["user"]
318
+ await self._user_states().replace_one(
319
+ {"_id": user_state_id},
320
+ user_state.to_doc(),
321
+ upsert=True,
322
+ )
323
+ if state_deltas["session"]:
324
+ mongo_session.state = mongo_session.state | state_deltas[
325
+ "session"
326
+ ]
327
+
328
+ update_time = datetime.fromtimestamp(event.timestamp, tz=timezone.utc)
329
+ mongo_session.update_time = update_time
330
+ await self._sessions().replace_one(
331
+ {"_id": sid}, mongo_session.to_doc()
332
+ )
333
+
334
+ mongo_event = MongoEvent.from_event(session, event)
335
+ await self._events().insert_one(mongo_event.to_doc())
336
+
337
+ session.last_update_time = _update_timestamp_tz(mongo_session.update_time)
338
+ await super().append_event(session=session, event=event)
339
+ return event
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: google-adk-mongo-session-service
3
+ Version: 0.0.1
4
+ Summary: Implementation of `google.adk.sessions.base_session_service.BaseSessionService` for storing session information in MongoDB.
5
+ Project-URL: Homepage, https://github.com/kirlut/google-adk-mongo-session-service
6
+ Project-URL: Repository, https://github.com/kirlut/google-adk-mongo-session-service
7
+ Project-URL: Issues, https://github.com/kirlut/google-adk-mongo-session-service/issues
8
+ Author: Kirill Lutcenko
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Kirill Lutcenko
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Requires-Python: >=3.13
32
+ Requires-Dist: google-adk>=1.23.0
33
+ Requires-Dist: pymongo>=4.16.0
34
+ Description-Content-Type: text/markdown
35
+
36
+ [![PyPI version](https://img.shields.io/pypi/v/google-adk-mongo-session-service.svg)](https://pypi.org/project/google-adk-mongo-session-service/)
37
+
38
+ **Disclaimer:** This code was written with the help of an AI agent and has never been tested in production. Please test it carefully before using it in production.
39
+
40
+ Implementation of `google.adk.sessions.base_session_service.BaseSessionService` for storing session information in MongoDB. It plugs into [Google ADK](https://github.com/google/adk) agents so sessions can be backed by MongoDB instead of in-memory/default storage.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install google-adk-mongo-session-service
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from google_adk_mongo_session_service import MongoSessionService
52
+
53
+ session_service = MongoSessionService(conn_string="mongodb://...", db_name="mydb")
54
+ # Pass session_service to your Agent / Runner as the session backend
55
+ ```
56
+
57
+ **Full example:** [example/](https://github.com/kirlut/google-adk-mongo-session-service/tree/main/example/) — runnable weather agent with MongoSessionService; see [example/README.md](https://github.com/kirlut/google-adk-mongo-session-service/blob/main/example/README.md) for setup and run instructions.
@@ -0,0 +1,7 @@
1
+ google_adk_mongo_session_service/__init__.py,sha256=deiRYAwHnNBW4R-ra8g5gJgsWejxK4eca3rQbhJatP0,90
2
+ google_adk_mongo_session_service/models.py,sha256=1XhC6wssMsddRJaAQQXSzIkPOVWWS__CYtVJxelUaNI,5559
3
+ google_adk_mongo_session_service/mongo_session_service.py,sha256=Fk-Pur-8k8OFZeHY5pG2rlrhRwV3Uoijab8g9tvWrpA,12454
4
+ google_adk_mongo_session_service-0.0.1.dist-info/METADATA,sha256=cA-PV2kkz-5ZMcdF-It7ew3pKEm7wHFHo6otyIkDSDI,3079
5
+ google_adk_mongo_session_service-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
+ google_adk_mongo_session_service-0.0.1.dist-info/licenses/LICENSE,sha256=Xwko7DhJQtVO6Uk5C-pTVTj5HXoTg3ZTyyjrJMKw-WU,1072
7
+ google_adk_mongo_session_service-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kirill Lutcenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.