riffer 0.24.2 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c289127e9ee54b63afe3ca65349054459f906553e37d39cf5001c6876ee6b1e9
4
- data.tar.gz: 22e8286f74b98f23d00e49c6c337bcba9c68f452763be082533fb0f082688632
3
+ metadata.gz: 55bd24093ac1af4a33163b95afc320f2f3326bf6105b8e55253e78bd0763551d
4
+ data.tar.gz: dc70e95bb46d2a7b51840299a2da6a4f79a27421791a06d18255c655afcd4cfe
5
5
  SHA512:
6
- metadata.gz: f2e75464955df9f0fd7dd1d57eef8b54a763d77d8d80ba46ff0bc2c3e11c8fa258de633ff5288551be299d8a92b10c99419ab95acdcb86683f9fb1ca0331ead2
7
- data.tar.gz: ce4a3e9ee805e074142a02a84ceb6089be539fb22d0a4d141de4eef43d8b9a0843f931ab05298571c79e933f1d1a6336c4f8b3ea443da403485e46a001d4dc45
6
+ metadata.gz: 992ab0a333cedb5107bc90fcf00f0d48151ea4cb09ceadb455dd80c95d06fd00ba6e005405b844f2857619f3a549dd0c8ff82939ea764db0b280fa22643f8511
7
+ data.tar.gz: 2de5d6c8add2a20442cf38d6d216384379fa270f115bc92cf81fa0ded554573de5a513694845615365fdb041308a8c2d830ab47044fbddf30e741c42107681c1
data/.bundle/config ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ BUNDLE_LOCKFILE_CHECKSUMS: "false"
3
+ BUNDLE_PATH: "/home/runner/work/riffer/riffer/vendor/bundle"
4
+ BUNDLE_DEPLOYMENT: "true"
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "release-type": "ruby",
3
+ "bump-minor-pre-major": true,
3
4
  "packages": {
4
5
  ".": {
5
6
  "release-type": "ruby",
6
7
  "package-name": "riffer",
7
- "version-file": "lib/riffer/version.rb"
8
+ "version-file": "lib/riffer/version.rb",
9
+ "bump-minor-pre-major": true
8
10
  }
9
11
  }
10
12
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.24.2"
2
+ ".": "0.25.0"
3
3
  }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.8
1
+ 4.0.3
data/AGENTS.md CHANGED
@@ -4,7 +4,7 @@ Ruby gem framework for building AI-powered agents with LLM provider adapters.
4
4
 
5
5
  ## Quick Reference
6
6
 
7
- - **Ruby**: 3.3.0+
7
+ - **Ruby**: 3.3.0+ (CI: 3.3, 3.4, 4.0)
8
8
  - **Lint + Test**: `bundle exec rake`
9
9
  - **Autoloading**: Zeitwerk (file paths must match module/class names)
10
10
  - **Model format**: `provider/model` (e.g., `openai/gpt-4`)
