Class: LLM::Skill
- Inherits:
-
Object
- Object
- LLM::Skill
- Defined in:
- lib/llm/skill.rb
Overview
LLM::Skill represents a directory-backed packaged capability.
A skill directory must contain a SKILL.md file with YAML frontmatter.
Skills can expose themselves as normal LLM::Tool classes through
#to_tool. This keeps skills on the same execution path as local tools.
Instance Attribute Summary collapse
-
#description ⇒ String
readonly
Returns the skill description.
-
#frontmatter ⇒ LLM::Object
readonly
Returns the skill frontmatter.
-
#instructions ⇒ String
readonly
Returns the skill instructions.
-
#name ⇒ String
readonly
Returns the skill name.
-
#path ⇒ String
readonly
Returns the skill directory.
-
#tools ⇒ Array<Class<LLM::Tool>>
readonly
Returns the skill tools.
Class Method Summary collapse
-
.load(path) ⇒ LLM::Skill
Load a skill from a directory.
Instance Method Summary collapse
-
#call(ctx) ⇒ Hash
Execute the skill by wrapping it in a small agent with the skill instructions.
-
#inherit_tools? ⇒ Boolean
Returns true when a skill should inherit tools from its parent.
- #initialize(path) ⇒ LLM::Skill constructor
-
#load! ⇒ LLM::Skill
Load and parse the skill.
-
#to_tool(ctx) ⇒ Class<LLM::Tool>
Expose the skill as a normal LLM::Tool.
Constructor Details
#initialize(path) ⇒ LLM::Skill
57 58 59 60 61 62 63 64 65 |
# File 'lib/llm/skill.rb', line 57 def initialize(path) @path = path.to_s @name = ::File.basename(@path) @description = "Skill: #{@name}" @instructions = "" @frontmatter = LLM::Object.from({}) @tools = [] @inherit_tools = false end |
Instance Attribute Details
#description ⇒ String (readonly)
Returns the skill description.
36 37 38 |
# File 'lib/llm/skill.rb', line 36 def description @description end |
#frontmatter ⇒ LLM::Object (readonly)
Returns the skill frontmatter.
46 47 48 |
# File 'lib/llm/skill.rb', line 46 def frontmatter @frontmatter end |
#instructions ⇒ String (readonly)
Returns the skill instructions.
41 42 43 |
# File 'lib/llm/skill.rb', line 41 def instructions @instructions end |
#name ⇒ String (readonly)
Returns the skill name.
31 32 33 |
# File 'lib/llm/skill.rb', line 31 def name @name end |
#path ⇒ String (readonly)
Returns the skill directory.
26 27 28 |
# File 'lib/llm/skill.rb', line 26 def path @path end |
#tools ⇒ Array<Class<LLM::Tool>> (readonly)
Returns the skill tools.
51 52 53 |
# File 'lib/llm/skill.rb', line 51 def tools @tools end |
Class Method Details
.load(path) ⇒ LLM::Skill
Load a skill from a directory.
19 20 21 |
# File 'lib/llm/skill.rb', line 19 def self.load(path) new(path).tap(&:load!) end |
Instance Method Details
#call(ctx) ⇒ Hash
Execute the skill by wrapping it in a small agent with the skill instructions. The context is bound explicitly by the caller so the nested agent can inherit context-level behavior such as streaming.
89 90 91 92 93 94 95 96 |
# File 'lib/llm/skill.rb', line 89 def call(ctx) stream = ctx.stream stream.on_skill_call(self) agent = self.agent(ctx) res = agent.talk("Solve the user's query.") stream.on_skill_return(agent, self, res) {content: res.content} end |
#inherit_tools? ⇒ Boolean
Returns true when a skill should inherit tools from its parent
133 134 135 |
# File 'lib/llm/skill.rb', line 133 def inherit_tools? @inherit_tools end |
#load! ⇒ LLM::Skill
Load and parse the skill.
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/llm/skill.rb', line 70 def load! if File.file?(@path) parse(File.read(@path)) elsif File.directory?(@path) path = File.join(@path, "SKILL.md") parse(File.read(path)) else raise LLM::Error, "'#{@path}' is neither a file or directory" end self end |
#to_tool(ctx) ⇒ Class<LLM::Tool>
Expose the skill as a normal LLM::Tool. The context is bound explicitly when the tool class is built.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/llm/skill.rb', line 104 def to_tool(ctx) skill = self Class.new(LLM::Tool) do attr_accessor :tracer ## # The skill's tool is named "<skill>-skill" so # that it is unique from any tool the skill # itself exposes (or any global tool). Otherwise # a skill named `weather` that uses a `weather` # tool would collide: both resolve to the same # name. name "#{skill.name}-skill" description skill.description define_singleton_method(:skill?) do true end define_method(:call) do skill.call(ctx) end end end |