feat(skills): inject session state into SKILL.md via adk_inject_state#5405
feat(skills): inject session state into SKILL.md via adk_inject_state#5405Koichi73 wants to merge 4 commits into
Conversation
Let SKILL.md bodies reference session state variables with the same
`{var}` / `{var?}` / `{artifact.name}` syntax already supported in
`LlmAgent.instruction`. The behavior is opt-in per skill through the
frontmatter metadata flag `adk_inject_state: true`, so existing SKILL.md
files are unaffected.
Interpolation is applied in `LoadSkillTool.run_async` using the existing
`instructions_utils.inject_session_state` helper, so skills reuse the
agent's state read mechanism without any new public concepts.
|
Hi @Koichi73 , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Can you please fix the failing unit tests before we can proceed with the review. |
…related to this PR)
0347b39 to
cd647b5
Compare
|
Hi @rohityan, the failing test is I've pushed an empty commit to retrigger CI, but the workflow is waiting for maintainer approval. Could you approve it when you have a moment? 🙏 |
|
Hi @rohityan, gentle ping on this — it's been a week since my last reply. To summarize where things stand:
Could you (or another maintainer) approve the CI run when you have a minute? Thanks for your time. |
|
Hi @Jacksunwei , can you please review this. |
…state # Conflicts: # src/google/adk/tools/skill_toolset.py
c97bbe3 to
44067c2
Compare
|
Hi @Jacksunwei, I've merged the latest |
Merge #5405 ### Link to Issue or Description of Change - Closes: #5404 **Problem:** SKILL.md bodies cannot reference session state the same way `LlmAgent.instruction` can. Today the only way to surface a state value inside a skill is to register a custom getter tool through `SkillToolset(additional_tools=[...])` and tell the model to call it. That is boilerplate for a capability the agent already has. **Solution:** Reuse the existing `instructions_utils.inject_session_state` helper from `LoadSkillTool`. When a skill opts in with `metadata.adk_inject_state: true` in its frontmatter, the skill body is interpolated with the same `{var}` / `{var?}` / `{artifact.name}` syntax that `LlmAgent.instruction` supports. The feature is strictly additive and gated on an opt-in flag, so existing SKILL.md files are unaffected. ### Testing Plan **Unit Tests:** - [x] I have added or updated unit tests for my change. - [x] All unit tests pass locally. Added to `tests/unittests/tools/test_skill_toolset.py`: - `test_load_skill_run_async_injects_state_when_opt_in` - `test_load_skill_run_async_skips_injection_when_opt_out` - `test_load_skill_run_async_skips_injection_when_metadata_absent` Added to `tests/unittests/skills/test_models.py`: - `test_metadata_adk_inject_state_bool` - `test_metadata_adk_inject_state_rejected_as_string` Existing mock fixtures for `skill1` / `skill2` frontmatter now explicitly set `metadata = {}`, documenting the default and preventing autospec-mock leakage into the new opt-in check. `pytest` summary: ``` $ pytest tests/unittests/tools/test_skill_toolset.py tests/unittests/skills/test_models.py 111 passed, 7 warnings in 11.39s ``` **Manual End-to-End (E2E) Tests:** 1. In a skill directory, add `metadata: {adk_inject_state: true}` to `SKILL.md` frontmatter and reference a state variable in the body, e.g. `The user prefers {temperature_unit?}.` 2. Seed session state with `session.state["temperature_unit"] = "celsius"` before the agent runs. 3. Run the agent (`adk run` or `adk web`) and trigger the skill. The `load_skill` tool response now contains the instructions with `{temperature_unit}` expanded to `celsius`. 4. Remove or set `adk_inject_state: false` and re-run — the same SKILL.md returns the literal `{temperature_unit?}` string, confirming backward compatibility. ### 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. ### Additional context This is the read side of a two-part effort to connect SKILL.md with session state. A follow-up PR will propose an `output_key`-style facility for writing state from a skill. Splitting the two keeps each review small and focused. Co-authored-by: Haran Rajkumar <haranrk@google.com> COPYBARA_INTEGRATE_REVIEW=#5405 from Koichi73:feat/skill-md-inject-state 44067c2 PiperOrigin-RevId: 936248442
Link to Issue or Description of Change
Problem: SKILL.md bodies cannot reference session state the same way
LlmAgent.instructioncan. Today the only way to surface a state value inside a skill is to register a custom getter tool throughSkillToolset(additional_tools=[...])and tell the model to call it. That is boilerplate for a capability the agent already has.Solution: Reuse the existing
instructions_utils.inject_session_statehelper fromLoadSkillTool. When a skill opts in withmetadata.adk_inject_state: truein its frontmatter, the skill body is interpolated with the same{var}/{var?}/{artifact.name}syntax thatLlmAgent.instructionsupports. The feature is strictly additive and gated on an opt-in flag, so existing SKILL.md files are unaffected.Testing Plan
Unit Tests:
Added to
tests/unittests/tools/test_skill_toolset.py:test_load_skill_run_async_injects_state_when_opt_intest_load_skill_run_async_skips_injection_when_opt_outtest_load_skill_run_async_skips_injection_when_metadata_absentAdded to
tests/unittests/skills/test_models.py:test_metadata_adk_inject_state_booltest_metadata_adk_inject_state_rejected_as_stringExisting mock fixtures for
skill1/skill2frontmatter now explicitly setmetadata = {}, documenting the default and preventing autospec-mock leakage into the new opt-in check.pytestsummary:Manual End-to-End (E2E) Tests:
metadata: {adk_inject_state: true}toSKILL.mdfrontmatter and reference a state variable in the body, e.g.The user prefers {temperature_unit?}.session.state["temperature_unit"] = "celsius"before the agent runs.adk runoradk web) and trigger the skill. Theload_skilltool response now contains the instructions with{temperature_unit}expanded tocelsius.adk_inject_state: falseand re-run — the same SKILL.md returns the literal{temperature_unit?}string, confirming backward compatibility.Checklist
Additional context
This is the read side of a two-part effort to connect SKILL.md with session state. A follow-up PR will propose an
output_key-style facility for writing state from a skill. Splitting the two keeps each review small and focused.