Skip to content

Commit bc2c97c

Browse files
Rayan Dasoriyacopybara-github
authored andcommitted
fix: Key directory-loaded skill resources with forward slashes
PiperOrigin-RevId: 962465660
1 parent 3df5a65 commit bc2c97c

2 files changed

Lines changed: 68 additions & 1 deletion

File tree

src/google/adk/skills/_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ def _load_dir(directory: pathlib.Path) -> dict[str, str]:
6767
if file_path.is_file():
6868
relative_path = file_path.relative_to(directory)
6969
try:
70-
files[str(relative_path)] = file_path.read_text(encoding="utf-8")
70+
files[relative_path.as_posix()] = file_path.read_text(
71+
encoding="utf-8"
72+
)
7173
except UnicodeDecodeError:
7274
# Binary files or non-UTF-8 files are skipped for text content.
7375
continue

tests/unittests/skills/test__utils.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import asyncio
1818
import builtins
1919
import io
20+
import pathlib
2021
import struct
2122
import sys
2223
import threading
@@ -82,6 +83,70 @@ def test__load_skill_from_dir(tmp_path):
8283
assert skill.resources.get_script("script1.sh").src == "echo hello"
8384

8485

86+
def _write_nested_skill(tmp_path):
87+
"""Writes a skill whose resources live in subdirectories."""
88+
skill_dir = tmp_path / "nested-skill"
89+
skill_dir.mkdir()
90+
(skill_dir / "SKILL.md").write_text("""---
91+
name: nested-skill
92+
description: Test description
93+
---
94+
Test instructions
95+
""")
96+
97+
scripts_dir = skill_dir / "scripts" / "runtime"
98+
scripts_dir.mkdir(parents=True)
99+
(scripts_dir / "helper.py").write_text("helper source")
100+
101+
ref_dir = skill_dir / "references" / "deep" / "deeper"
102+
ref_dir.mkdir(parents=True)
103+
(ref_dir / "note.md").write_text("nested note")
104+
105+
assets_dir = skill_dir / "assets" / "templates"
106+
assets_dir.mkdir(parents=True)
107+
(assets_dir / "tmpl.txt").write_text("template body")
108+
109+
return skill_dir
110+
111+
112+
def test__load_skill_from_dir_nested_resources_use_forward_slash_keys(tmp_path):
113+
"""Resources in subdirectories are keyed with forward slashes."""
114+
skill = _load_skill_from_dir(_write_nested_skill(tmp_path))
115+
116+
assert skill.resources.get_script("runtime/helper.py").src == "helper source"
117+
assert skill.resources.get_reference("deep/deeper/note.md") == "nested note"
118+
assert skill.resources.get_asset("templates/tmpl.txt") == "template body"
119+
120+
121+
def test__load_skill_from_dir_nested_resources_on_windows_paths(tmp_path):
122+
"""Windows-style separators still produce forward-slash keys.
123+
124+
Regression test for the Windows-only defect where `_load_dir` keyed resources
125+
with `str(relative_path)`. On Windows that is backslash-separated, while
126+
callers such as `load_skill_resource` look resources up with forward slashes,
127+
so every resource in a subdirectory was unreachable.
128+
129+
The bug cannot reproduce on a POSIX test runner, where `str()` already yields
130+
forward slashes, so the Windows flavour of `relative_to` is simulated here.
131+
132+
Args:
133+
tmp_path: pytest fixture providing a temporary directory.
134+
"""
135+
skill_dir = _write_nested_skill(tmp_path)
136+
real_relative_to = pathlib.Path.relative_to
137+
138+
def windows_relative_to(self, *args, **kwargs):
139+
return pathlib.PureWindowsPath(real_relative_to(self, *args, **kwargs))
140+
141+
with mock.patch.object(pathlib.Path, "relative_to", windows_relative_to):
142+
skill = _load_skill_from_dir(skill_dir)
143+
144+
assert list(skill.resources.scripts) == ["runtime/helper.py"]
145+
assert skill.resources.get_script("runtime/helper.py").src == "helper source"
146+
assert skill.resources.get_reference("deep/deeper/note.md") == "nested note"
147+
assert skill.resources.get_asset("templates/tmpl.txt") == "template body"
148+
149+
85150
def test_allowed_tools_yaml_key(tmp_path):
86151
"""Tests that allowed-tools YAML key loads correctly."""
87152
skill_dir = tmp_path / "my-skill"

0 commit comments

Comments
 (0)