riffer 0.7.0 → 0.9.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/architecture.md +113 -0
  3. data/.agents/code-style.md +42 -0
  4. data/.agents/providers.md +46 -0
  5. data/.agents/rdoc.md +51 -0
  6. data/.agents/testing.md +56 -0
  7. data/.release-please-manifest.json +1 -1
  8. data/AGENTS.md +21 -308
  9. data/CHANGELOG.md +17 -0
  10. data/README.md +21 -112
  11. data/Rakefile +1 -1
  12. data/docs/01_OVERVIEW.md +106 -0
  13. data/docs/02_GETTING_STARTED.md +128 -0
  14. data/docs/03_AGENTS.md +226 -0
  15. data/docs/04_TOOLS.md +342 -0
  16. data/docs/05_MESSAGES.md +173 -0
  17. data/docs/06_STREAM_EVENTS.md +191 -0
  18. data/docs/07_CONFIGURATION.md +195 -0
  19. data/docs_providers/01_PROVIDERS.md +168 -0
  20. data/docs_providers/02_AMAZON_BEDROCK.md +196 -0
  21. data/docs_providers/03_ANTHROPIC.md +211 -0
  22. data/docs_providers/04_OPENAI.md +157 -0
  23. data/docs_providers/05_TEST_PROVIDER.md +163 -0
  24. data/docs_providers/06_CUSTOM_PROVIDERS.md +304 -0
  25. data/lib/riffer/agent.rb +103 -63
  26. data/lib/riffer/config.rb +20 -12
  27. data/lib/riffer/core.rb +7 -7
  28. data/lib/riffer/helpers/class_name_converter.rb +6 -3
  29. data/lib/riffer/helpers/dependencies.rb +18 -0
  30. data/lib/riffer/helpers/validations.rb +9 -0
  31. data/lib/riffer/messages/assistant.rb +23 -1
  32. data/lib/riffer/messages/base.rb +15 -0
  33. data/lib/riffer/messages/converter.rb +15 -5
  34. data/lib/riffer/messages/system.rb +8 -1
  35. data/lib/riffer/messages/tool.rb +45 -2
  36. data/lib/riffer/messages/user.rb +8 -1
  37. data/lib/riffer/messages.rb +7 -0
  38. data/lib/riffer/providers/amazon_bedrock.rb +8 -4
  39. data/lib/riffer/providers/anthropic.rb +209 -0
  40. data/lib/riffer/providers/base.rb +17 -12
  41. data/lib/riffer/providers/open_ai.rb +7 -1
  42. data/lib/riffer/providers/repository.rb +9 -4
  43. data/lib/riffer/providers/test.rb +25 -7
  44. data/lib/riffer/providers.rb +6 -0
  45. data/lib/riffer/stream_events/base.rb +13 -1
  46. data/lib/riffer/stream_events/reasoning_delta.rb +15 -1
  47. data/lib/riffer/stream_events/reasoning_done.rb +15 -1
  48. data/lib/riffer/stream_events/text_delta.rb +14 -1
  49. data/lib/riffer/stream_events/text_done.rb +14 -1
  50. data/lib/riffer/stream_events/tool_call_delta.rb +18 -11
  51. data/lib/riffer/stream_events/tool_call_done.rb +22 -12
  52. data/lib/riffer/stream_events.rb +9 -0
  53. data/lib/riffer/tool.rb +92 -25
  54. data/lib/riffer/tools/param.rb +19 -16
  55. data/lib/riffer/tools/params.rb +28 -22
  56. data/lib/riffer/tools/response.rb +90 -0
  57. data/lib/riffer/tools.rb +6 -0
  58. data/lib/riffer/version.rb +1 -1
  59. data/lib/riffer.rb +21 -21
  60. metadata +35 -1
@@ -1,12 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Base class for all streaming events in the Riffer framework.
4
+ #
5
+ # Subclasses must implement the +to_h+ method.
3
6
  class Riffer::StreamEvents::Base
7
+ # The message role (typically :assistant).
8
+ #
9
+ # Returns Symbol.
4
10
  attr_reader :role
5
11
 
6
- def initialize(role: "assistant")
12
+ # Creates a new stream event.
13
+ #
14
+ # role:: Symbol - the message role (defaults to :assistant)
15
+ def initialize(role: :assistant)
7
16
  @role = role
