Skip to content

Commit 968205d

Browse files
committed
Inject agent session info into context
It can be useful for agents to know their own session ID to provide to tools that might want to call back to the agent, generate a link to the agent session, or just have a stable identifier to use for something. Signed-off-by: Dobes Vandermeer <dobes.vandermeer@newsela.com>
1 parent b108bde commit 968205d

File tree

6 files changed

+116
-4
lines changed

6 files changed

+116
-4
lines changed

python/packages/kagent-adk/src/kagent/adk/cli.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from kagent.core import KAgentConfig, configure_logging, configure_tracing
1616

1717
from . import AgentConfig, KAgentApp
18-
from .tools import add_skills_tool_to_agent
18+
from .tools import add_session_tool, add_skills_tool_to_agent
1919

2020
logger = logging.getLogger(__name__)
2121
logging.getLogger("google_adk.google.adk.tools.base_authenticated_tool").setLevel(logging.ERROR)
@@ -74,7 +74,7 @@ def static(
7474

7575
def root_agent_factory() -> BaseAgent:
7676
root_agent = agent_config.to_agent(app_cfg.name, sts_integration)
77-
77+
add_session_tool(root_agent)
7878
maybe_add_skills(root_agent)
7979

8080
return root_agent
@@ -149,6 +149,7 @@ def root_agent_factory() -> BaseAgent:
149149
if sts_integration:
150150
add_to_agent(sts_integration, root_agent)
151151

152+
add_session_tool(root_agent)
152153
maybe_add_skills(root_agent)
153154

154155
return root_agent
@@ -213,6 +214,7 @@ async def test_agent(agent_config: AgentConfig, agent_card: AgentCard, task: str
213214

214215
def root_agent_factory() -> BaseAgent:
215216
root_agent = agent_config.to_agent(app_cfg.name, sts_integration)
217+
add_session_tool(root_agent)
216218
maybe_add_skills(root_agent)
217219
return root_agent
218220

python/packages/kagent-adk/src/kagent/adk/tools/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ description: Analyze CSV/Excel files
123123
| **ReadFile** | Read files with line numbers | `read_file("skills/data-analysis/config.json")` |
124124
| **WriteFile** | Create/overwrite files | `write_file("outputs/report.pdf", data)` |
125125
| **EditFile** | Precise string replacements | `edit_file("script.py", old="x", new="y")` |
126+
| **SessionInfo**| Auto-injects session details into system instructions | *(automatic — no tool call needed)* |
126127

127128
### Working Directory Structure
128129

python/packages/kagent-adk/src/kagent/adk/tools/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .bash_tool import BashTool
22
from .file_tools import EditFileTool, ReadFileTool, WriteFileTool
3+
from .session_tool import SessionInfoTool, add_session_tool
34
from .skill_tool import SkillsTool
45
from .skills_plugin import add_skills_tool_to_agent
56
from .skills_toolset import SkillsToolset
@@ -8,8 +9,10 @@
89
"SkillsTool",
910
"SkillsToolset",
1011
"BashTool",
12+
"SessionInfoTool",
1113
"EditFileTool",
1214
"ReadFileTool",
1315
"WriteFileTool",
16+
"add_session_tool",
1417
"add_skills_tool_to_agent",
1518
]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from typing import TYPE_CHECKING
5+
6+
from google.adk.agents import BaseAgent, LlmAgent
7+
from google.adk.tools.base_tool import BaseTool
8+
from google.adk.tools.tool_context import ToolContext
9+
from typing_extensions import override
10+
11+
if TYPE_CHECKING:
12+
from google.adk.models.llm_request import LlmRequest
13+
14+
logger = logging.getLogger("kagent_adk." + __name__)
15+
16+
17+
def add_session_tool(agent: BaseAgent) -> None:
18+
if not isinstance(agent, LlmAgent):
19+
return
20+
existing_tool_names = {getattr(t, "name", None) for t in agent.tools}
21+
if "get_session_info" not in existing_tool_names:
22+
agent.tools.append(SessionInfoTool())
23+
logger.debug(f"Added session info tool to agent: {agent.name}")
24+
25+
26+
class SessionInfoTool(BaseTool):
27+
"""Tool for retrieving information about the current agent session."""
28+
29+
def __init__(self):
30+
super().__init__(
31+
name="get_session_info",
32+
description="Get information about the current agent session, including the session ID.",
33+
)
34+
35+
@override
36+
async def process_llm_request(
37+
self,
38+
*,
39+
tool_context: ToolContext,
40+
llm_request: LlmRequest,
41+
) -> None:
42+
session = tool_context.session
43+
if not session:
44+
return
45+
info = (
46+
"kagent session:\n"
47+
f"- session_id: {session.id or 'N/A'}\n"
48+
f"- user_id: {session.user_id or 'N/A'}\n"
49+
f"- app_name: {session.app_name or 'N/A'}"
50+
)
51+
llm_request.append_instructions([info])

python/packages/kagent-adk/src/kagent/adk/tools/skills_toolset.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from google.adk.tools import BaseTool
1414
from google.adk.tools.base_toolset import BaseToolset
1515

16-
from ..tools import BashTool, EditFileTool, ReadFileTool, WriteFileTool
16+
from ..tools import BashTool, EditFileTool, ReadFileTool, SessionInfoTool, WriteFileTool
1717
from .skill_tool import SkillsTool
1818

1919
logger = logging.getLogger("kagent_adk." + __name__)
@@ -28,6 +28,7 @@ class SkillsToolset(BaseToolset):
2828
3. WriteFileTool - Write/create files
2929
4. EditFileTool - Edit files with precise replacements
3030
5. BashTool - Execute shell commands
31+
6. SessionInfoTool - Inject session information into system instructions
3132
3233
Skills provide specialized domain knowledge and scripts that the agent can use
3334
to solve complex tasks. The toolset enables discovery of available skills,
@@ -51,18 +52,20 @@ def __init__(self, skills_directory: str | Path):
5152
self.write_file_tool = WriteFileTool()
5253
self.edit_file_tool = EditFileTool()
5354
self.bash_tool = BashTool(skills_directory)
55+
self.session_info_tool = SessionInfoTool()
5456

5557
@override
5658
async def get_tools(self, readonly_context: Optional[ReadonlyContext] = None) -> List[BaseTool]:
5759
"""Get all skills tools.
5860
5961
Returns:
60-
List containing all skills tools: skills, read, write, edit, and bash.
62+
List containing all skills tools: skills, read, write, edit, bash, and session info.
6163
"""
6264
return [
6365
self.skills_tool,
6466
self.read_file_tool,
6567
self.write_file_tool,
6668
self.edit_file_tool,
6769
self.bash_tool,
70+
self.session_info_tool,
6871
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 unittest.mock import Mock
16+
17+
import pytest
18+
from google.adk.models.llm_request import LlmRequest
19+
20+
from kagent.adk.tools.session_tool import SessionInfoTool
21+
22+
23+
class TestSessionInfoTool:
24+
@pytest.mark.asyncio
25+
async def test_session_info_tool(self):
26+
tool = SessionInfoTool()
27+
28+
context = Mock()
29+
context.session = Mock()
30+
context.session.id = "session-123"
31+
context.session.user_id = "user-456"
32+
context.session.app_name = "test-app"
33+
34+
llm_request = LlmRequest()
35+
await tool.process_llm_request(tool_context=context, llm_request=llm_request)
36+
37+
assert "session-123" in llm_request.config.system_instruction
38+
assert "user-456" in llm_request.config.system_instruction
39+
assert "test-app" in llm_request.config.system_instruction
40+
assert "get_session_info" not in llm_request.tools_dict
41+
42+
@pytest.mark.asyncio
43+
async def test_session_info_tool_none_session(self):
44+
tool = SessionInfoTool()
45+
46+
context = Mock()
47+
context.session = None
48+
49+
llm_request = LlmRequest()
50+
await tool.process_llm_request(tool_context=context, llm_request=llm_request)
51+
52+
assert llm_request.config.system_instruction is None

0 commit comments

Comments
 (0)