Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6517592
feat(sessions): Add session title management functionality
zhangp365 Nov 25, 2025
c737bba
refactor(sessions): format method signatures for the last commit.
zhangp365 Nov 25, 2025
6c96278
feat(sessions): Update session title handling to use FastAPI Body for…
zhangp365 Nov 25, 2025
2a81c3a
Merge branch 'google:main' into main
zhangp365 Nov 26, 2025
f7853c6
Merge branch 'google:main' into main
zhangp365 Nov 27, 2025
075085c
Merge remote-tracking branch 'upstream/main'
zhangp365 Nov 29, 2025
6cfc057
fix a bug in the PerAgentDatabaseSessionService class
zhangp365 Nov 30, 2025
dc98833
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 4, 2025
71a0e5d
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 4, 2025
1022f73
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 4, 2025
04f77ae
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 6, 2025
d7a722f
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 9, 2025
befd3c9
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 10, 2025
f64608c
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 11, 2025
649daaf
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 11, 2025
f8d77d6
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 11, 2025
bfe19b5
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 12, 2025
d224489
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 13, 2025
f70a2d8
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 16, 2025
880edcc
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 17, 2025
07f6061
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 17, 2025
81e5cf1
Merge remote-tracking branch 'upstream/main'
zhangp365 Dec 18, 2025
eafc126
Merge branch 'main' into main
zhangp365 Dec 18, 2025
2e9ab59
Merge branch 'main' into main
zhangp365 Dec 19, 2025
c76c9c2
Merge branch 'main' into main
zhangp365 Dec 20, 2025
3efab01
Merge branch 'main' into main
zhangp365 Dec 23, 2025
c7b8bd3
Merge branch 'main' into main
zhangp365 Dec 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/google/adk/cli/adk_web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from typing import Literal
from typing import Optional