8
17
  end
9
18
 
19
+ # Converts the event to a hash.
20
+ #
21
+ # Raises NotImplementedError if not implemented by subclass.
10
22
  def to_h
11
23
  raise NotImplementedError, "Subclasses must implement #to_h"
12
24
  end
@@ -1,13 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Represents an incremental reasoning chunk during streaming.
4
+ #
5
+ # Emitted when the LLM produces reasoning/thinking content incrementally.
6
+ # Only available with providers that support reasoning (e.g., OpenAI with reasoning option).
3
7
  class Riffer::StreamEvents::ReasoningDelta < Riffer::StreamEvents::Base
8
+ # The incremental reasoning content.
9
+ #
10
+ # Returns String.
4
11
  attr_reader :content
5
12
 
6
- def initialize(content, role: "assistant")
13
+ # Creates a new reasoning delta event.
14
+ #
15
+ # content:: String - the incremental reasoning content
16
+ # role:: Symbol - the message role (defaults to :assistant)
17
+ def initialize(content, role: :assistant)
7
18
  super(role: role)
8
19
  @content = content
9
20
  end
10
21
 
22
+ # Converts the event to a hash.
23
+ #
24
+ # Returns Hash with +:role+ and +:content+ keys.
11
25
  def to_h
12
26
  {role: @role, content: @content}
13
27
  end
@@ -1,13 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Represents completion of reasoning during streaming.
4
+ #
5
+ # Emitted when the LLM has finished producing reasoning/thinking content.
6
+ # Only available with providers that support reasoning (e.g., OpenAI with reasoning option).
3
7
  class Riffer::StreamEvents::ReasoningDone < Riffer::StreamEvents::Base
8
+ # The complete reasoning content.
9
+ #
10
+ # Returns String.
4
11
  attr_reader :content
5
12
 
6
- def initialize(content, role: "assistant")
13
+ # Creates a new reasoning done event.
14
+ #
15
+ # content:: String - the complete reasoning content
16
+ # role:: Symbol - the message role (defaults to :assistant)
17
+ def initialize(content, role: :assistant)
7
18
  super(role: role)
8
19
  @content = content
9
20
  end
10
21
 
22
+ # Converts the event to a hash.
23
+ #
24
+ # Returns Hash with +:role+ and +:content+ keys.
11
25
  def to_h
12
26
  {role: @role, content: @content}
13
27
  end
@@ -1,13 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Represents an incremental text chunk during streaming.
4
+ #
5
+ # Emitted when the LLM produces text content incrementally.
3
6
  class Riffer::StreamEvents::TextDelta < Riffer::StreamEvents::Base
7
+ # The incremental text content.
8
+ #
9
+ # Returns String.
4
10
  attr_reader :content
5
11
 
6
- def initialize(content, role: "assistant")
12
+ # Creates a new text delta event.
13
+ #
14
+ # content:: String - the incremental text content
15
+ # role:: Symbol - the message role (defaults to :assistant)
16
+ def initialize(content, role: :assistant)
7
17
  super(role: role)
8
18
  @content = content
9
19
  end
10
20
 
21
+ # Converts the event to a hash.
22
+ #
23
+ # Returns Hash with +:role+ and +:content+ keys.
11
24
  def to_h
12
25
  {role: @role, content: @content}
13
26
  end
@@ -1,13 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Represents completion of text generation during streaming.
4
+ #
5
+ # Emitted when the LLM has finished producing text content.
3
6
  class Riffer::StreamEvents::TextDone < Riffer::StreamEvents::Base
7
+ # The complete text content.
8
+ #
9
+ # Returns String.
4
10
  attr_reader :content
5
11
 
6
- def initialize(content, role: "assistant")
12
+ # Creates a new text done event.
13
+ #
14
+ # content:: String - the complete text content
15
+ # role:: Symbol - the message role (defaults to :assistant)
16
+ def initialize(content, role: :assistant)
7
17
  super(role: role)
8
18
  @content = content
9
19
  end
10
20
 
21
+ # Converts the event to a hash.
22
+ #
23
+ # Returns Hash with +:role+ and +:content+ keys.
11
24
  def to_h
12
25
  {role: @role, content: @content}
13
26
  end
