Skip to content

Commit fdfbc15

Browse files
Add 05 skills/plugins examples to test-examples (#1773)
Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 9b3a2b7 commit fdfbc15

File tree

3 files changed

+103
-102
lines changed

3 files changed

+103
-102
lines changed

examples/05_skills_and_plugins/01_loading_agentskills/main.py

Lines changed: 97 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"""
2222

2323
import os
24+
import sys
2425
from pathlib import Path
2526

2627
from pydantic import SecretStr
@@ -35,105 +36,99 @@
3536
from openhands.tools.terminal import TerminalTool
3637

3738

38-
def main():
39-
# Get the directory containing this script
40-
script_dir = Path(__file__).parent
41-
example_skills_dir = script_dir / "example_skills"
42-
43-
# =========================================================================
44-
# Part 1: Loading Skills from a Directory
45-
# =========================================================================
46-
print("=" * 80)
47-
print("Part 1: Loading Skills from a Directory")
48-
print("=" * 80)
49-
50-
print(f"Loading skills from: {example_skills_dir}")
51-
52-
# Discover resources in the skill directory
53-
skill_subdir = example_skills_dir / "rot13-encryption"
54-
resources = discover_skill_resources(skill_subdir)
55-
print("\nDiscovered resources in rot13-encryption/:")
56-
print(f" - scripts: {resources.scripts}")
57-
print(f" - references: {resources.references}")
58-
print(f" - assets: {resources.assets}")
59-
60-
# Load skills from the directory
61-
repo_skills, knowledge_skills, agent_skills = load_skills_from_dir(
62-
example_skills_dir
63-
)
64-
65-
print("\nLoaded skills from directory:")
66-
print(f" - Repo skills: {list(repo_skills.keys())}")
67-
print(f" - Knowledge skills: {list(knowledge_skills.keys())}")
68-
print(f" - Agent skills (SKILL.md): {list(agent_skills.keys())}")
69-
70-
# Access the loaded skill and show all AgentSkills standard fields
71-
if agent_skills:
72-
skill_name = list(agent_skills.keys())[0]
73-
loaded_skill = agent_skills[skill_name]
74-
print(f"\nDetails for '{skill_name}' (AgentSkills standard fields):")
75-
print(f" - Name: {loaded_skill.name}")
76-
desc = loaded_skill.description or ""
77-
print(f" - Description: {desc[:70]}...")
78-
print(f" - License: {loaded_skill.license}")
79-
print(f" - Compatibility: {loaded_skill.compatibility}")
80-
print(f" - Metadata: {loaded_skill.metadata}")
81-
if loaded_skill.resources:
82-
print(" - Resources:")
83-
print(f" - Scripts: {loaded_skill.resources.scripts}")
84-
print(f" - References: {loaded_skill.resources.references}")
85-
print(f" - Assets: {loaded_skill.resources.assets}")
86-
print(f" - Skill root: {loaded_skill.resources.skill_root}")
87-
88-
# =========================================================================
89-
# Part 2: Using Skills with an Agent
90-
# =========================================================================
91-
print("\n" + "=" * 80)
92-
print("Part 2: Using Skills with an Agent")
93-
print("=" * 80)
94-
95-
# Check for API key
96-
api_key = os.getenv("LLM_API_KEY")
97-
if not api_key:
98-
print("Skipping agent demo (LLM_API_KEY not set)")
99-
print("\nTo run the full demo, set the LLM_API_KEY environment variable:")
100-
print(" export LLM_API_KEY=your-api-key")
101-
return
102-
103-
# Configure LLM
104-
model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929")
105-
llm = LLM(
106-
usage_id="skills-demo",
107-
model=model,
108-
api_key=SecretStr(api_key),
109-
base_url=os.getenv("LLM_BASE_URL"),
110-
)
111-
112-
# Create agent context with loaded skills
113-
agent_context = AgentContext(
114-
skills=list(agent_skills.values()),
115-
# Disable public skills for this demo to keep output focused
116-
load_public_skills=False,
117-
)
118-
119-
# Create agent with tools so it can read skill resources
120-
tools = [
121-
Tool(name=TerminalTool.name),
122-
Tool(name=FileEditorTool.name),
123-
]
124-
agent = Agent(llm=llm, tools=tools, agent_context=agent_context)
125-
126-
# Create conversation
127-
conversation = Conversation(agent=agent, workspace=os.getcwd())
128-
129-
# Test the skill (triggered by "encrypt" keyword)
130-
# The skill provides instructions and a script for ROT13 encryption
131-
print("\nSending message with 'encrypt' keyword to trigger skill...")
132-
conversation.send_message("Encrypt the message 'hello world'.")
133-
conversation.run()
134-
135-
print(f"\nTotal cost: ${llm.metrics.accumulated_cost:.4f}")
136-
137-
138-
if __name__ == "__main__":
139-
main()
39+
# Get the directory containing this script
40+
script_dir = Path(__file__).parent
41+
example_skills_dir = script_dir / "example_skills"
42+
43+
# =========================================================================
44+
# Part 1: Loading Skills from a Directory
45+
# =========================================================================
46+
print("=" * 80)
47+
print("Part 1: Loading Skills from a Directory")
48+
print("=" * 80)
49+
50+
print(f"Loading skills from: {example_skills_dir}")
51+
52+
# Discover resources in the skill directory
53+
skill_subdir = example_skills_dir / "rot13-encryption"
54+
resources = discover_skill_resources(skill_subdir)
55+
print("\nDiscovered resources in rot13-encryption/:")
56+
print(f" - scripts: {resources.scripts}")
57+
print(f" - references: {resources.references}")
58+
print(f" - assets: {resources.assets}")
59+
60+
# Load skills from the directory
61+
repo_skills, knowledge_skills, agent_skills = load_skills_from_dir(example_skills_dir)
62+
63+
print("\nLoaded skills from directory:")
64+
print(f" - Repo skills: {list(repo_skills.keys())}")
65+
print(f" - Knowledge skills: {list(knowledge_skills.keys())}")
66+
print(f" - Agent skills (SKILL.md): {list(agent_skills.keys())}")
67+
68+
# Access the loaded skill and show all AgentSkills standard fields
69+
if agent_skills:
70+
skill_name = next(iter(agent_skills))
71+
loaded_skill = agent_skills[skill_name]
72+
print(f"\nDetails for '{skill_name}' (AgentSkills standard fields):")
73+
print(f" - Name: {loaded_skill.name}")
74+
desc = loaded_skill.description or ""
75+
print(f" - Description: {desc[:70]}...")
76+
print(f" - License: {loaded_skill.license}")
77+
print(f" - Compatibility: {loaded_skill.compatibility}")
78+
print(f" - Metadata: {loaded_skill.metadata}")
79+
if loaded_skill.resources:
80+
print(" - Resources:")
81+
print(f" - Scripts: {loaded_skill.resources.scripts}")
82+
print(f" - References: {loaded_skill.resources.references}")
83+
print(f" - Assets: {loaded_skill.resources.assets}")
84+
print(f" - Skill root: {loaded_skill.resources.skill_root}")
85+
86+
# =========================================================================
87+
# Part 2: Using Skills with an Agent
88+
# =========================================================================
89+
print("\n" + "=" * 80)
90+
print("Part 2: Using Skills with an Agent")
91+
print("=" * 80)
92+
93+
# Check for API key
94+
api_key = os.getenv("LLM_API_KEY")
95+
if not api_key:
96+
print("Skipping agent demo (LLM_API_KEY not set)")
97+
print("\nTo run the full demo, set the LLM_API_KEY environment variable:")
98+
print(" export LLM_API_KEY=your-api-key")
99+
sys.exit(0)
100+
101+
# Configure LLM
102+
model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929")
103+
llm = LLM(
104+
usage_id="skills-demo",
105+
model=model,
106+
api_key=SecretStr(api_key),
107+
base_url=os.getenv("LLM_BASE_URL"),
108+
)
109+
110+
# Create agent context with loaded skills
111+
agent_context = AgentContext(
112+
skills=list(agent_skills.values()),
113+
# Disable public skills for this demo to keep output focused
114+
load_public_skills=False,
115+
)
116+
117+
# Create agent with tools so it can read skill resources
118+
tools = [
119+
Tool(name=TerminalTool.name),
120+
Tool(name=FileEditorTool.name),
121+
]
122+
agent = Agent(llm=llm, tools=tools, agent_context=agent_context)
123+
124+
# Create conversation
125+
conversation = Conversation(agent=agent, workspace=os.getcwd())
126+
127+
# Test the skill (triggered by "encrypt" keyword)
128+
# The skill provides instructions and a script for ROT13 encryption
129+
print("\nSending message with 'encrypt' keyword to trigger skill...")
130+
conversation.send_message("Encrypt the message 'hello world'.")
131+
conversation.run()
132+
133+
print(f"\nTotal cost: ${llm.metrics.accumulated_cost:.4f}")
134+
print(f"EXAMPLE_COST: {llm.metrics.accumulated_cost:.4f}")

examples/05_skills_and_plugins/02_loading_plugins/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
print("Skipping agent demo (LLM_API_KEY not set)")
130130
print("\nTo run the full demo, set the LLM_API_KEY environment variable:")
131131
print(" export LLM_API_KEY=your-api-key")
132+
print("EXAMPLE_COST: 0")
132133
sys.exit(0)
133134

134135
# Configure LLM
@@ -137,6 +138,7 @@
137138
usage_id="plugin-demo",
138139
model=model,
139140
api_key=SecretStr(api_key),
141+
base_url=os.getenv("LLM_BASE_URL"),
140142
)
141143

142144
# Create agent context with plugin skills
@@ -203,3 +205,4 @@
203205
print("No hook log file found (hooks may not have executed)")
204206

205207
print(f"\nTotal cost: ${llm.metrics.accumulated_cost:.4f}")
208+
print(f"EXAMPLE_COST: {llm.metrics.accumulated_cost:.4f}")

tests/examples/test_examples.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
_TARGET_DIRECTORIES = (
2626
EXAMPLES_ROOT / "01_standalone_sdk",
2727
EXAMPLES_ROOT / "02_remote_agent_server",
28+
# These examples live under subdirectories (each with a single `main.py`).
29+
EXAMPLES_ROOT / "05_skills_and_plugins" / "01_loading_agentskills",
30+
EXAMPLES_ROOT / "05_skills_and_plugins" / "02_loading_plugins",
2831
)
2932

3033
# LLM-specific examples that require model overrides

0 commit comments

Comments
 (0)