from fastapi import Body
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi import Query
Expand Down Expand Up @@ -868,6 +869,21 @@ async def create_session(

return session

@app.patch(
"/apps/{app_name}/users/{user_id}/sessions/{session_id}/title",
response_model_exclude_none=True,
)
async def update_session_title(
app_name: str,
user_id: str,
session_id: str,
title: Optional[str] = Body(None, embed=True),
) -> dict[str, str]:
await self.session_service.update_session_title(
app_name=app_name, user_id=user_id, session_id=session_id, title=title
)
return {"status": "success"}

@app.delete("/apps/{app_name}/users/{user_id}/sessions/{session_id}")
async def delete_session(
app_name: str, user_id: str, session_id: str
Expand Down
19 changes: 19 additions & 0 deletions src/google/adk/cli/utils/local_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,15 @@ async def create_session(
user_id: str,
state: Optional[dict[str, object]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
service = await self._get_service(app_name)
return await service.create_session(
app_name=app_name,
user_id=user_id,
state=state,
session_id=session_id,
title=title,
)

@override
Expand Down Expand Up @@ -206,6 +208,23 @@ async def delete_session(
app_name=app_name, user_id=user_id, session_id=session_id
)

@override
async def update_session_title(
self,
*,
app_name: str,
user_id: str,
session_id: str,
title: Optional[str],
) -> None:
service = await self._get_service(app_name)
await service.update_session_title(
app_name=app_name,
user_id=user_id,
session_id=session_id,
title=title,
)

@override
async def append_event(self, session: Session, event: Event) -> Event:
service = await self._get_service(session.app_name)
Expand Down
19 changes: 19 additions & 0 deletions src/google/adk/sessions/base_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ async def create_session(
user_id: str,
state: Optional[dict[str, Any]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
"""Creates a new session.

Expand Down Expand Up @@ -102,6 +103,24 @@ async def delete_session(
) -> None:
"""Deletes a session."""

@abc.abstractmethod
async def update_session_title(
self,
*,
app_name: str,
user_id: str,
session_id: str,
title: Optional[str],
) -> None:
"""Updates the title of a session.

Args:
app_name: The name of the app.
user_id: The id of the user.
session_id: The id of the session.
title: The new title for the session. If None, clears the title.
"""

async def append_event(self, session: Session, event: Event) -> Event:
"""Appends an event to a session object."""
if event.partial:
Expand Down
40 changes: 34 additions & 6 deletions src/google/adk/sessions/database_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ async def create_session(
user_id: str,
state: Optional[dict[str, Any]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
# 1. Populate states.
# 2. Build storage session object
Expand Down Expand Up @@ -268,12 +269,15 @@ async def create_session(
storage_user_state.state = storage_user_state.state | user_state_delta

# Store the session
storage_session = schema.StorageSession(
app_name=app_name,
user_id=user_id,
id=session_id,
state=session_state,
)
storage_session_kwargs = {
"app_name": app_name,
"user_id": user_id,
"id": session_id,
"state": session_state,
}
if hasattr(schema.StorageSession, "title"):
storage_session_kwargs["title"] = title
storage_session = schema.StorageSession(**storage_session_kwargs)
sql_session.add(storage_session)
await sql_session.commit()

Expand Down Expand Up @@ -408,6 +412,30 @@ async def delete_session(
await sql_session.execute(stmt)
await sql_session.commit()

@override
async def update_session_title(
self,
*,
app_name: str,
user_id: str,
session_id: str,
title: Optional[str],
) -> None:
await self._prepare_tables()
schema = self._get_schema_classes()
async with self.database_session_factory() as sql_session:
storage_session = await sql_session.get(
schema.StorageSession, (app_name, user_id, session_id)
)
if storage_session is None:
raise ValueError(
f"Session not found: app_name={app_name}, user_id={user_id},"
f" session_id={session_id}"
)
if hasattr(storage_session, "title"):
storage_session.title = title
await sql_session.commit()

@override
async def append_event(self, session: Session, event: Event) -> Event:
await self._prepare_tables()
Expand Down
23 changes: 23 additions & 0 deletions src/google/adk/sessions/in_memory_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ async def create_session(
user_id: str,
state: Optional[dict[str, Any]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
return self._create_session_impl(
app_name=app_name,
user_id=user_id,
state=state,
session_id=session_id,
title=title,
)

def create_session_sync(
Expand All @@ -73,13 +75,15 @@ def create_session_sync(
user_id: str,
state: Optional[dict[str, Any]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
logger.warning('Deprecated. Please migrate to the async method.')
return self._create_session_impl(
app_name=app_name,
user_id=user_id,
state=state,
session_id=session_id,
title=title,
)

def _create_session_impl(
Expand All @@ -89,6 +93,7 @@ def _create_session_impl(
user_id: str,
state: Optional[dict[str, Any]] = None,
session_id: Optional[str] = None,
title: Optional[str] = None,
) -> Session:
if session_id and self._get_session_impl(
app_name=app_name, user_id=user_id, session_id=session_id
Expand Down Expand Up @@ -116,6 +121,7 @@ def _create_session_impl(
id=session_id,
state=session_state or {},
last_update_time=time.time(),
title=title,
)

if app_name not in self.sessions:
Expand Down Expand Up @@ -286,6 +292,23 @@ def _delete_session_impl(

self.sessions[app_name][user_id].pop(session_id)

@override
async def update_session_title(
self,
*,
app_name: str,
user_id: str,
session_id: str,
title: Optional[str],
) -> None:
session = self.sessions.get(app_name, {}).get(user_id, {}).get(session_id)
if session is None:
raise ValueError(
f'Session not found: app_name={app_name}, user_id={user_id},'
f' session_id={session_id}'
)
session.title = title

@override
async def append_event(self, session: Session, event: Event) -> Event:
if event.partial:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ def migrate(source_db_url: str, dest_db_path: str):
sessions = source_session.query(v0_schema.StorageSession).all()
for item in sessions:
dest_cursor.execute(
"INSERT INTO sessions (app_name, user_id, id, state, create_time,"
" update_time) VALUES (?, ?, ?, ?, ?, ?)",
"INSERT INTO sessions (app_name, user_id, id, state, title,"
" create_time, update_time) VALUES (?, ?, ?, ?, ?, ?, ?)",
(
item.app_name,
item.user_id,
item.id,
json.dumps(item.state),
item.title,
item.create_time.replace(tzinfo=timezone.utc).timestamp(),
item.update_time.replace(tzinfo=timezone.utc).timestamp(),
),
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/sessions/schemas/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class StorageSession(Base):
state: Mapped[MutableDict[str, Any]] = mapped_column(
MutableDict.as_mutable(DynamicJSON), default={}
)
title: Mapped[Optional[str]] = mapped_column(
String(DEFAULT_MAX_VARCHAR_LENGTH), nullable=True
)

create_time: Mapped[datetime] = mapped_column(
PreciseTimestamp, default=func.now()
Expand Down Expand Up @@ -139,6 +142,7 @@ def to_session(
state=state,
events=events,
last_update_time=self.update_timestamp_tz,
title=self.title,
)


Expand Down
3 changes: 3 additions & 0 deletions src/google/adk/sessions/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from __future__ import annotations

from typing import Any
from typing import Optional

from pydantic import alias_generators
from pydantic import BaseModel
Expand Down Expand Up @@ -48,3 +49,5 @@ class Session(BaseModel):
call/response, etc."""
last_update_time: float = 0.0
"""The last update time of the session."""
title: Optional[str] = None
"""The title of the session."""
Loading