@@ -3,25 +3,32 @@
3
3
  # Riffer::StreamEvents::ToolCallDelta represents an incremental tool call chunk during streaming.
4
4
  #
5
5
  # Emitted when the LLM is building a tool call, containing partial argument data.
6
- #
7
- # @api public
8
6
  class Riffer::StreamEvents::ToolCallDelta < Riffer::StreamEvents::Base
9
- attr_reader :item_id, :name, :arguments_delta
7
+ # The tool call item identifier.
8
+ attr_reader :item_id
9
+
10
+ # The tool name (may only be present in first delta).
11
+ attr_reader :name
12
+
13
+ # The incremental arguments JSON fragment.
14
+ attr_reader :arguments_delta
10
15
 
11
- # Creates a new tool call delta event
12
- # @param item_id [String] the tool call item identifier
13
- # @param name [String, nil] the tool name (may only be present in first delta)
14
- # @param arguments_delta [String] the incremental arguments JSON fragment
15
- # @param role [String] the message role (defaults to "assistant")
16
- def initialize(item_id:, arguments_delta:, name: nil, role: "assistant")
16
+ # Creates a new tool call delta event.
17
+ #
18
+ # item_id:: String - the tool call item identifier
19
+ # name:: String or nil - the tool name (may only be present in first delta)
20
+ # arguments_delta:: String - the incremental arguments JSON fragment
21
+ # role:: Symbol - the message role (defaults to :assistant)
22
+ def initialize(item_id:, arguments_delta:, name: nil, role: :assistant)
17
23
  super(role: role)
18
24
  @item_id = item_id
19
25
  @name = name
20
26
  @arguments_delta = arguments_delta
21
27
  end
22
28
 
23
- # Converts the event to a hash
24
- # @return [Hash] the event data
29
+ # Converts the event to a hash.
30
+ #
31
+ # Returns Hash - the event data.
25
32
  def to_h
26
33
  {role: @role, item_id: @item_id, name: @name, arguments_delta: @arguments_delta}.compact
27
34
  end
@@ -3,18 +3,27 @@
3
3
  # Riffer::StreamEvents::ToolCallDone represents a completed tool call during streaming.
4
4
  #
5
5
  # Emitted when the LLM has finished building a tool call with complete arguments.
6
- #
7
- # @api public
8
6
  class Riffer::StreamEvents::ToolCallDone < Riffer::StreamEvents::Base
9
- attr_reader :item_id, :call_id, :name, :arguments
7
+ # The tool call item identifier.
8
+ attr_reader :item_id
9
+
10
+ # The call identifier for response matching.
11
+ attr_reader :call_id
12
+
13
+ # The tool name.
14
+ attr_reader :name
15
+
16
+ # The complete arguments JSON string.
17
+ attr_reader :arguments
10
18
 
11
- # Creates a new tool call done event
12
- # @param item_id [String] the tool call item identifier
13
- # @param call_id [String] the call identifier for response matching
14
- # @param name [String] the tool name
15
- # @param arguments [String] the complete arguments JSON string
16
- # @param role [String] the message role (defaults to "assistant")
17
- def initialize(item_id:, call_id:, name:, arguments:, role: "assistant")
19
+ # Creates a new tool call done event.
20
+ #
21
+ # item_id:: String - the tool call item identifier
22
+ # call_id:: String - the call identifier for response matching
23
+ # name:: String - the tool name
24
+ # arguments:: String - the complete arguments JSON string
25
+ # role:: Symbol - the message role (defaults to :assistant)
26
+ def initialize(item_id:, call_id:, name:, arguments:, role: :assistant)
18
27
  super(role: role)
19
28
  @item_id = item_id
20
29
  @call_id = call_id
@@ -22,8 +31,9 @@ class Riffer::StreamEvents::ToolCallDone < Riffer::StreamEvents::Base
22
31
  @arguments = arguments
23
32
  end
24
33
 
25
- # Converts the event to a hash
26
- # @return [Hash] the event data
34
+ # Converts the event to a hash.
35
+ #
36
+ # Returns Hash - the event data.
27
37
  def to_h
28
38
  {role: @role, item_id: @item_id, call_id: @call_id, name: @name, arguments: @arguments}
29
39
  end