data/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.25.0](https://github.com/janeapp/riffer/compare/riffer/v0.24.2...riffer/v0.25.0) (2026-04-29)
9
+
10
+
11
+ ### ⚠ BREAKING CHANGES
12
+
13
+ * Removed Riffer::Skills::Adapter#activate_tool. Set the activation tool via Riffer.config.skills.default_activate_tool, or per-agent with `skills do; activate_tool MyTool; end`.
14
+ * Riffer::Skills::Adapter.new now requires skill_activate_tool:. The agent wires this up; only matters if you construct an adapter yourself. Custom adapters that override #initialize must call super.
15
+
16
+ ### Features
17
+
18
+ * class-level tool resolution; lift activation tool to global config ([#230](https://github.com/janeapp/riffer/issues/230)) ([936c4ba](https://github.com/janeapp/riffer/commit/936c4baa6beff4f2cd6c87bfe90be8c20a7f30e1))
19
+
8
20
  ## [0.24.2](https://github.com/janeapp/riffer/compare/riffer/v0.24.1...riffer/v0.24.2) (2026-04-23)
9
21
 
10
22
 
data/README.md CHANGED
@@ -6,7 +6,7 @@ The all-in-one Ruby framework for building AI-powered applications and agents.
6
6
 
7
7
  ## Requirements
8
8
 
9
- - Ruby 3.3 or later
9
+ - Ruby 3.3, 3.4, or 4.0
10
10
 
11
11
  ## Installation
12
12
 
@@ -54,6 +54,34 @@ end
54
54
 
55
55
  Per-agent configuration overrides this global default. See [Advanced Tool Configuration — Tool Runtime](07_TOOL_ADVANCED.md#tool-runtime-experimental) for details.
56
56
 
57
+ ### Skills
58
+
59
+ Skills-related global configuration lives under `config.skills`.
60
+
61
+ #### Default activation tool
62
+
63
+ Override the tool the LLM calls to activate a skill. Defaults to `Riffer::Skills::ActivateTool`:
64
+
65
+ ```ruby
66
+ Riffer.configure do |config|
67
+ config.skills.default_activate_tool = MyCustomActivateTool
68
+ end
69
+ ```
70
+
71
+ Per-agent override is available inside the `skills` block via `activate_tool MyCustomActivateTool`. See [Skills — Custom Activation Tool](13_SKILLS.md#custom-activation-tool).
72
+
73
+ #### Default backend
74
+
75
+ Set an app-wide default skills backend. Used by any agent that declares a `skills` block without specifying its own `backend`:
76
+
77
+ ```ruby
78
+ Riffer.configure do |config|
79
+ config.skills.default_backend = Riffer::Skills::FilesystemBackend.new(".skills")
80
+ end
81
+ ```
82
+
83
+ Accepts a `Riffer::Skills::Backend` instance or a `Proc` that receives `context` and returns a backend. Defaults to `nil` — agents that don't set their own backend get no skills, matching pre-existing behavior. Per-agent backends override this default.
84
+
57
85
  ### Message ID Strategy
58
86
 
59
87
  Opt in to stable identifiers on every message for logging, persistence, or replay:
data/docs/13_SKILLS.md CHANGED
@@ -125,24 +125,48 @@ end
125
125
 
126
126
  ## Custom Adapters
127
127
 
128
- Subclass `Riffer::Skills::Adapter` to customize how the skill catalog is rendered and which tool the LLM uses to activate skills:
128
+ Subclass `Riffer::Skills::Adapter` to customize how the skill catalog is rendered in the system prompt:
129
129
 
130
130
  ```ruby
131
131
  class CustomAdapter < Riffer::Skills::Adapter
132
132
  def render_catalog(skills)
133
133
  # Return String (skill catalog for the system prompt)
134
- # Use `activate_tool.name` to reference the activation tool
134
+ # Use `skill_activate_tool.name` to reference the activation tool the LLM should call
135
135
  end
136
+ end
137
+ ```
138
+
139
+ The activation tool is set on the adapter at construction (`Riffer::Skills::Adapter.new(skill_activate_tool: ...)`) and exposed via the `skill_activate_tool` reader. The agent wires this up automatically — custom adapters that override `initialize` must call `super`.
140
+
141
+ The built-in adapters are `Riffer::Skills::MarkdownAdapter` (default) and `Riffer::Skills::XmlAdapter` (used by Anthropic).
136
142
 
137
- def activate_tool
138
- # Return a Riffer::Tool subclass
139
- # Defaults to Riffer::Skills::ActivateTool
140
- Riffer::Skills::ActivateTool
143
+ ## Custom Activation Tool
144
+
145
+ The activation tool is global. Set it once via `Riffer.config.skills.default_activate_tool` to apply across all agents, or override per-agent inside the `skills` block.
146
+
147
+ The recommended approach is to subclass `Riffer::Skills::ActivateTool` so the identifier, description, params, and timeout are inherited — you only override the behavior you need to change:
148
+
149
+ ```ruby
150
+ # Wrap the default behavior with telemetry
151
+ class InstrumentedActivateTool < Riffer::Skills::ActivateTool
152
+ def call(context:, name:)
153
+ Telemetry.measure("skill_activate", skill: name) { super }
154
+ end
155
+ end
156
+
157
+ # Global default
158
+ Riffer.config.skills.default_activate_tool = InstrumentedActivateTool
159
+
160
+ # Per-agent override
161
+ class MyAgent < Riffer::Agent
162
+ skills do
163
+ backend Riffer::Skills::FilesystemBackend.new(".skills")
164
+ activate_tool InstrumentedActivateTool
141
165
  end
142
166
  end
143
167
  ```
144
168
 
145
- The built-in adapters are `Riffer::Skills::MarkdownAdapter` (default) and `Riffer::Skills::XmlAdapter` (used by Anthropic).
169
+ If you need a different parameter shape entirely, subclass `Riffer::Tool` directly and provide your own `identifier`, `description`, `params`, and `call`.
146
170
 
147
171
  ## Accessing Skills in Tools
148
172
 
data/lib/riffer/agent.rb CHANGED
@@ -132,6 +132,46 @@ class Riffer::Agent
132
132
  @tools_config = tools_or_lambda
133
133
  end
134
134
 
135
+ # Returns the tool classes the LLM should see for this agent.
136
+ #
137
+ # Class-level companion to the instance #resolved_tools. Resolves the
138
+ # Proc form of +uses_tools+ and appends the skill activation tool when
139
+ # a +skills+ block is configured. Does not read the skills backend —
140
+ # the LLM-facing tool schema reflects class-level intent, not the
141
+ # runtime state of any backend.
142
+ #
143
+ # When +uses_tools+ is a Proc, +context+ is forwarded to it.
144
+ #
145
+ # The activation tool class is resolved from the agent's
146
+ # <tt>skills do; activate_tool ...; end</tt> override when set, otherwise
147
+ # from <tt>Riffer.config.skills.default_activate_tool</tt>.
148
+ #
149
+ # Raises Riffer::ArgumentError on tool name conflicts with the skill
150
+ # activation tool.
151
+ #
152
+ #--
153
+ #: (?context: Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
154
+ def self.resolved_tool_classes(context: nil)
155
+ base = resolve_uses_tools_config(context)
156
+ return base unless skills
157
+
158
+ skill_activate_tool_class = skills.activate_tool || Riffer.config.skills.default_activate_tool
159
+ if base.any? { |t| t.name == skill_activate_tool_class.name }
160
+ raise Riffer::ArgumentError, "Tool name conflict with skill tools: #{skill_activate_tool_class.name}"
161
+ end
162
+ base + [skill_activate_tool_class]
163
+ end
164
+
165
+ #--
166
+ #: (Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
167
+ def self.resolve_uses_tools_config(context)
168
+ config = uses_tools
169
+ return [] if config.nil?
170
+ return config unless config.is_a?(Proc)
171
+ config.arity.zero? ? config.call : config.call(context)
172
+ end
173
+ private_class_method :resolve_uses_tools_config
174
+
135
175
  # Gets or sets the tool runtime for this agent.
136
176
  #
137
177
  # Accepts a Riffer::ToolRuntime subclass, a Riffer::ToolRuntime instance,
@@ -717,25 +757,7 @@ class Riffer::Agent
717
757
  #--
718
758
  #: () -> Array[singleton(Riffer::Tool)]
719
759
  def resolved_tools
720
- @resolved_tools ||= begin
721
- config = self.class.uses_tools
722
-
723
- tools = if config.nil?
724
- []
725
- elsif config.is_a?(Proc)
726
- (config.arity == 0) ? config.call : config.call(@context)
727
- else
728
- config
729
- end
730
-
731
- if @skills_state
732
- activate_tool = @skills_state.adapter.activate_tool
733
- raise Riffer::ArgumentError, "Tool name conflict with skill tools: #{activate_tool.name}" if tools.any? { |t| t.name == activate_tool.name }
734
- tools + [activate_tool]
735
- else
736
- tools
737
- end
738
- end
760
+ @resolved_tools ||= self.class.resolved_tool_classes(context: @context)
739
761
  end
740
762
 
741
763
  #--
@@ -769,7 +791,7 @@ class Riffer::Agent
769
791
  def resolve_skills(context = @context)
770
792
  return nil unless self.class.skills
771
793
 
772
- backend = self.class.skills.backend
794
+ backend = self.class.skills.backend || Riffer.config.skills.default_backend
773
795
  return nil unless backend
774
796
 
775
797
  backend = backend.is_a?(Proc) ? backend.call(context) : backend
@@ -778,8 +800,13 @@ class Riffer::Agent
778
800
 
779
801
  skills = skills_list.to_h { |s| [s.name, s] }
780
802
  adapter_class = self.class.skills.adapter || provider_class.skills_adapter
803
+ skill_activate_tool_class = self.class.skills.activate_tool || Riffer.config.skills.default_activate_tool
781
804
 
782
- skills_context = Riffer::Skills::Context.new(backend: backend, skills: skills, adapter: adapter_class.new)
805
+ skills_context = Riffer::Skills::Context.new(
806
+ backend: backend,
807
+ skills: skills,
808
+ adapter: adapter_class.new(skill_activate_tool: skill_activate_tool_class)
809
+ )
783
810
  ctx = (context || {}).merge(skills: skills_context)
784
811
 
785
812
  activate = self.class.skills.activate
@@ -802,7 +829,10 @@ class Riffer::Agent
802
829
  #--
803
830
  #: () -> Riffer::Messages::Assistant?
804
831
  def extract_final_response
832
+ # TODO: Replace with rfind when minimum Ruby is 4.0+
833
+ # rubocop:disable Style/ReverseFind
805
834
  @messages.reverse.find { |msg| msg.is_a?(Riffer::Messages::Assistant) } #: Riffer::Messages::Assistant?
835
+ # rubocop:enable Style/ReverseFind
806
836
  end
807
837
 
808
838
  #--
data/lib/riffer/config.rb CHANGED
@@ -22,6 +22,57 @@ class Riffer::Config
22
22
  OpenAI = Struct.new(:api_key, keyword_init: true)
23
23
  Evals = Struct.new(:judge_model, keyword_init: true)
24
24
 
25
+ # Skills-related global configuration.
26
+ #
27
+ # See <tt>Riffer.config.skills.default_activate_tool</tt> and
28
+ # <tt>Riffer.config.skills.default_backend</tt>.
29
+ class Skills
30
+ # Default skill activation tool class.
31
+ #
32
+ # The tool class the LLM calls to activate a skill. Defaults to
33
+ # <tt>Riffer::Skills::ActivateTool</tt>. Per-agent override is available
34
+ # via <tt>skills do; activate_tool ...; end</tt>.
35
+ attr_reader :default_activate_tool #: singleton(Riffer::Tool)
36
+
37
+ # Default skills backend.
38
+ #
39
+ # Used by agents that declare a +skills+ block without specifying a
40
+ # backend. Accepts a Riffer::Skills::Backend instance or a Proc.
41
+ # Defaults to +nil+ (no global default).
42
+ attr_reader :default_backend #: (Riffer::Skills::Backend | Proc)?
43
+
44
+ #--
45
+ #: () -> void
46
+ def initialize
47
+ @default_activate_tool = Riffer::Skills::ActivateTool
48
+ @default_backend = nil
49
+ end
50
+
51
+ # Sets the default skill activation tool class.
52
+ #
53
+ # Raises +Riffer::ArgumentError+ if the value is not a Riffer::Tool subclass.
54
+ #
55
+ #--
56
+ #: (singleton(Riffer::Tool)) -> void
57
+ def default_activate_tool=(value)
58
+ raise Riffer::ArgumentError, "default_activate_tool must be a Riffer::Tool subclass" unless value.is_a?(Class) && value < Riffer::Tool
59
+ @default_activate_tool = value
60
+ end
61
+
62
+ # Sets the default skills backend.
63
+ #
64
+ # Raises +Riffer::ArgumentError+ if the value is not a
65
+ # Riffer::Skills::Backend instance, a Proc, or +nil+.
66
+ #
67
+ #--
68
+ #: ((Riffer::Skills::Backend | Proc)?) -> void
69
+ def default_backend=(value)
70
+ valid = value.nil? || value.is_a?(Riffer::Skills::Backend) || value.is_a?(Proc)
71
+ raise Riffer::ArgumentError, "default_backend must be a Riffer::Skills::Backend instance, Proc, or nil" unless valid
72
+ @default_backend = value
73
+ end
74
+ end
75
+
25
76
  VALID_MESSAGE_ID_STRATEGIES = %i[none uuid uuidv7].freeze
26
77
 
27
78
  # Amazon Bedrock configuration (Struct with +api_token+ and +region+).
@@ -61,6 +112,10 @@ class Riffer::Config
61
112
  @tool_runtime = value
62
113
  end
63
114
 
115
+ # Skills-related global configuration. Returns a Riffer::Config::Skills
116
+ # object — see <tt>Riffer.config.skills.default_activate_tool</tt>.
117
+ attr_reader :skills #: Riffer::Config::Skills
118
+
64
119
  # Strategy for auto-generating message ids. One of +:none+ (default, no id),
65
120
  # +:uuid+ (UUIDv4), or +:uuidv7+ (time-ordered UUIDv7).
66
121
  #
@@ -94,6 +149,7 @@ class Riffer::Config
94
149
  @openai = OpenAI.new
95
150
  @evals = Evals.new
96
151
  @tool_runtime = Riffer::ToolRuntime::Inline.new
152
+ @skills = Skills.new
97
153
  @message_id_strategy = :none
98
154
  end
99
155
  end
@@ -3,16 +3,31 @@
3
3
 
4
4
  # Base class defining the interface for skill adapters.
5
5
  #
6
- # A skill adapter encapsulates the provider-specific behavior for skills:
7
- # how the skill catalog is rendered in the system prompt, and which tool
8
- # the LLM calls to activate a skill.
6
+ # A skill adapter encapsulates the provider-specific catalog rendering for
7
+ # skills — how the available-skills section appears in the system prompt.
9
8
  #
10
- # Subclass and override +render_catalog+ and optionally +activate_tool+
11
- # to customize behavior.
9
+ # Subclass and override +render_catalog+. The activation tool is set at
10
+ # construction time and is exposed via +#skill_activate_tool+ for use in
11
+ # rendered output.
12
12
  #
13
13
  # See Riffer::Skills::MarkdownAdapter for the default implementation.
14
14
  # See Riffer::Skills::XmlAdapter for the Anthropic/Claude variant.
15
15
  class Riffer::Skills::Adapter
16
+ # The activation tool class for this adapter.
17
+ attr_reader :skill_activate_tool #: singleton(Riffer::Tool)
18
+
19
+ # Creates a new adapter.
20
+ #
21
+ # [skill_activate_tool] the activation tool class — referenced by name
22
+ # in the rendered catalog so the LLM knows which
23
+ # tool to call.
24
+ #
25
+ #--
26
+ #: (skill_activate_tool: singleton(Riffer::Tool)) -> void
27
+ def initialize(skill_activate_tool:)
28
+ @skill_activate_tool = skill_activate_tool
29
+ end
30
+
16
31
  # Renders a skill catalog section for the system prompt.
17
32
  #
18
33
  # [skills] array of Frontmatter objects to render.
@@ -24,14 +39,4 @@ class Riffer::Skills::Adapter
24
39
  def render_catalog(skills)
25
40
  raise NotImplementedError, "#{self.class} must implement #render_catalog"
26
41
  end
27
-
28
- # Returns the tool class used to activate skills.
29
- #
30
- # Override to provide a custom activation tool.
31
- #
32
- #--
33
- #: () -> singleton(Riffer::Tool)
34
- def activate_tool
35
- Riffer::Skills::ActivateTool
36
- end
37
42
  end
@@ -19,6 +19,7 @@ class Riffer::Skills::Config
19
19
  @backend = nil
20
20
  @adapter = nil
21
21
  @activate = nil
22
+ @activate_tool = nil
22
23
  end
23
24
 
24
25
  # Gets or sets the skills backend.
@@ -59,4 +60,21 @@ class Riffer::Skills::Config
59
60
  return @activate if value.nil?
60
61
  @activate = value
61
62
  end
63
+
64
+ # Gets or sets the per-agent override for the skill activation tool class.
65
+ #
66
+ # Returns the configured override when set, or +nil+ when unset. The
67
+ # global fallback to <tt>Riffer.config.skills.default_activate_tool</tt>
68
+ # is applied by the agent at resolution time (see
69
+ # Riffer::Agent.resolved_tool_classes), not by this getter.
70
+ #
71
+ # The override must be a subclass of Riffer::Tool.
72
+ #
73
+ #--
74
+ #: (?singleton(Riffer::Tool)?) -> singleton(Riffer::Tool)?
75
+ def activate_tool(value = nil)
76
+ return @activate_tool if value.nil?
77
+ raise Riffer::ArgumentError, "activate_tool must be a Riffer::Tool subclass" unless value.is_a?(Class) && value < Riffer::Tool
78
+ @activate_tool = value
79
+ end
62
80
  end
@@ -24,7 +24,8 @@ class Riffer::Skills::Context
24
24
  #
25
25
  # [backend] the skills backend for reading skill bodies.
26
26
  # [skills] skill catalog indexed by name.
27
- # [adapter] the adapter used to render skill content.
27
+ # [adapter] the adapter used to render skill content. The adapter
28
+ # carries the activation tool class via its initializer.
28
29
  #
29
30
  #--
30
31
  #: (backend: Riffer::Skills::Backend, skills: Hash[String, Riffer::Skills::Frontmatter], adapter: Riffer::Skills::Adapter) -> void
@@ -18,7 +18,7 @@ class Riffer::Skills::MarkdownAdapter < Riffer::Skills::Adapter
18
18
  lines = []
19
19
  lines << "## Available Skills"
20
20
  lines << ""
21
- lines << "When a user's request matches a skill description below, call the `#{activate_tool.name}` tool with the skill name. After activation, follow the skill's instructions."
21
+ lines << "When a user's request matches a skill description below, call the `#{skill_activate_tool.name}` tool with the skill name. After activation, follow the skill's instructions."
22
22
  lines << ""
23
23
  skills.each do |skill|
24
24
  lines << "- **#{skill.name}**: #{skill.description}"
@@ -17,7 +17,7 @@ class Riffer::Skills::XmlAdapter < Riffer::Skills::Adapter
17
17
  #: (Array[Riffer::Skills::Frontmatter]) -> String
18
18
  def render_catalog(skills)
19
19
  lines = []
20
- lines << "When a user's request matches a skill description below, call the `#{activate_tool.name}` tool with the skill name. After activation, follow the skill's instructions."
20
+ lines << "When a user's request matches a skill description below, call the `#{skill_activate_tool.name}` tool with the skill name. After activation, follow the skill's instructions."
21
21
  lines << ""
22
22
  lines << "<available_skills>"
23
23
  skills.each do |skill|
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.24.2" #: String
5
+ VERSION = "0.25.0" #: String
6
6
  end
@@ -88,6 +88,31 @@ class Riffer::Agent
88
88
  # : (?(Array[singleton(Riffer::Tool)] | Proc)?) -> (Array[singleton(Riffer::Tool)] | Proc)?
89
89
  def self.uses_tools: (?(Array[singleton(Riffer::Tool)] | Proc)?) -> (Array[singleton(Riffer::Tool)] | Proc)?
90
90
 
91
+ # Returns the tool classes the LLM should see for this agent.
92
+ #
93
+ # Class-level companion to the instance #resolved_tools. Resolves the
94
+ # Proc form of +uses_tools+ and appends the skill activation tool when
95
+ # a +skills+ block is configured. Does not read the skills backend —
96
+ # the LLM-facing tool schema reflects class-level intent, not the
97
+ # runtime state of any backend.
98
+ #
99
+ # When +uses_tools+ is a Proc, +context+ is forwarded to it.
100
+ #
101
+ # The activation tool class is resolved from the agent's
102
+ # <tt>skills do; activate_tool ...; end</tt> override when set, otherwise
103
+ # from <tt>Riffer.config.skills.default_activate_tool</tt>.
104
+ #
105
+ # Raises Riffer::ArgumentError on tool name conflicts with the skill
106
+ # activation tool.
107
+ #
108
+ # --
109
+ # : (?context: Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
110
+ def self.resolved_tool_classes: (?context: Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
111
+
112
+ # --
113
+ # : (Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
114
+ def self.resolve_uses_tools_config: (Hash[Symbol, untyped]?) -> Array[singleton(Riffer::Tool)]
115
+
91
116
  # Gets or sets the tool runtime for this agent.
92
117
  #
93
118
  # Accepts a Riffer::ToolRuntime subclass, a Riffer::ToolRuntime instance,
@@ -63,6 +63,47 @@ class Riffer::Config
63
63
  | ({ ?judge_model: untyped }) -> instance
64
64
  end
65
65
 
66
+ # Skills-related global configuration.
67
+ #
68
+ # See <tt>Riffer.config.skills.default_activate_tool</tt> and
69
+ # <tt>Riffer.config.skills.default_backend</tt>.
70
+ class Skills
71
+ # Default skill activation tool class.
72
+ #
73
+ # The tool class the LLM calls to activate a skill. Defaults to
74
+ # <tt>Riffer::Skills::ActivateTool</tt>. Per-agent override is available
75
+ # via <tt>skills do; activate_tool ...; end</tt>.
76
+ attr_reader default_activate_tool: singleton(Riffer::Tool)
77
+
78
+ # Default skills backend.
79
+ #
80
+ # Used by agents that declare a +skills+ block without specifying a
81
+ # backend. Accepts a Riffer::Skills::Backend instance or a Proc.
82
+ # Defaults to +nil+ (no global default).
83
+ attr_reader default_backend: (Riffer::Skills::Backend | Proc)?
84
+
85
+ # --
86
+ # : () -> void
87
+ def initialize: () -> void
88
+
89
+ # Sets the default skill activation tool class.
90
+ #
91
+ # Raises +Riffer::ArgumentError+ if the value is not a Riffer::Tool subclass.
92
+ #
93
+ # --
94
+ # : (singleton(Riffer::Tool)) -> void
95
+ def default_activate_tool=: (singleton(Riffer::Tool)) -> void
96
+
97
+ # Sets the default skills backend.
98
+ #
99
+ # Raises +Riffer::ArgumentError+ if the value is not a
100
+ # Riffer::Skills::Backend instance, a Proc, or +nil+.
101
+ #
102
+ # --
103
+ # : ((Riffer::Skills::Backend | Proc)?) -> void
104
+ def default_backend=: ((Riffer::Skills::Backend | Proc)?) -> void
105
+ end
106
+
66
107
  VALID_MESSAGE_ID_STRATEGIES: untyped
67
108
 
68
109
  # Amazon Bedrock configuration (Struct with +api_token+ and +region+).
@@ -98,6 +139,10 @@ class Riffer::Config
98
139
  # : ((singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc)) -> void
99
140
  def tool_runtime=: (singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc) -> void
100
141
 
142
+ # Skills-related global configuration. Returns a Riffer::Config::Skills
143
+ # object — see <tt>Riffer.config.skills.default_activate_tool</tt>.
144
+ attr_reader skills: Riffer::Config::Skills
145
+
101
146
  # Strategy for auto-generating message ids. One of +:none+ (default, no id),
102
147
  # +:uuid+ (UUIDv4), or +:uuidv7+ (time-ordered UUIDv7).
103
148
  #
@@ -2,16 +2,29 @@
2
2
 
3
3
  # Base class defining the interface for skill adapters.
4
4
  #
5
- # A skill adapter encapsulates the provider-specific behavior for skills:
6
- # how the skill catalog is rendered in the system prompt, and which tool
7
- # the LLM calls to activate a skill.
5
+ # A skill adapter encapsulates the provider-specific catalog rendering for
6
+ # skills — how the available-skills section appears in the system prompt.
8
7
  #
9
- # Subclass and override +render_catalog+ and optionally +activate_tool+
10
- # to customize behavior.
8
+ # Subclass and override +render_catalog+. The activation tool is set at
9
+ # construction time and is exposed via +#skill_activate_tool+ for use in
10
+ # rendered output.
11
11
  #
12
12
  # See Riffer::Skills::MarkdownAdapter for the default implementation.
13
13
  # See Riffer::Skills::XmlAdapter for the Anthropic/Claude variant.
14
14
  class Riffer::Skills::Adapter
15
+ # The activation tool class for this adapter.
16
+ attr_reader skill_activate_tool: singleton(Riffer::Tool)
17
+
18
+ # Creates a new adapter.
19
+ #
20
+ # [skill_activate_tool] the activation tool class — referenced by name
21
+ # in the rendered catalog so the LLM knows which
22
+ # tool to call.
23
+ #
24
+ # --
25
+ # : (skill_activate_tool: singleton(Riffer::Tool)) -> void
26
+ def initialize: (skill_activate_tool: singleton(Riffer::Tool)) -> void
27
+
15
28
  # Renders a skill catalog section for the system prompt.
16
29
  #
17
30
  # [skills] array of Frontmatter objects to render.
@@ -21,12 +34,4 @@ class Riffer::Skills::Adapter
21
34
  # --
22
35
  # : (Array[Riffer::Skills::Frontmatter]) -> String
23
36
  def render_catalog: (Array[Riffer::Skills::Frontmatter]) -> String
24
-
25
- # Returns the tool class used to activate skills.
26
- #
27
- # Override to provide a custom activation tool.
28
- #
29
- # --
30
- # : () -> singleton(Riffer::Tool)
31
- def activate_tool: () -> singleton(Riffer::Tool)
32
37
  end
@@ -45,4 +45,17 @@ class Riffer::Skills::Config
45
45
  # --
46
46
  # : (?(Array[String] | Proc)?) -> (Array[String] | Proc)?
47
47
  def activate: (?(Array[String] | Proc)?) -> (Array[String] | Proc)?
48
+
49
+ # Gets or sets the per-agent override for the skill activation tool class.
50
+ #
51
+ # Returns the configured override when set, or +nil+ when unset. The
52
+ # global fallback to <tt>Riffer.config.skills.default_activate_tool</tt>
53
+ # is applied by the agent at resolution time (see
54
+ # Riffer::Agent.resolved_tool_classes), not by this getter.
55
+ #
56
+ # The override must be a subclass of Riffer::Tool.
57
+ #
58
+ # --
59
+ # : (?singleton(Riffer::Tool)?) -> singleton(Riffer::Tool)?
60
+ def activate_tool: (?singleton(Riffer::Tool)?) -> singleton(Riffer::Tool)?
48
61
  end
@@ -23,7 +23,8 @@ class Riffer::Skills::Context
23
23
  #
24
24
  # [backend] the skills backend for reading skill bodies.
25
25
  # [skills] skill catalog indexed by name.
26
- # [adapter] the adapter used to render skill content.
26
+ # [adapter] the adapter used to render skill content. The adapter
27
+ # carries the activation tool class via its initializer.
27
28
  #
28
29
  # --
29
30
  # : (backend: Riffer::Skills::Backend, skills: Hash[String, Riffer::Skills::Frontmatter], adapter: Riffer::Skills::Adapter) -> void
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.2
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - "~>"
37
37
  - !ruby/object:Gem::Version
38
- version: 1.35.0
38
+ version: 1.36.0
39
39
  type: :development
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
- version: 1.35.0
45
+ version: 1.36.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: aws-sdk-bedrockruntime
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -221,6 +221,7 @@ files:
221
221
  - ".agents/rbs-inline.md"
222
222
  - ".agents/rdoc.md"
223
223
  - ".agents/testing.md"
224
+ - ".bundle/config"
224
225
  - ".release-please-config.json"
225
226
  - ".release-please-manifest.json"
226
227
  - ".ruby-version"
@@ -449,7 +450,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
449
450
  - !ruby/object:Gem::Version
450
451
  version: '0'
451
452
  requirements: []
452
- rubygems_version: 3.6.9
453
+ rubygems_version: 4.0.6
453
454
  specification_version: 4
454
455
  summary: The all-in-one Ruby framework for building AI-powered applications and agents.
455
456
  test_files: []