riffer 0.41.0 → 0.43.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 +4 -4
- data/{AGENTS.md → .claude/CLAUDE.md} +0 -8
- data/.claude/rules/comments.md +13 -0
- data/{.agents → .claude/rules}/rbs-inline.md +28 -104
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +22 -0
- data/docs/AGENTS.md +24 -3
- data/docs/AGENT_LIFECYCLE.md +2 -2
- data/docs/AGENT_LOOP.md +6 -4
- data/docs/TOOLS.md +22 -2
- data/docs/TOOL_ADVANCED.md +20 -7
- data/docs/TRACING.md +5 -4
- data/lib/riffer/agent/run.rb +2 -4
- data/lib/riffer/agent.rb +3 -17
- data/lib/riffer/guardrail.rb +1 -1
- data/lib/riffer/helpers/identifier.rb +41 -0
- data/lib/riffer/messages/tool.rb +2 -1
- data/lib/riffer/providers/anthropic.rb +4 -0
- data/lib/riffer/providers/base.rb +4 -1
- data/lib/riffer/registrable.rb +81 -0
- data/lib/riffer/tool.rb +25 -9
- data/lib/riffer/tools/response.rb +10 -5
- data/lib/riffer/tools/runtime.rb +20 -11
- data/lib/riffer/tools/toolable.rb +2 -3
- data/lib/riffer/version.rb +1 -1
- data/lib/riffer.rb +6 -1
- data/sig/generated/riffer/agent.rbs +2 -12
- data/sig/generated/riffer/helpers/identifier.rbs +19 -0
- data/sig/generated/riffer/messages/tool.rbs +2 -1
- data/sig/generated/riffer/providers/base.rbs +2 -0
- data/sig/generated/riffer/registrable.rbs +51 -0
- data/sig/generated/riffer/tool.rbs +6 -5
- data/sig/generated/riffer/tools/response.rbs +8 -4
- data/sig/generated/riffer/tools/runtime.rbs +5 -4
- data/sig/generated/riffer/tools/toolable.rbs +3 -1
- data/sig/generated/riffer.rbs +7 -1
- data/sig/manual/riffer/agent.rbs +7 -0
- data/sig/manual/riffer/helpers/identifier.rbs +5 -0
- data/sig/manual/riffer/tool.rbs +7 -0
- metadata +11 -11
- data/.agents/architecture.md +0 -265
- data/.agents/code-style.md +0 -110
- data/.agents/providers.md +0 -54
- data/.agents/testing.md +0 -60
- data/CLAUDE.md +0 -1
- data/lib/riffer/helpers/class_name_converter.rb +0 -22
- data/sig/generated/riffer/helpers/class_name_converter.rbs +0 -12
- data/sig/manual/riffer/helpers/class_name_converter.rbs +0 -5
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
# Registry of a class's named direct subclasses, keyed by identifier. Extend it
|
|
5
|
+
# onto a base class to look up subclasses in constant time via +find+ and +all+.
|
|
6
|
+
#
|
|
7
|
+
# class Riffer::Tool
|
|
8
|
+
# extend Riffer::Registrable
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# Riffer::Tool.find("weather_tool") # => WeatherTool
|
|
12
|
+
#
|
|
13
|
+
# @rbs module-self Class
|
|
14
|
+
module Riffer::Registrable
|
|
15
|
+
# @rbs @identifier_registry: Hash[String, Class]?
|
|
16
|
+
|
|
17
|
+
# Finds a registered subclass by identifier, or +nil+ when none matches.
|
|
18
|
+
# Only *named direct* subclasses are registered: grandchildren are not
|
|
19
|
+
# visible to a grandparent's +find+ (call +find+ on their direct parent
|
|
20
|
+
# instead), anonymous classes are never registered, and duplicate identifiers
|
|
21
|
+
# raise Riffer::DuplicateIdentifierError at first lookup.
|
|
22
|
+
#
|
|
23
|
+
#--
|
|
24
|
+
#: (String | Symbol) -> Class?
|
|
25
|
+
def find(identifier)
|
|
26
|
+
identifier_registry[identifier.to_s]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns all registered subclasses. Only *named direct* subclasses are
|
|
30
|
+
# registered: grandchildren are not included (call +all+ on their direct
|
|
31
|
+
# parent instead), anonymous classes are never registered, and duplicate
|
|
32
|
+
# identifiers raise Riffer::DuplicateIdentifierError at first lookup.
|
|
33
|
+
#
|
|
34
|
+
#--
|
|
35
|
+
#: () -> Array[Class]
|
|
36
|
+
def all
|
|
37
|
+
identifier_registry.values
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Ruby invokes +inherited+ with +self+ bound to the direct superclass — the
|
|
43
|
+
# only registry the new subclass joins — so busting self's memo is exactly
|
|
44
|
+
# sufficient.
|
|
45
|
+
#--
|
|
46
|
+
#: (Class) -> void
|
|
47
|
+
def inherited(subclass)
|
|
48
|
+
super
|
|
49
|
+
@identifier_registry = nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#--
|
|
53
|
+
#: () -> Hash[String, Class]
|
|
54
|
+
def identifier_registry
|
|
55
|
+
@identifier_registry ||= build_identifier_registry
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#--
|
|
59
|
+
#: () -> Hash[String, Class]
|
|
60
|
+
def build_identifier_registry
|
|
61
|
+
registry = {} #: Hash[String, Class]
|
|
62
|
+
subclasses.each_with_object(registry) do |subclass, acc|
|
|
63
|
+
# Anonymous classes are skipped even with an explicit identifier — the
|
|
64
|
+
# MCP factory and serializer shells synthesize short-lived anonymous
|
|
65
|
+
# classes whose registration would flake with GC timing.
|
|
66
|
+
next if Riffer::Helpers::Identifier.for(subclass).empty?
|
|
67
|
+
|
|
68
|
+
candidate = subclass #: untyped
|
|
69
|
+
key = candidate.identifier.to_s
|
|
70
|
+
next if key.strip.empty?
|
|
71
|
+
|
|
72
|
+
existing = acc[key]
|
|
73
|
+
if existing
|
|
74
|
+
raise Riffer::DuplicateIdentifierError,
|
|
75
|
+
"Duplicate identifier #{key.inspect} for #{existing} and #{subclass}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
acc[key] = subclass
|
|
79
|
+
end.freeze
|
|
80
|
+
end
|
|
81
|
+
end
|
data/lib/riffer/tool.rb
CHANGED
|
@@ -21,6 +21,7 @@ require "timeout"
|
|
|
21
21
|
#
|
|
22
22
|
class Riffer::Tool
|
|
23
23
|
extend Riffer::Tools::Toolable
|
|
24
|
+
extend Riffer::Registrable
|
|
24
25
|
|
|
25
26
|
kind :tool
|
|
26
27
|
|
|
@@ -55,19 +56,23 @@ class Riffer::Tool
|
|
|
55
56
|
Riffer::Tools::Response.error(message, type: type)
|
|
56
57
|
end
|
|
57
58
|
|
|
58
|
-
# Executes the tool with validation and timeout
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# Raises Riffer::Error if the tool does not return a Response object.
|
|
59
|
+
# Executes the tool with validation and timeout, folding every +StandardError+
|
|
60
|
+
# into an error Response. Anything outside +StandardError+ — an unimplemented
|
|
61
|
+
# +#call+ above all — still propagates, because a broken tool is a broken
|
|
62
|
+
# deploy rather than a bad request.
|
|
63
63
|
#
|
|
64
64
|
#--
|
|
65
65
|
#: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
66
66
|
def call_with_validation(context:, **kwargs)
|
|
67
67
|
params_builder = self.class.params
|
|
68
|
-
validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
begin
|
|
70
|
+
validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
|
|
71
|
+
rescue Riffer::ValidationError => e
|
|
72
|
+
return Riffer::Tools::Response.error(e.message, type: :validation_error)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
result = Timeout.timeout(self.class.timeout, Riffer::TimeoutError) do
|
|
71
76
|
call(context: context, **validated_args) #: untyped
|
|
72
77
|
end
|
|
73
78
|
|
|
@@ -76,7 +81,18 @@ class Riffer::Tool
|
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
result
|
|
79
|
-
rescue
|
|
80
|
-
|
|
84
|
+
rescue Riffer::TimeoutError
|
|
85
|
+
Riffer::Tools::Response.error(
|
|
86
|
+
"Tool execution timed out after #{self.class.timeout} seconds",
|
|
87
|
+
type: :timeout_error,
|
|
88
|
+
)
|
|
89
|
+
rescue Riffer::ToolExecutionError => e
|
|
90
|
+
Riffer::Tools::Response.error(e.message, type: :execution_error)
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
Riffer::Tools::Response.error(
|
|
93
|
+
"Error executing tool: #{e.class}: #{e.message}",
|
|
94
|
+
type: :unhandled_error,
|
|
95
|
+
exception: e,
|
|
96
|
+
)
|
|
81
97
|
end
|
|
82
98
|
end
|
|
@@ -28,6 +28,10 @@ class Riffer::Tools::Response
|
|
|
28
28
|
# The error type, or +nil+ on success.
|
|
29
29
|
attr_reader :error_type #: Symbol?
|
|
30
30
|
|
|
31
|
+
# The exception an unhandled failure was folded from, or +nil+. Kept out of
|
|
32
|
+
# every serialized form so it never reaches an LLM or a message payload.
|
|
33
|
+
attr_reader :exception #: Exception?
|
|
34
|
+
|
|
31
35
|
# Creates a success response.
|
|
32
36
|
#
|
|
33
37
|
# Raises Riffer::ArgumentError if format is invalid.
|
|
@@ -62,9 +66,9 @@ class Riffer::Tools::Response
|
|
|
62
66
|
# Creates an error response.
|
|
63
67
|
#
|
|
64
68
|
#--
|
|
65
|
-
#: (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
66
|
-
def self.error(message, type: :execution_error)
|
|
67
|
-
new(content: message, success: false, error_message: message, error_type: type)
|
|
69
|
+
#: (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
|
|
70
|
+
def self.error(message, type: :execution_error, exception: nil)
|
|
71
|
+
new(content: message, success: false, error_message: message, error_type: type, exception: exception)
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
# Returns true if the tool execution succeeded.
|
|
@@ -88,11 +92,12 @@ class Riffer::Tools::Response
|
|
|
88
92
|
private
|
|
89
93
|
|
|
90
94
|
#--
|
|
91
|
-
#: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
|
|
92
|
-
def initialize(content:, success:, error_message: nil, error_type: nil)
|
|
95
|
+
#: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
|
|
96
|
+
def initialize(content:, success:, error_message: nil, error_type: nil, exception: nil)
|
|
93
97
|
@content = content
|
|
94
98
|
@success = success
|
|
95
99
|
@error_message = error_message
|
|
96
100
|
@error_type = error_type
|
|
101
|
+
@exception = exception
|
|
97
102
|
end
|
|
98
103
|
end
|
data/lib/riffer/tools/runtime.rb
CHANGED
|
@@ -88,19 +88,20 @@ class Riffer::Tools::Runtime
|
|
|
88
88
|
tool_instance = tool_class.new
|
|
89
89
|
arguments = parse_arguments(tool_call.arguments)
|
|
90
90
|
|
|
91
|
+
unless arguments.is_a?(Hash)
|
|
92
|
+
return Riffer::Tools::Response.error(
|
|
93
|
+
"Invalid JSON in tool arguments: expected an object, got #{arguments.class}",
|
|
94
|
+
type: :validation_error,
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
91
98
|
tool_instance.call_with_validation(context: context, **arguments)
|
|
92
|
-
rescue
|
|
93
|
-
Riffer::Tools::Response.error(e.message, type: :
|
|
94
|
-
rescue Riffer::ValidationError => e
|
|
95
|
-
Riffer::Tools::Response.error(e.message, type: :validation_error)
|
|
96
|
-
rescue Riffer::ToolExecutionError => e
|
|
97
|
-
Riffer::Tools::Response.error(e.message, type: :execution_error)
|
|
98
|
-
rescue RuntimeError => e
|
|
99
|
-
Riffer::Tools::Response.error("Error executing tool: #{e.message}", type: :execution_error)
|
|
99
|
+
rescue JSON::ParserError => e
|
|
100
|
+
Riffer::Tools::Response.error("Invalid JSON in tool arguments: #{e.message}", type: :validation_error)
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
#--
|
|
103
|
-
#: (String?) ->
|
|
104
|
+
#: (String?) -> untyped
|
|
104
105
|
def parse_arguments(arguments)
|
|
105
106
|
return {} if arguments.nil? || arguments.empty?
|
|
106
107
|
|
|
@@ -144,13 +145,21 @@ class Riffer::Tools::Runtime
|
|
|
144
145
|
tags.transform_keys { |key| "riffer.tag.#{key}" }
|
|
145
146
|
end
|
|
146
147
|
|
|
147
|
-
# A
|
|
148
|
-
#
|
|
148
|
+
# A deliberate error Response is a handled outcome, so its status stays unset.
|
|
149
|
+
# An error status is reserved for a Response carrying the exception it was
|
|
150
|
+
# folded from — the tool failed for a reason nobody anticipated.
|
|
149
151
|
#--
|
|
150
152
|
#: ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tools::Response) -> void
|
|
151
153
|
def record_tool_outcome(span, result)
|
|
152
154
|
error_type = result.error_type
|
|
153
155
|
span.set_attribute("error.type", error_type.to_s) if error_type
|
|
156
|
+
|
|
157
|
+
exception = result.exception
|
|
158
|
+
if exception
|
|
159
|
+
span.record_exception(exception)
|
|
160
|
+
span.error!(exception.message)
|
|
161
|
+
end
|
|
162
|
+
|
|
154
163
|
capture_tool_result(span, result)
|
|
155
164
|
end
|
|
156
165
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# end
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
|
+
# @rbs module-self Module
|
|
18
19
|
module Riffer::Tools::Toolable
|
|
19
20
|
# @rbs self.@extenders: Array[Module]?
|
|
20
21
|
# @rbs @description: String?
|
|
@@ -57,9 +58,7 @@ module Riffer::Tools::Toolable
|
|
|
57
58
|
#--
|
|
58
59
|
#: (?String?) -> String
|
|
59
60
|
def identifier(value = nil)
|
|
60
|
-
if value.nil?
|
|
61
|
-
return @identifier || Riffer::Helpers::ClassNameConverter.convert(Module.instance_method(:name).bind_call(self))
|
|
62
|
-
end
|
|
61
|
+
return @identifier || Riffer::Helpers::Identifier.for(self) if value.nil?
|
|
63
62
|
|
|
64
63
|
@identifier = value.to_s
|
|
65
64
|
end
|
data/lib/riffer/version.rb
CHANGED
data/lib/riffer.rb
CHANGED
|
@@ -26,12 +26,17 @@ module Riffer
|
|
|
26
26
|
# Raised when tool parameter validation fails.
|
|
27
27
|
class ValidationError < Error; end
|
|
28
28
|
|
|
29
|
-
# Raised
|
|
29
|
+
# Raised inside a tool's +call+ when execution exceeds the configured
|
|
30
|
+
# timeout. Rescue it in the tool to clean up; otherwise it becomes a
|
|
31
|
+
# +:timeout_error+ response.
|
|
30
32
|
class TimeoutError < Error; end
|
|
31
33
|
|
|
32
34
|
# Raised when a tool encounters an expected execution error.
|
|
33
35
|
class ToolExecutionError < Error; end
|
|
34
36
|
|
|
37
|
+
# Raised when two registered subclasses share the same identifier.
|
|
38
|
+
class DuplicateIdentifierError < Error; end
|
|
39
|
+
|
|
35
40
|
# Returns the Riffer configuration.
|
|
36
41
|
#
|
|
37
42
|
#--
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
# agent = MyAgent.new
|
|
12
12
|
# agent.generate('Hello!')
|
|
13
13
|
class Riffer::Agent
|
|
14
|
+
extend Riffer::Registrable
|
|
15
|
+
|
|
14
16
|
self.@config: Riffer::Agent::Config?
|
|
15
17
|
|
|
16
18
|
INTERRUPT_MAX_STEPS: Symbol
|
|
@@ -104,18 +106,6 @@ class Riffer::Agent
|
|
|
104
106
|
# : () ?{ (Riffer::Skills::Config) [self: Riffer::Skills::Config] -> void } -> Riffer::Skills::Config?
|
|
105
107
|
def self.skills: () ?{ (Riffer::Skills::Config) [self: Riffer::Skills::Config] -> void } -> Riffer::Skills::Config?
|
|
106
108
|
|
|
107
|
-
# Finds an agent class by identifier.
|
|
108
|
-
#
|
|
109
|
-
# --
|
|
110
|
-
# : (String) -> singleton(Riffer::Agent)?
|
|
111
|
-
def self.find: (String) -> singleton(Riffer::Agent)?
|
|
112
|
-
|
|
113
|
-
# Returns all agent subclasses.
|
|
114
|
-
#
|
|
115
|
-
# --
|
|
116
|
-
# : () -> Array[singleton(Riffer::Agent)]
|
|
117
|
-
def self.all: () -> Array[singleton(Riffer::Agent)]
|
|
118
|
-
|
|
119
109
|
# Generates a response using a new agent instance.
|
|
120
110
|
# --
|
|
121
111
|
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Generated from lib/riffer/helpers/identifier.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Helper module for deriving snake_case identifiers from class names.
|
|
4
|
+
module Riffer::Helpers::Identifier
|
|
5
|
+
# Derives a snake_case identifier from a class name string.
|
|
6
|
+
#
|
|
7
|
+
# --
|
|
8
|
+
# : (String?) -> String
|
|
9
|
+
def derive: (String?) -> String
|
|
10
|
+
|
|
11
|
+
# Derives and memoizes the identifier for a class or module. Anonymous
|
|
12
|
+
# classes return "" without caching, so a class named later still derives its
|
|
13
|
+
# real identifier — a guard that must travel with the cache, so callers never
|
|
14
|
+
# memoize their own.
|
|
15
|
+
#
|
|
16
|
+
# --
|
|
17
|
+
# : (Module) -> String
|
|
18
|
+
def for: (Module) -> String
|
|
19
|
+
end
|
|
@@ -11,7 +11,8 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
|
|
|
11
11
|
# The error message if the tool execution failed.
|
|
12
12
|
attr_reader error: String?
|
|
13
13
|
|
|
14
|
-
# The type of error (:unknown_tool, :validation_error, :execution_error,
|
|
14
|
+
# The type of error (:unknown_tool, :validation_error, :execution_error,
|
|
15
|
+
# :timeout_error, :unhandled_error).
|
|
15
16
|
attr_reader error_type: Symbol?
|
|
16
17
|
|
|
17
18
|
# --
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Generated from lib/riffer/registrable.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Registry of a class's named direct subclasses, keyed by identifier. Extend it
|
|
4
|
+
# onto a base class to look up subclasses in constant time via +find+ and +all+.
|
|
5
|
+
#
|
|
6
|
+
# class Riffer::Tool
|
|
7
|
+
# extend Riffer::Registrable
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# Riffer::Tool.find("weather_tool") # => WeatherTool
|
|
11
|
+
#
|
|
12
|
+
# @rbs module-self Class
|
|
13
|
+
module Riffer::Registrable : Class
|
|
14
|
+
@identifier_registry: Hash[String, Class]?
|
|
15
|
+
|
|
16
|
+
# Finds a registered subclass by identifier, or +nil+ when none matches.
|
|
17
|
+
# Only *named direct* subclasses are registered: grandchildren are not
|
|
18
|
+
# visible to a grandparent's +find+ (call +find+ on their direct parent
|
|
19
|
+
# instead), anonymous classes are never registered, and duplicate identifiers
|
|
20
|
+
# raise Riffer::DuplicateIdentifierError at first lookup.
|
|
21
|
+
#
|
|
22
|
+
# --
|
|
23
|
+
# : (String | Symbol) -> Class?
|
|
24
|
+
def find: (String | Symbol) -> Class?
|
|
25
|
+
|
|
26
|
+
# Returns all registered subclasses. Only *named direct* subclasses are
|
|
27
|
+
# registered: grandchildren are not included (call +all+ on their direct
|
|
28
|
+
# parent instead), anonymous classes are never registered, and duplicate
|
|
29
|
+
# identifiers raise Riffer::DuplicateIdentifierError at first lookup.
|
|
30
|
+
#
|
|
31
|
+
# --
|
|
32
|
+
# : () -> Array[Class]
|
|
33
|
+
def all: () -> Array[Class]
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Ruby invokes +inherited+ with +self+ bound to the direct superclass — the
|
|
38
|
+
# only registry the new subclass joins — so busting self's memo is exactly
|
|
39
|
+
# sufficient.
|
|
40
|
+
# --
|
|
41
|
+
# : (Class) -> void
|
|
42
|
+
def inherited: (Class) -> void
|
|
43
|
+
|
|
44
|
+
# --
|
|
45
|
+
# : () -> Hash[String, Class]
|
|
46
|
+
def identifier_registry: () -> Hash[String, Class]
|
|
47
|
+
|
|
48
|
+
# --
|
|
49
|
+
# : () -> Hash[String, Class]
|
|
50
|
+
def build_identifier_registry: () -> Hash[String, Class]
|
|
51
|
+
end
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
class Riffer::Tool
|
|
19
19
|
extend Riffer::Tools::Toolable
|
|
20
20
|
|
|
21
|
+
extend Riffer::Registrable
|
|
22
|
+
|
|
21
23
|
# Executes the tool with the given arguments.
|
|
22
24
|
# --
|
|
23
25
|
# : (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
@@ -41,11 +43,10 @@ class Riffer::Tool
|
|
|
41
43
|
# : (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
42
44
|
def error: (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
43
45
|
|
|
44
|
-
# Executes the tool with validation and timeout
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# Raises Riffer::Error if the tool does not return a Response object.
|
|
46
|
+
# Executes the tool with validation and timeout, folding every +StandardError+
|
|
47
|
+
# into an error Response. Anything outside +StandardError+ — an unimplemented
|
|
48
|
+
# +#call+ above all — still propagates, because a broken tool is a broken
|
|
49
|
+
# deploy rather than a bad request.
|
|
49
50
|
#
|
|
50
51
|
# --
|
|
51
52
|
# : (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
@@ -24,6 +24,10 @@ class Riffer::Tools::Response
|
|
|
24
24
|
# The error type, or +nil+ on success.
|
|
25
25
|
attr_reader error_type: Symbol?
|
|
26
26
|
|
|
27
|
+
# The exception an unhandled failure was folded from, or +nil+. Kept out of
|
|
28
|
+
# every serialized form so it never reaches an LLM or a message payload.
|
|
29
|
+
attr_reader exception: Exception?
|
|
30
|
+
|
|
27
31
|
# Creates a success response.
|
|
28
32
|
#
|
|
29
33
|
# Raises Riffer::ArgumentError if format is invalid.
|
|
@@ -47,8 +51,8 @@ class Riffer::Tools::Response
|
|
|
47
51
|
# Creates an error response.
|
|
48
52
|
#
|
|
49
53
|
# --
|
|
50
|
-
# : (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
51
|
-
def self.error: (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
54
|
+
# : (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
|
|
55
|
+
def self.error: (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
|
|
52
56
|
|
|
53
57
|
# Returns true if the tool execution succeeded.
|
|
54
58
|
# --
|
|
@@ -69,6 +73,6 @@ class Riffer::Tools::Response
|
|
|
69
73
|
private
|
|
70
74
|
|
|
71
75
|
# --
|
|
72
|
-
# : (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
|
|
73
|
-
def initialize: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?) -> void
|
|
76
|
+
# : (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
|
|
77
|
+
def initialize: (content: String, success: bool, ?error_message: String?, ?error_type: Symbol?, ?exception: Exception?) -> void
|
|
74
78
|
end
|
|
@@ -44,8 +44,8 @@ class Riffer::Tools::Runtime
|
|
|
44
44
|
def dispatch_tool_call: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
|
|
45
45
|
|
|
46
46
|
# --
|
|
47
|
-
# : (String?) ->
|
|
48
|
-
def parse_arguments: (String?) ->
|
|
47
|
+
# : (String?) -> untyped
|
|
48
|
+
def parse_arguments: (String?) -> untyped
|
|
49
49
|
|
|
50
50
|
# Emitted outside +around_tool_call+ so host enrichment spans nest beneath it.
|
|
51
51
|
# --
|
|
@@ -62,8 +62,9 @@ class Riffer::Tools::Runtime
|
|
|
62
62
|
# : (Hash[String, String]) -> Hash[String, String]
|
|
63
63
|
def tag_attributes: (Hash[String, String]) -> Hash[String, String]
|
|
64
64
|
|
|
65
|
-
# A
|
|
66
|
-
#
|
|
65
|
+
# A deliberate error Response is a handled outcome, so its status stays unset.
|
|
66
|
+
# An error status is reserved for a Response carrying the exception it was
|
|
67
|
+
# folded from — the tool failed for a reason nobody anticipated.
|
|
67
68
|
# --
|
|
68
69
|
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tools::Response) -> void
|
|
69
70
|
def record_tool_outcome: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Riffer::Tools::Response) -> void
|
data/sig/generated/riffer.rbs
CHANGED
|
@@ -15,7 +15,9 @@ module Riffer
|
|
|
15
15
|
class ValidationError < Error
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
# Raised
|
|
18
|
+
# Raised inside a tool's +call+ when execution exceeds the configured
|
|
19
|
+
# timeout. Rescue it in the tool to clean up; otherwise it becomes a
|
|
20
|
+
# +:timeout_error+ response.
|
|
19
21
|
class TimeoutError < Error
|
|
20
22
|
end
|
|
21
23
|
|
|
@@ -23,6 +25,10 @@ module Riffer
|
|
|
23
25
|
class ToolExecutionError < Error
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
# Raised when two registered subclasses share the same identifier.
|
|
29
|
+
class DuplicateIdentifierError < Error
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
# Returns the Riffer configuration.
|
|
27
33
|
#
|
|
28
34
|
# --
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# `Riffer::Agent` extends `Riffer::Registrable`, whose generic signatures return
|
|
2
|
+
# `Class`. Narrow them here so callers get the agent singleton type back.
|
|
3
|
+
class Riffer::Agent
|
|
4
|
+
def self.find: (String | Symbol) -> singleton(Riffer::Agent)?
|
|
5
|
+
|
|
6
|
+
def self.all: () -> Array[singleton(Riffer::Agent)]
|
|
7
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# `Riffer::Tool` extends `Riffer::Registrable`, whose generic signatures return
|
|
2
|
+
# `Class`. Narrow them here so callers get the tool singleton type back.
|
|
3
|
+
class Riffer::Tool
|
|
4
|
+
def self.find: (String | Symbol) -> singleton(Riffer::Tool)?
|
|
5
|
+
|
|
6
|
+
def self.all: () -> Array[singleton(Riffer::Tool)]
|
|
7
|
+
end
|
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.
|
|
4
|
+
version: 0.43.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jake Bottrall
|
|
@@ -41,19 +41,15 @@ extra_rdoc_files:
|
|
|
41
41
|
- LICENSE.txt
|
|
42
42
|
- README.md
|
|
43
43
|
files:
|
|
44
|
-
- ".agents/architecture.md"
|
|
45
|
-
- ".agents/code-style.md"
|
|
46
|
-
- ".agents/providers.md"
|
|
47
|
-
- ".agents/rbs-inline.md"
|
|
48
|
-
- ".agents/testing.md"
|
|
49
44
|
- ".bundle/config"
|
|
45
|
+
- ".claude/CLAUDE.md"
|
|
46
|
+
- ".claude/rules/comments.md"
|
|
47
|
+
- ".claude/rules/rbs-inline.md"
|
|
50
48
|
- ".release-please-config.json"
|
|
51
49
|
- ".release-please-manifest.json"
|
|
52
50
|
- ".rubocop.yml"
|
|
53
51
|
- ".ruby-version"
|
|
54
|
-
- AGENTS.md
|
|
55
52
|
- CHANGELOG.md
|
|
56
|
-
- CLAUDE.md
|
|
57
53
|
- CODE_OF_CONDUCT.md
|
|
58
54
|
- Guardfile
|
|
59
55
|
- LICENSE.txt
|
|
@@ -123,8 +119,8 @@ files:
|
|
|
123
119
|
- lib/riffer/helpers.rb
|
|
124
120
|
- lib/riffer/helpers/boolean.rb
|
|
125
121
|
- lib/riffer/helpers/call_or_value.rb
|
|
126
|
-
- lib/riffer/helpers/class_name_converter.rb
|
|
127
122
|
- lib/riffer/helpers/dependencies.rb
|
|
123
|
+
- lib/riffer/helpers/identifier.rb
|
|
128
124
|
- lib/riffer/mcp.rb
|
|
129
125
|
- lib/riffer/mcp/authenticated_tool.rb
|
|
130
126
|
- lib/riffer/mcp/client.rb
|
|
@@ -157,6 +153,7 @@ files:
|
|
|
157
153
|
- lib/riffer/providers/open_router.rb
|
|
158
154
|
- lib/riffer/providers/repository.rb
|
|
159
155
|
- lib/riffer/providers/token_usage.rb
|
|
156
|
+
- lib/riffer/registrable.rb
|
|
160
157
|
- lib/riffer/runner.rb
|
|
161
158
|
- lib/riffer/runner/fibers.rb
|
|
162
159
|
- lib/riffer/runner/sequential.rb
|
|
@@ -242,8 +239,8 @@ files:
|
|
|
242
239
|
- sig/generated/riffer/helpers.rbs
|
|
243
240
|
- sig/generated/riffer/helpers/boolean.rbs
|
|
244
241
|
- sig/generated/riffer/helpers/call_or_value.rbs
|
|
245
|
-
- sig/generated/riffer/helpers/class_name_converter.rbs
|
|
246
242
|
- sig/generated/riffer/helpers/dependencies.rbs
|
|
243
|
+
- sig/generated/riffer/helpers/identifier.rbs
|
|
247
244
|
- sig/generated/riffer/mcp.rbs
|
|
248
245
|
- sig/generated/riffer/mcp/authenticated_tool.rbs
|
|
249
246
|
- sig/generated/riffer/mcp/client.rbs
|
|
@@ -276,6 +273,7 @@ files:
|
|
|
276
273
|
- sig/generated/riffer/providers/open_router.rbs
|
|
277
274
|
- sig/generated/riffer/providers/repository.rbs
|
|
278
275
|
- sig/generated/riffer/providers/token_usage.rbs
|
|
276
|
+
- sig/generated/riffer/registrable.rbs
|
|
279
277
|
- sig/generated/riffer/runner.rbs
|
|
280
278
|
- sig/generated/riffer/runner/fibers.rbs
|
|
281
279
|
- sig/generated/riffer/runner/sequential.rbs
|
|
@@ -322,20 +320,22 @@ files:
|
|
|
322
320
|
- sig/generated/riffer/version.rbs
|
|
323
321
|
- sig/manifest.yaml
|
|
324
322
|
- sig/manual/riffer.rbs
|
|
323
|
+
- sig/manual/riffer/agent.rbs
|
|
325
324
|
- sig/manual/riffer/agent/run.rbs
|
|
326
325
|
- sig/manual/riffer/agent/serializer.rbs
|
|
327
326
|
- sig/manual/riffer/agent/session/repair.rbs
|
|
328
327
|
- sig/manual/riffer/evals/evaluator_runner.rbs
|
|
329
328
|
- sig/manual/riffer/helpers/boolean.rbs
|
|
330
329
|
- sig/manual/riffer/helpers/call_or_value.rbs
|
|
331
|
-
- sig/manual/riffer/helpers/class_name_converter.rbs
|
|
332
330
|
- sig/manual/riffer/helpers/dependencies.rbs
|
|
331
|
+
- sig/manual/riffer/helpers/identifier.rbs
|
|
333
332
|
- sig/manual/riffer/mcp.rbs
|
|
334
333
|
- sig/manual/riffer/mcp/authenticated_tool.rbs
|
|
335
334
|
- sig/manual/riffer/mcp/registry.rbs
|
|
336
335
|
- sig/manual/riffer/mcp/tool_factory.rbs
|
|
337
336
|
- sig/manual/riffer/providers.rbs
|
|
338
337
|
- sig/manual/riffer/providers/repository.rbs
|
|
338
|
+
- sig/manual/riffer/tool.rbs
|
|
339
339
|
- sig/manual/riffer/tracing.rbs
|
|
340
340
|
- sig/manual/riffer/tracing/capture.rbs
|
|
341
341
|
- sig/manual/riffer/tracing/no_op.rbs
|