@@ -1,4 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Namespace for streaming event types in the Riffer framework.
4
+ #
5
+ # When streaming responses, these events are yielded to represent incremental updates:
6
+ # - Riffer::StreamEvents::TextDelta - Incremental text content
7
+ # - Riffer::StreamEvents::TextDone - Complete text content
8
+ # - Riffer::StreamEvents::ToolCallDelta - Incremental tool call arguments
9
+ # - Riffer::StreamEvents::ToolCallDone - Complete tool call
10
+ # - Riffer::StreamEvents::ReasoningDelta - Incremental reasoning content
11
+ # - Riffer::StreamEvents::ReasoningDone - Complete reasoning content
3
12
  module Riffer::StreamEvents
4
13
  end
data/lib/riffer/tool.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "timeout"
4
+
3
5
  # Riffer::Tool is the base class for all tools in the Riffer framework.
4
6
  #
5
7
  # Provides a DSL for defining tool description and parameters.
8
+ # Subclasses must implement the +call+ method.
6
9
  #
7
- # @abstract Subclasses must implement the `call` method.
10
+ # See Riffer::Agent.
8
11
  #
9
- # @example
10
12
  # class WeatherLookupTool < Riffer::Tool
11
13
  # description "Provides current weather information for a specified city."
12
14
  #
@@ -20,22 +22,27 @@
20
22
  # end
21
23
  # end
22
24
  #
23
- # @see Riffer::Agent
24
25
  class Riffer::Tool
26
+ DEFAULT_TIMEOUT = 10
27
+
25
28
  class << self
26
29
  include Riffer::Helpers::ClassNameConverter
27
30
 
28
- # Gets or sets the tool description
29
- # @param value [String, nil] the description to set, or nil to get
30
- # @return [String, nil] the tool description
31
+ # Gets or sets the tool description.
32
+ #
33
+ # value:: String or nil - the description to set, or nil to get
34
+ #
35
+ # Returns String or nil - the tool description.
31
36
  def description(value = nil)
32
37
  return @description if value.nil?
33
38
  @description = value.to_s
34
39
  end
35
40
 
36
- # Gets or sets the tool identifier/name
37
- # @param value [String, nil] the identifier to set, or nil to get
38
- # @return [String] the tool identifier (defaults to snake_case class name)
41
+ # Gets or sets the tool identifier/name.
42
+ #
43
+ # value:: String or nil - the identifier to set, or nil to get
44
+ #
45
+ # Returns String - the tool identifier (defaults to snake_case class name).
39
46
  def identifier(value = nil)
40
47
  return @identifier || class_name_to_path(Module.instance_method(:name).bind_call(self)) if value.nil?
41
48
  @identifier = value.to_s
@@ -44,17 +51,30 @@ class Riffer::Tool
44
51
  # Alias for identifier - used by providers
45
52
  alias_method :name, :identifier
46
53
 
47
- # Defines parameters using the Params DSL
48
- # @yield the parameter definition block
49
- # @return [Riffer::Tools::Params, nil] the params builder
54
+ # Gets or sets the tool timeout in seconds.
55
+ #
56
+ # value:: Numeric or nil - the timeout to set in seconds, or nil to get
57
+ #
58
+ # Returns Numeric - the tool timeout (defaults to 10).
59
+ def timeout(value = nil)
60
+ return @timeout || DEFAULT_TIMEOUT if value.nil?
61
+ @timeout = value.to_f
62
+ end
63
+
64
+ # Defines parameters using the Params DSL.
65
+ #
66
+ # Yields to the parameter definition block.
67
+ #
68
+ # Returns Riffer::Tools::Params or nil - the params builder.
50
69
  def params(&block)
51
70
  return @params_builder if block.nil?
52
71
  @params_builder = Riffer::Tools::Params.new
53
72
  @params_builder.instance_eval(&block)
54
73
  end
55
74
 
56
- # Returns the JSON Schema for the tool's parameters
57
- # @return [Hash] the JSON Schema
75
+ # Returns the JSON Schema for the tool's parameters.
76
+ #
77
+ # Returns Hash - the JSON Schema.
58
78
  def parameters_schema
59
79
  @params_builder&.to_json_schema || empty_schema
60
80
  end
@@ -66,23 +86,70 @@ class Riffer::Tool
66
86
  end
67
87
  end
