Skip to content

feat(skills): Support non-blocking skill loading in async runtimes#6060

Open
godwin3737 wants to merge 2 commits into
google:mainfrom
godwin3737:feat/async-skill-loading
Open

feat(skills): Support non-blocking skill loading in async runtimes#6060
godwin3737 wants to merge 2 commits into
google:mainfrom
godwin3737:feat/async-skill-loading

Conversation

@godwin3737

@godwin3737 godwin3737 commented Jun 10, 2026

Copy link
Copy Markdown

1. Link to an existing issue (if applicable):

Problem:
Currently, all local and GCS skill loading and listing functions (load_skill_from_dir, load_skill_from_gcs_dir, list_skills_in_dir, list_skills_in_gcs_dir) are strictly synchronous and perform blocking disk or network I/O.

While the SkillRegistry abstract interface uses async methods, implementing a registry that loads local/GCS skills or using these loading utilities inside an asynchronous web server (like FastAPI) or async runners causes the main event loop to block, degrading performance and responsiveness

Solution:

Add asynchronous counterparts for all 4 core skill loading/listing functions in the google.adk.skills package, preserving the existing synchronous functions for backward compatibility:

load_skill_from_dir_async(skill_dir)
load_skill_from_gcs_dir_async(bucket_name, skill_id, ...)
list_skills_in_dir_async(skills_base_path)
list_skills_in_gcs_dir_async(bucket_name, ...)

These functions should utilize Python's built-in asyncio.to_thread to offload blocking I/O operations to a background thread pool, keeping the async event loop completely non-blocking.

Testing Plan

This change was verified using local unit tests. Four new asynchronous test cases were added to tests/unittests/skills/test__utils.py to validate the behavior of the new async APIs under different scenarios (local directories and Google Cloud Storage).

The following new unit tests were added:

test_load_skill_from_dir_async: Verifies that a skill (including instructions, frontmatter, and assets/references) is correctly loaded asynchronously from a local directory.
test_list_skills_in_dir_async: Verifies that skills in a parent directory are discovered asynchronously and their lightweight metadata is returned correctly.
test_load_skill_from_gcs_dir_async: Verifies that a skill is loaded asynchronously from Google Cloud Storage (GCS), with mock storage client interactions.
test_list_skills_in_gcs_dir_async: Verifies that skills stored in GCS are listed and discovered asynchronously using mock GCS directory prefixes.

Unit Tests:

  • [X ] I have added or updated unit tests for my change.
  • [ X] All unit tests pass locally.

Summary of passed pytest results.

============================= test session starts ==============================
platform linux -- Python 3.11.9, pytest-9.0.3, pluggy-1.6.0
rootdir: /usr/local/google/home/godwinpaul/Documents/source/adk-python
configfile: pyproject.toml
plugins: langsmith-0.8.11, asyncio-1.4.0, anyio-4.13.0, xdist-3.8.0, mock-3.15.1
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collecting ...
collecting 21 items
collected 21 items

tests/unittests/skills/test__utils.py ..................... [100%]

======================== 21 passed, 4 warnings in 2.74s ========================

Manual End-to-End (E2E) Tests:

  1. Setup a Mock Skill Directory called temp_skills with a dummy skill (my_dummy_skill)
  2. Create a Test Script
import asyncio
import pathlib
from google.adk.skills import load_skill_from_dir_async

async def main():
    skill_path = pathlib.Path("temp_skills/my-dummy-skill")
    
    print(" [1/3] Loading skill asynchronously...")
    skill = await load_skill_from_dir_async(skill_path)
    
    print(" [2/3] Skill loaded successfully!")
    print(f"  • Name: {skill.name}")
    print(f"  • Description: {skill.description}")
    print(f"  • Instructions: {skill.instructions.strip()}")
    
    print(" [3/3] Accessing async-loaded resources...")
    ref_content = skill.resources.get_reference("test_reference.txt")
    print(f"  • Reference content: '{ref_content.strip()}'")

if __name__ == "__main__":
    asyncio.run(main())
  1. Run the Script to see the skill loaded correctly.

Checklist

  • [ X] I have read the 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.
  • Any dependent changes have been merged and published in downstream modules.

Add asynchronous counterparts for loading and listing local and GCS skills:
- load_skill_from_dir_async
- load_skill_from_gcs_dir_async
- list_skills_in_dir_async
- list_skills_in_gcs_dir_async

These use asyncio.to_thread to run blocking I/O in a background thread pool, keeping the main event loop free.

Closes google#6057
@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core [Component] This issue is related to the core interface and implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add async counterparts for loading and listing skills

2 participants