68
88
 
69
- # Executes the tool with the given arguments
70
- # @param context [Object, nil] optional context passed from the agent
71
- # @param kwargs [Hash] the tool arguments
72
- # @return [Object] the tool result
73
- # @raise [NotImplementedError] if not implemented by subclass
89
+ # Executes the tool with the given arguments.
90
+ #
91
+ # context:: Object or nil - optional context passed from the agent
92
+ # kwargs:: Hash - the tool arguments
93
+ #
94
+ # Returns Object - the tool result.
95
+ #
96
+ # Raises NotImplementedError if not implemented by subclass.
74
97
  def call(context:, **kwargs)
75
98
  raise NotImplementedError, "#{self.class} must implement #call"
76
99
  end
77
100
 
78
- # Executes the tool with validation (used by Agent)
79
- # @param context [Object, nil] context passed from the agent
80
- # @param kwargs [Hash] the tool arguments
81
- # @return [Object] the tool result
82
- # @raise [Riffer::ValidationError] if validation fails
101
+ # Creates a text response. Shorthand for Riffer::Tools::Response.text.
102
+ #
103
+ # result:: Object - the tool result (converted via to_s)
104
+ #
105
+ # Returns Riffer::Tools::Response.
106
+ def text(result)
107
+ Riffer::Tools::Response.text(result)
108
+ end
109
+
110
+ # Creates a JSON response. Shorthand for Riffer::Tools::Response.json.
111
+ #
112
+ # result:: Object - the tool result (converted via JSON.generate)
113
+ #
114
+ # Returns Riffer::Tools::Response.
115
+ def json(result)
116
+ Riffer::Tools::Response.json(result)
117
+ end
118
+
119
+ # Creates an error response. Shorthand for Riffer::Tools::Response.error.
120
+ #
121
+ # message:: String - the error message
122
+ # type:: Symbol - the error type (default: :execution_error)
123
+ #
124
+ # Returns Riffer::Tools::Response.
125
+ def error(message, type: :execution_error)
126
+ Riffer::Tools::Response.error(message, type: type)
127
+ end
128
+
129
+ # Executes the tool with validation and timeout (used by Agent).
130
+ #
131
+ # context:: Object or nil - context passed from the agent
132
+ # kwargs:: Hash - the tool arguments
133
+ #
134
+ # Returns Riffer::Tools::Response - the tool response.
135
+ #
136
+ # Raises Riffer::ValidationError if validation fails.
137
+ # Raises Riffer::TimeoutError if execution exceeds the configured timeout.
138
+ # Raises Riffer::Error if the tool does not return a Response object.
83
139
  def call_with_validation(context:, **kwargs)
84
140
  params_builder = self.class.params
85
141
  validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
86
- call(context: context, **validated_args)
142
+
143
+ result = Timeout.timeout(self.class.timeout) do
144
+ call(context: context, **validated_args)
145
+ end
146
+
147
+ unless result.is_a?(Riffer::Tools::Response)
148
+ raise Riffer::Error, "#{self.class} must return a Riffer::Tools::Response from #call"
149
+ end
150
+
151
+ result
152
+ rescue Timeout::Error
153
+ raise Riffer::TimeoutError, "Tool execution timed out after #{self.class.timeout} seconds"
87
154
  end
88
155
  end
@@ -3,8 +3,6 @@
3
3
  # Riffer::Tools::Param represents a single parameter definition for a tool.
4
4
  #
5
5
  # Handles type validation and JSON Schema generation for individual parameters.
6
- #
7
- # @api private
8
6
  class Riffer::Tools::Param
9
7
  # Maps Ruby types to JSON Schema type strings
10
8
  TYPE_MAPPINGS = {
@@ -19,13 +17,14 @@ class Riffer::Tools::Param
19
17
 
20
18
  attr_reader :name, :type, :required, :description, :enum, :default
21
19
 
22
- # Creates a new parameter definition
23
- # @param name [Symbol] the parameter name
24
- # @param type [Class] the expected Ruby type
25
- # @param required [Boolean] whether the parameter is required
26
- # @param description [String, nil] optional description for the parameter
27
- # @param enum [Array, nil] optional list of allowed values
28
- # @param default [Object, nil] optional default value for optional parameters
20
+ # Creates a new parameter definition.
21
+ #
22
+ # name:: Symbol - the parameter name
23
+ # type:: Class - the expected Ruby type
24
+ # required:: Boolean - whether the parameter is required
25
+ # description:: String or nil - optional description for the parameter
26
+ # enum:: Array or nil - optional list of allowed values
27
+ # default:: Object or nil - optional default value for optional parameters
29
28
  def initialize(name:, type:, required:, description: nil, enum: nil, default: nil)
30
29
  @name = name.to_sym
31
30
  @type = type
@@ -35,9 +34,11 @@ class Riffer::Tools::Param
35
34
  @default = default
36
35
  end
37
36
 
38
- # Validates that a value matches the expected type
39
- # @param value [Object] the value to validate
40
- # @return [Boolean] true if valid, false otherwise
37
+ # Validates that a value matches the expected type.
38
+ #
39
+ # value:: Object - the value to validate
40
+ #
41
+ # Returns Boolean - true if valid, false otherwise.
41
42
  def valid_type?(value)
42
43
  return true if value.nil? && !required
43
44
 
@@ -48,14 +49,16 @@ class Riffer::Tools::Param
48
49
  end
49
50
  end
50
51
 
51
- # Returns the JSON Schema type name for this parameter
52
- # @return [String] the JSON Schema type
52
+ # Returns the JSON Schema type name for this parameter.
53
+ #
54
+ # Returns String - the JSON Schema type.
53
55
  def type_name
54
56
  TYPE_MAPPINGS[type] || type.to_s.downcase
55
57
  end
56
58
 
57
- # Converts this parameter to JSON Schema format
58
- # @return [Hash] the JSON Schema representation
59
+ # Converts this parameter to JSON Schema format.
60
+ #
61
+ # Returns Hash - the JSON Schema representation.
59
62
  def to_json_schema
60
63
  schema = {type: type_name}
61
64
  schema[:description] = description if description
@@ -2,15 +2,13 @@
2
2
 
3
3
  # Riffer::Tools::Params provides a DSL for defining tool parameters.
4
4
  #
5
- # Used within a Tool's `params` block to define required and optional parameters.
5
+ # Used within a Tool's +params+ block to define required and optional parameters.
6
6
  #
7
- # @example
8
7
  # params do
9
8
  # required :city, String, description: "The city name"
10
9
  # optional :units, String, default: "celsius", enum: ["celsius", "fahrenheit"]
11
10
  # end
12
11
  #
13
- # @api private
14
12
  class Riffer::Tools::Params
15
13
  attr_reader :parameters
16
14
 
@@ -18,12 +16,14 @@ class Riffer::Tools::Params
18
16
  @parameters = []
19
17
  end
20
18
 
21
- # Defines a required parameter
22
- # @param name [Symbol] the parameter name
23
- # @param type [Class] the expected Ruby type
24
- # @param description [String, nil] optional description
25
- # @param enum [Array, nil] optional list of allowed values
26
- # @return [void]
19
+ # Defines a required parameter.
20
+ #
21
+ # name:: Symbol - the parameter name
22
+ # type:: Class - the expected Ruby type
23
+ # description:: String or nil - optional description
24
+ # enum:: Array or nil - optional list of allowed values
25
+ #
26
+ # Returns void.
27
27
  def required(name, type, description: nil, enum: nil)
28
28
  @parameters << Riffer::Tools::Param.new(
29
29
  name: name,
@@ -34,13 +34,15 @@ class Riffer::Tools::Params
34
34
  )
35
35
  end
36
36
 
37
- # Defines an optional parameter
38
- # @param name [Symbol] the parameter name
39
- # @param type [Class] the expected Ruby type
40
- # @param description [String, nil] optional description
41
- # @param enum [Array, nil] optional list of allowed values
42
- # @param default [Object, nil] default value when not provided
43
- # @return [void]
37
+ # Defines an optional parameter.
38
+ #
39
+ # name:: Symbol - the parameter name
40
+ # type:: Class - the expected Ruby type
41
+ # description:: String or nil - optional description
42
+ # enum:: Array or nil - optional list of allowed values
43
+ # default:: Object or nil - default value when not provided
44
+ #
45
+ # Returns void.
44
46
  def optional(name, type, description: nil, enum: nil, default: nil)
45
47
  @parameters << Riffer::Tools::Param.new(
46
48
  name: name,
@@ -52,10 +54,13 @@ class Riffer::Tools::Params
52
54
  )
53
55
  end
54
56
 
55
- # Validates arguments against parameter definitions
56
- # @param arguments [Hash] the arguments to validate
57
- # @return [Hash] validated arguments with defaults applied
58
- # @raise [Riffer::ValidationError] if validation fails
57
+ # Validates arguments against parameter definitions.
58
+ #
59
+ # arguments:: Hash - the arguments to validate
60
+ #
61
+ # Returns Hash - validated arguments with defaults applied.
62
+ #
63
+ # Raises Riffer::ValidationError if validation fails.
59
64
  def validate(arguments)
60
65
  validated = {}
61
66
  errors = []
@@ -91,8 +96,9 @@ class Riffer::Tools::Params
91
96
  validated
92
97
  end
93
98
 
94
- # Converts all parameters to JSON Schema format
95
- # @return [Hash] the JSON Schema representation
99
+ # Converts all parameters to JSON Schema format.
100
+ #
101
+ # Returns Hash - the JSON Schema representation.
96
102
  def to_json_schema
97
103
  properties = {}
98
104
  required_params = []
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ # Riffer::Tools::Response represents the result of a tool execution.
6
+ #
7
+ # All tools must return a Response object from their +call+ method.
8
+ # Use +Response.success+ for successful results and +Response.error+ for failures.
9
+ #
10
+ # class MyTool < Riffer::Tool
11
+ # def call(context:, **kwargs)
12
+ # result = perform_operation
13
+ # Riffer::Tools::Response.success(result)
14
+ # rescue MyError => e
15
+ # Riffer::Tools::Response.error(e.message)
16
+ # end
17
+ # end
18
+ #
19
+ class Riffer::Tools::Response
20
+ VALID_FORMATS = %i[text json].freeze
21
+
22
+ attr_reader :content, :error_message, :error_type
23
+
24
+ # Creates a success response.
25
+ #
26
+ # result:: Object - the tool result
27
+ # format:: Symbol - the format (:text or :json; default: :text)
28
+ #
29
+ # Returns Riffer::Tools::Response.
30
+ #
31
+ # Raises Riffer::ArgumentError if format is invalid.
32
+ def self.success(result, format: :text)
33
+ unless VALID_FORMATS.include?(format)
34
+ raise Riffer::ArgumentError, "Invalid format: #{format}. Must be one of: #{VALID_FORMATS.join(", ")}"
35
+ end
36
+
37
+ content = (format == :json) ? result.to_json : result.to_s
38
+ new(content: content, success: true)
39
+ end
40
+
41
+ # Creates a success response with text format.
42
+ #
43
+ # result:: Object - the tool result (converted via to_s)
44
+ #
45
+ # Returns Riffer::Tools::Response.
46
+ def self.text(result)
47
+ success(result, format: :text)
48
+ end
49
+
50
+ # Creates a success response with JSON format.
51
+ #
52
+ # result:: Object - the tool result (converted via to_json)
53
+ #
54
+ # Returns Riffer::Tools::Response.
55
+ def self.json(result)
56
+ success(result, format: :json)
57
+ end
58
+
59
+ # Creates an error response.
60
+ #
61
+ # message:: String - the error message
62
+ # type:: Symbol - the error type (default: :execution_error)
63
+ #
64
+ # Returns Riffer::Tools::Response.
65
+ def self.error(message, type: :execution_error)
66
+ new(content: message, success: false, error_message: message, error_type: type)
67
+ end
68
+
69
+ # Returns true if the response is successful.
70
+ def success? = @success
71
+
72
+ # Returns true if the response is an error.
73
+ def error? = !@success
74
+
75
+ # Returns a hash representation of the response.
76
+ #
77
+ # Returns Hash with :content, :error, and :error_type keys.
78
+ def to_h
79
+ {content: @content, error: @error_message, error_type: @error_type}
80
+ end
81
+
82
+ private
83
+
84
+ def initialize(content:, success:, error_message: nil, error_type: nil)
85
+ @content = content
86
+ @success = success
87
+ @error_message = error_message
88
+ @error_type = error_type
89
+ end
90
+ end