riffer 0.47.2 → 0.48.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +23 -0
  4. data/docs/AGENTS.md +37 -3
  5. data/docs/AGENT_LIFECYCLE.md +1 -0
  6. data/docs/CONFIGURATION.md +1 -1
  7. data/docs/EVALS.md +2 -1
  8. data/docs/MCP.md +0 -4
  9. data/docs/MESSAGES.md +85 -17
  10. data/docs/STREAM_EVENTS.md +8 -4
  11. data/docs/TRACING.md +1 -1
  12. data/docs/providers/AMAZON_BEDROCK.md +1 -1
  13. data/docs/providers/CUSTOM_PROVIDERS.md +56 -4
  14. data/docs/providers/GEMINI.md +1 -1
  15. data/docs/providers/MOCK_PROVIDER.md +17 -0
  16. data/lib/riffer/agent/config.rb +30 -6
  17. data/lib/riffer/agent/response.rb +7 -0
  18. data/lib/riffer/agent/run.rb +24 -8
  19. data/lib/riffer/agent/session.rb +1 -0
  20. data/lib/riffer/agent.rb +29 -5
  21. data/lib/riffer/config.rb +1 -1
  22. data/lib/riffer/evals/evaluator.rb +29 -2
  23. data/lib/riffer/evals/judge.rb +11 -5
  24. data/lib/riffer/files/resolver.rb +3 -3
  25. data/lib/riffer/helpers/deep_dup.rb +42 -0
  26. data/lib/riffer/messages/assistant/reasoning_part.rb +90 -0
  27. data/lib/riffer/messages/assistant/tool_call.rb +63 -0
  28. data/lib/riffer/messages/assistant.rb +50 -2
  29. data/lib/riffer/messages/base.rb +5 -21
  30. data/lib/riffer/messages/system.rb +10 -0
  31. data/lib/riffer/messages/tool.rb +17 -0
  32. data/lib/riffer/messages/{file_part.rb → user/file_part.rb} +15 -11
  33. data/lib/riffer/messages/user.rb +13 -2
  34. data/lib/riffer/params/param.rb +12 -0
  35. data/lib/riffer/params.rb +9 -0
  36. data/lib/riffer/providers/amazon_bedrock.rb +5 -4
  37. data/lib/riffer/providers/anthropic.rb +2 -2
  38. data/lib/riffer/providers/base.rb +27 -8
  39. data/lib/riffer/providers/gemini.rb +2 -2
  40. data/lib/riffer/providers/mock.rb +18 -2
  41. data/lib/riffer/providers/open_ai.rb +6 -5
  42. data/lib/riffer/providers/open_router.rb +6 -5
  43. data/lib/riffer/providers/token_usage.rb +16 -0
  44. data/lib/riffer/skills/config.rb +12 -0
  45. data/lib/riffer/stream_events/reasoning_done.rb +10 -8
  46. data/lib/riffer/tools/runtime.rb +3 -4
  47. data/lib/riffer/tracing/capture.rb +2 -4
  48. data/lib/riffer/version.rb +1 -1
  49. data/sig/generated/riffer/agent/config.rbs +19 -5
  50. data/sig/generated/riffer/agent/response.rbs +6 -1
  51. data/sig/generated/riffer/agent/run.rbs +15 -8
  52. data/sig/generated/riffer/agent.rbs +25 -9
  53. data/sig/generated/riffer/config.rbs +1 -1
  54. data/sig/generated/riffer/evals/evaluator.rbs +20 -1
  55. data/sig/generated/riffer/evals/judge.rbs +5 -2
  56. data/sig/generated/riffer/files/resolver.rbs +6 -6
  57. data/sig/generated/riffer/helpers/deep_dup.rbs +21 -0
  58. data/sig/generated/riffer/messages/assistant/reasoning_part.rbs +58 -0
  59. data/sig/generated/riffer/messages/assistant/tool_call.rbs +42 -0
  60. data/sig/generated/riffer/messages/assistant.rbs +24 -10
  61. data/sig/generated/riffer/messages/system.rbs +6 -0
  62. data/sig/generated/riffer/messages/tool.rbs +6 -0
  63. data/sig/generated/riffer/messages/{file_part.rbs → user/file_part.rbs} +11 -6
  64. data/sig/generated/riffer/messages/user.rbs +9 -3
  65. data/sig/generated/riffer/params/param.rbs +7 -0
  66. data/sig/generated/riffer/params.rbs +6 -0
  67. data/sig/generated/riffer/providers/amazon_bedrock.rbs +4 -4
  68. data/sig/generated/riffer/providers/anthropic.rbs +2 -2
  69. data/sig/generated/riffer/providers/base.rbs +22 -10
  70. data/sig/generated/riffer/providers/gemini.rbs +4 -4
  71. data/sig/generated/riffer/providers/mock.rbs +7 -2
  72. data/sig/generated/riffer/providers/open_ai.rbs +4 -4
  73. data/sig/generated/riffer/providers/open_router.rbs +4 -4
  74. data/sig/generated/riffer/providers/token_usage.rbs +6 -0
  75. data/sig/generated/riffer/skills/config.rbs +9 -0
  76. data/sig/generated/riffer/stream_events/reasoning_done.rbs +8 -6
  77. data/sig/generated/riffer/tools/runtime.rbs +2 -2
  78. data/sig/generated/riffer/tracing/capture.rbs +4 -4
  79. data/sig/manual/riffer/helpers/deep_dup.rbs +5 -0
  80. metadata +10 -3
@@ -6,6 +6,8 @@
6
6
  class Riffer::Agent::Config
7
7
  DEFAULT_MAX_STEPS: Integer
8
8
 
9
+ @tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?
10
+
9
11
  # The configured agent identifier.
10
12
  attr_reader identifier: String?
11
13
 
@@ -30,9 +32,6 @@ class Riffer::Agent::Config
30
32
  # The accumulated +use_mcp+ tag configurations.
31
33
  attr_reader mcp_configs: Array[Hash[Symbol, untyped]]
32
34
 
33
- # The configured tool runtime.
34
- attr_reader tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc
35
-
36
35
  # The configured skills.
37
36
  attr_accessor skills_config: Riffer::Skills::Config?
38
37
 
@@ -51,11 +50,11 @@ class Riffer::Agent::Config
51
50
  # ?max_steps: Numeric?,
52
51
  # ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?,
53
52
  # ?mcp_configs: Array[Hash[Symbol, untyped]],
54
- # ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc),
53
+ # ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?,
55
54
  # ?skills_config: Riffer::Skills::Config?,
56
55
  # ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]
57
56
  # ) -> void
58
- def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
57
+ def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
59
58
 
60
59
  # Sets +identifier+, coercing the value to String.
61
60
  # --
@@ -67,6 +66,13 @@ class Riffer::Agent::Config
67
66
  # : (Riffer::Params?) -> Riffer::Params?
68
67
  def structured_output=: (Riffer::Params?) -> Riffer::Params?
69
68
 
69
+ # Returns the declared tool runtime, or +Riffer.config.tool_runtime+ when none
70
+ # was declared. Resolving the global here rather than at construction is what
71
+ # lets a copy tell an inherited runtime from a defaulted one.
72
+ # --
73
+ # : () -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
74
+ def tool_runtime: () -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
75
+
70
76
  # Sets +tool_runtime+. Raises Riffer::ArgumentError on an invalid value.
71
77
  # --
72
78
  # : ((singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
@@ -105,6 +111,14 @@ class Riffer::Agent::Config
105
111
 
106
112
  private
107
113
 
114
+ # +dup+ would leave the copy sharing every collection with this one, so a
115
+ # declaration on either would reach the other. The nesting runs deeper than one
116
+ # level: an mcp entry holds its own +:tags+ array, a guardrail entry its own
117
+ # +:options+ hash, and +model_options+ is arbitrary.
118
+ # --
119
+ # : (Riffer::Agent::Config) -> void
120
+ def initialize_copy: (Riffer::Agent::Config) -> void
121
+
108
122
  # --
109
123
  # : (untyped, String) -> void
110
124
  def validate_string_or_proc!: (untyped, String) -> void
@@ -23,6 +23,10 @@ class Riffer::Agent::Response
23
23
  # The modifications made by guardrails during processing.
24
24
  attr_reader modifications: Array[Riffer::Guardrails::Modification]
25
25
 
26
+ # The reasoning parts on the final assistant message, if the provider
27
+ # produced any.
28
+ attr_reader reasoning: Array[Riffer::Messages::Assistant::ReasoningPart]
29
+
26
30
  # The parsed structured output, if structured output was configured.
27
31
  attr_reader structured_output: Hash[Symbol, untyped]?
28
32
 
@@ -46,13 +50,14 @@ class Riffer::Agent::Response
46
50
  # outcome: Riffer::Agent::Outcome,
47
51
  # ?tripwire: Riffer::Guardrails::Tripwire?,
48
52
  # ?modifications: Array[Riffer::Guardrails::Modification],
53
+ # ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart],
49
54
  # ?structured_output: Hash[Symbol, untyped]?,
50
55
  # ?messages: Array[Riffer::Messages::Base],
51
56
  # ?healed_tool_call_ids: Array[String],
52
57
  # ?token_usage: Riffer::Providers::TokenUsage?,
53
58
  # ?steps: Integer
54
59
  # ) -> void
55
- def initialize: (String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?structured_output: Hash[Symbol, untyped]?, ?messages: Array[Riffer::Messages::Base], ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> void
60
+ def initialize: (String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart], ?structured_output: Hash[Symbol, untyped]?, ?messages: Array[Riffer::Messages::Base], ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> void
56
61
 
57
62
  # Returns true if any guardrail modified data during processing.
58
63
  #
@@ -7,20 +7,21 @@ module Riffer::Agent::Run
7
7
  # for prompt/files semantics.
8
8
  #
9
9
  # --
10
- # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
11
- def generate: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
10
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
11
+ def generate: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
12
12
 
13
13
  # Runs the streaming loop for the given agent. See Riffer::Agent#stream
14
14
  # for prompt/files semantics.
15
15
  #
16
16
  # --
17
- # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
18
- def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
17
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
18
+ def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
19
19
 
20
20
  private
21
21
 
22
22
  # Both +generate+ and +stream+ funnel here, so this is the single place raw
23
- # +tags+ are normalized. The clean <tt>String => String</tt> map is then
23
+ # +tags+ are normalized and merged over the default tags (a caller tag wins
24
+ # on a shared key). The clean <tt>String => String</tt> map is then
24
25
  # threaded to every span builder in the run as +riffer.tag.*+ and to each
25
26
  # provider call (via +merged_model_options+) for native request-metadata
26
27
  # mapping.
@@ -100,6 +101,11 @@ module Riffer::Agent::Run
100
101
  # : (Riffer::Agent, ?Hash[String, String]) -> Hash[Symbol, untyped]
101
102
  def merged_model_options: (Riffer::Agent, ?Hash[String, String]) -> Hash[Symbol, untyped]
102
103
 
104
+ # The tags riffer adds to every run, identifying the agent it's on behalf of.
105
+ # --
106
+ # : (Riffer::Agent) -> Hash[String, String]
107
+ def default_tags: (Riffer::Agent) -> Hash[String, String]
108
+
103
109
  # --
104
110
  # : (
105
111
  # Riffer::Agent,
@@ -107,18 +113,19 @@ module Riffer::Agent::Run
107
113
  # outcome: Riffer::Agent::Outcome,
108
114
  # ?tripwire: Riffer::Guardrails::Tripwire?,
109
115
  # ?modifications: Array[Riffer::Guardrails::Modification],
116
+ # ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart],
110
117
  # ?structured_output: Hash[Symbol, untyped]?,
111
118
  # ?healed_tool_call_ids: Array[String],
112
119
  # ?token_usage: Riffer::Providers::TokenUsage?,
113
120
  # ?steps: Integer
114
121
  # ) -> Riffer::Agent::Response
115
- def build_response: (Riffer::Agent, String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> Riffer::Agent::Response
122
+ def build_response: (Riffer::Agent, String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart], ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> Riffer::Agent::Response
116
123
 
117
124
  # Raises when +files+ are supplied without a +prompt+ — the provider needs
118
125
  # text to anchor the attachments.
119
126
  # --
120
- # : (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
121
- def append_user_message: (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
127
+ # : (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?) -> void
128
+ def append_user_message: (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?) -> void
122
129
 
123
130
  # --
124
131
  # : (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
@@ -22,6 +22,13 @@ class Riffer::Agent
22
22
  # : () -> Riffer::Agent::Config
23
23
  def self.config: () -> Riffer::Agent::Config
24
24
 
25
+ # +identifier+ is cleared because it is configuration here but identity on the
26
+ # subclass; two classes claiming one raise Riffer::DuplicateIdentifierError at
27
+ # the next registry lookup.
28
+ # --
29
+ # : (Class) -> void
30
+ def self.inherited: (Class) -> void
31
+
25
32
  # Gets or sets the agent identifier.
26
33
  #
27
34
  # --
@@ -108,13 +115,13 @@ class Riffer::Agent
108
115
 
109
116
  # Generates a response using a new agent instance.
110
117
  # --
111
- # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
112
- def self.generate: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
118
+ # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
119
+ def self.generate: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
113
120
 
114
121
  # Streams a response using a new agent instance.
115
122
  # --
116
- # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
117
- def self.stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
123
+ # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
124
+ def self.stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
118
125
 
119
126
  # Reconstructs a runnable agent from a wire hash produced by +#to_h+.
120
127
  # --
@@ -190,6 +197,14 @@ class Riffer::Agent
190
197
  # : (?session: Riffer::Agent::Session?, ?context: Hash[Symbol, untyped]?, ?config: Riffer::Agent::Config?) -> void
191
198
  def initialize: (?session: Riffer::Agent::Session?, ?context: Hash[Symbol, untyped]?, ?config: Riffer::Agent::Config?) -> void
192
199
 
200
+ # The identifier for this agent: the instance config's identifier when set
201
+ # (e.g. an agent restored with +Riffer::Agent.from_h+), otherwise the class
202
+ # identifier.
203
+ #
204
+ # --
205
+ # : () -> String
206
+ def identifier: () -> String
207
+
193
208
  # Generates a response from the agent.
194
209
  #
195
210
  # With +prompt+, a new user message is appended (silently — +on_message+ does
@@ -202,11 +217,12 @@ class Riffer::Agent
202
217
  # are stamped as +riffer.tag.*+ on every span the call emits. See
203
218
  # +docs/AGENTS.md+ for the per-provider mapping. The reserved key
204
219
  # +user_id+ also maps to the provider's native user identifier where one
205
- # exists.
220
+ # exists. Riffer adds the default tags +kind+ (+"agent"+) and +agent+ (the
221
+ # agent identifier); a caller tag with the same key wins.
206
222
  #
207
223
  # --
208
- # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
209
- def generate: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
224
+ # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
225
+ def generate: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
210
226
 
211
227
  # Streams a response from the agent as an +Enumerator+ of
212
228
  # +Riffer::StreamEvents+ whose block-form +each+ returns the final
@@ -215,8 +231,8 @@ class Riffer::Agent
215
231
  # Raises Riffer::ArgumentError if structured output is configured.
216
232
  #
217
233
  # --
218
- # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
219
- def stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
234
+ # : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
235
+ def stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::User::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
220
236
 
221
237
  # Interrupts the agent loop from an +on_message+ callback. Equivalent to
222
238
  # <tt>throw :riffer_interrupt, reason</tt>.
@@ -147,7 +147,7 @@ class Riffer::Config
147
147
  def backend=: (untyped) -> void
148
148
  end
149
149
 
150
- # File-attachment-download policy for +Riffer::Messages::FilePart+ URL sources
150
+ # File-attachment-download policy for +Riffer::Messages::User::FilePart+ URL sources
151
151
  class Files
152
152
  # Allow file attachments to be downloaded to send to providers.
153
153
  attr_reader allow_downloads: bool
@@ -16,8 +16,13 @@ class Riffer::Evals::Evaluator
16
16
 
17
17
  self.@judge_model: String?
18
18
 
19
+ self.@identifier: String?
20
+
19
21
  @judge: Riffer::Evals::Judge?
20
22
 
23
+ # The identifier for an anonymous evaluator class.
24
+ DEFAULT_IDENTIFIER: String
25
+
21
26
  # Gets or sets the evaluation instructions (criteria and scoring rubric).
22
27
  #
23
28
  # --
@@ -36,6 +41,19 @@ class Riffer::Evals::Evaluator
36
41
  # : (?String?) -> String?
37
42
  def self.judge_model: (?String?) -> String?
38
43
 
44
+ # Gets or sets the evaluator identifier, sent as the +agent+ tag on judge
45
+ # calls. Defaults to the snake_cased class name, or DEFAULT_IDENTIFIER for
46
+ # an anonymous class.
47
+ #
48
+ # --
49
+ # : (?String?) -> String
50
+ def self.identifier: (?String?) -> String
51
+
52
+ # Anonymous classes derive an empty identifier.
53
+ # --
54
+ # : () -> String
55
+ private def self.derived_identifier: () -> String
56
+
39
57
  # Evaluates an input/output pair. The default calls the judge with the
40
58
  # class-level +instructions+; override for custom logic (e.g. rule-based
41
59
  # evaluators).
@@ -49,7 +67,8 @@ class Riffer::Evals::Evaluator
49
67
  # : (String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base]) -> String
50
68
  def format_input: (String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base]) -> String
51
69
 
52
- # Returns a Judge instance configured for this evaluator.
70
+ # Returns a Judge instance configured for this evaluator. Its calls carry
71
+ # the +kind+ (+"judge"+) and +agent+ (the evaluator identifier) tags.
53
72
  #
54
73
  # --
55
74
  # : () -> Riffer::Evals::Judge
@@ -19,10 +19,13 @@ class Riffer::Evals::Judge
19
19
  # The model string (provider/model format).
20
20
  attr_reader model: String
21
21
 
22
+ # The tags passed to the provider on every call.
23
+ attr_reader tags: Hash[String, String]
24
+
22
25
  # Raises Riffer::ArgumentError unless +model+ is "provider/model" format.
23
26
  # --
24
- # : (model: String) -> void
25
- def initialize: (model: String) -> void
27
+ # : (model: String, ?tags: Hash[String, String]) -> void
28
+ def initialize: (model: String, ?tags: Hash[String, String]) -> void
26
29
 
27
30
  # Evaluates an input/output pair using the configured LLM.
28
31
  # --
@@ -20,17 +20,17 @@ class Riffer::Files::Resolver
20
20
  # : (Riffer::Messages::User) -> void
21
21
  def check_file_count!: (Riffer::Messages::User) -> void
22
22
 
23
- # : (Riffer::Messages::FilePart) -> void
24
- def resolve_file!: (Riffer::Messages::FilePart) -> void
23
+ # : (Riffer::Messages::User::FilePart) -> void
24
+ def resolve_file!: (Riffer::Messages::User::FilePart) -> void
25
25
 
26
- # : (Riffer::Messages::FilePart) -> void
27
- def verify_inline!: (Riffer::Messages::FilePart) -> void
26
+ # : (Riffer::Messages::User::FilePart) -> void
27
+ def verify_inline!: (Riffer::Messages::User::FilePart) -> void
28
28
 
29
29
  # +cache:+ is false for a :url-delivery provider verifying a sha256 — the
30
30
  # request still sends the URL, never the downloaded bytes, so caching them
31
31
  # would hold memory nothing reads and let later turns skip re-verifying.
32
- # : (Riffer::Messages::FilePart, cache: (false | Symbol)) -> void
33
- def download!: (Riffer::Messages::FilePart, cache: false | Symbol) -> void
32
+ # : (Riffer::Messages::User::FilePart, cache: (false | Symbol)) -> void
33
+ def download!: (Riffer::Messages::User::FilePart, cache: false | Symbol) -> void
34
34
 
35
35
  def verify_bytes!: (untyped bytes, untyped sha256) -> untyped
36
36
  end
@@ -0,0 +1,21 @@
1
+ # Generated from lib/riffer/helpers/deep_dup.rb with RBS::Inline
2
+
3
+ # Copying for the nested Hashes and Arrays a configuration object holds.
4
+ module Riffer::Helpers::DeepDup
5
+ # Returns +value+ with every Hash and Array rebuilt, so a copy shares no
6
+ # collection with its source.
7
+ #
8
+ # Anything else is returned as-is, which a Class or a Proc needs: +Class#dup+
9
+ # answers a new anonymous class. One source collection maps to one copy, so
10
+ # references shared within +value+ stay shared in the result — an +:around+
11
+ # guardrail registered under two phases is still one registration afterwards.
12
+ # --
13
+ # : (untyped) -> untyped
14
+ def call: (untyped) -> untyped
15
+
16
+ private
17
+
18
+ # --
19
+ # : (untyped, Hash[untyped, untyped]) -> untyped
20
+ def rebuild: (untyped, Hash[untyped, untyped]) -> untyped
21
+ end
@@ -0,0 +1,58 @@
1
+ # Generated from lib/riffer/messages/assistant/reasoning_part.rb with RBS::Inline
2
+
3
+ # Represents one block of model reasoning attached to an assistant message.
4
+ # riffer stores and replays a part verbatim rather than interpreting it, so a
5
+ # provider that requires its own thinking blocks back gets them unchanged.
6
+ class Riffer::Messages::Assistant::ReasoningPart
7
+ TYPES: Array[Symbol]
8
+
9
+ # What the part carries: readable reasoning (+:text+), a provider-condensed
10
+ # digest (+:summary+), or an opaque payload (+:encrypted+).
11
+ attr_reader type: Symbol
12
+
13
+ # The reasoning prose, for +:text+ and +:summary+ parts.
14
+ attr_reader text: String?
15
+
16
+ # The opaque payload, for +:encrypted+ parts.
17
+ attr_reader data: String?
18
+
19
+ # The provider's signature over the part, when it issues one.
20
+ attr_reader signature: String?
21
+
22
+ # The provider's identifier for the part, when it issues one.
23
+ attr_reader id: String?
24
+
25
+ # The wire format of the part, owned by the provider adapter that produced it
26
+ # (e.g. <tt>"anthropic-claude-v1"</tt>). Adapters replay only the formats they
27
+ # recognize, so it is never validated here.
28
+ attr_reader format: String?
29
+
30
+ # Builds a ReasoningPart from a hash, or returns +part+ unchanged when it is
31
+ # already a ReasoningPart.
32
+ # --
33
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::Assistant::ReasoningPart)) -> Riffer::Messages::Assistant::ReasoningPart
34
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::Assistant::ReasoningPart) -> Riffer::Messages::Assistant::ReasoningPart
35
+
36
+ # Raises Riffer::ArgumentError on a +type+ outside TYPES.
37
+ # --
38
+ # : (type: Symbol, ?text: String?, ?data: String?, ?signature: String?, ?id: String?, ?format: String?) -> void
39
+ def initialize: (type: Symbol, ?text: String?, ?data: String?, ?signature: String?, ?id: String?, ?format: String?) -> void
40
+
41
+ # Serializes the part to a hash, omitting the fields it doesn't carry.
42
+ #
43
+ # --
44
+ # : () -> Hash[Symbol, untyped]
45
+ def to_h: () -> Hash[Symbol, untyped]
46
+
47
+ # --
48
+ # : (untyped) -> bool
49
+ def ==: (untyped) -> bool
50
+
51
+ # --
52
+ # : (untyped) -> bool
53
+ def eql?: (untyped) -> bool
54
+
55
+ # --
56
+ # : () -> Integer
57
+ def hash: () -> Integer
58
+ end
@@ -0,0 +1,42 @@
1
+ # Generated from lib/riffer/messages/assistant/tool_call.rb with RBS::Inline
2
+
3
+ # Represents one tool invocation the model requested on an assistant message.
4
+ class Riffer::Messages::Assistant::ToolCall
5
+ # The provider's identifier for the call, echoed back on the tool result.
6
+ attr_reader call_id: String
7
+
8
+ # The name of the tool to invoke.
9
+ attr_reader name: String
10
+
11
+ # The JSON-encoded arguments, exactly as the provider emitted them.
12
+ attr_reader arguments: String
13
+
14
+ # Builds a ToolCall from a hash, or returns +call+ unchanged when it is
15
+ # already a ToolCall. Raises Riffer::ArgumentError when the hash is missing
16
+ # a field.
17
+ # --
18
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::Assistant::ToolCall)) -> Riffer::Messages::Assistant::ToolCall
19
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::Assistant::ToolCall) -> Riffer::Messages::Assistant::ToolCall
20
+
21
+ # --
22
+ # : (call_id: String, name: String, arguments: String) -> void
23
+ def initialize: (call_id: String, name: String, arguments: String) -> void
24
+
25
+ # Serializes the call to a hash.
26
+ #
27
+ # --
28
+ # : () -> Hash[Symbol, untyped]
29
+ def to_h: () -> Hash[Symbol, untyped]
30
+
31
+ # --
32
+ # : (untyped) -> bool
33
+ def ==: (untyped) -> bool
34
+
35
+ # --
36
+ # : (untyped) -> bool
37
+ def eql?: (untyped) -> bool
38
+
39
+ # --
40
+ # : () -> Integer
41
+ def hash: () -> Integer
42
+ end
@@ -3,20 +3,23 @@
3
3
  # Represents an assistant (LLM) message in a conversation; may include tool
4
4
  # calls when the LLM requests tool execution.
5
5
  class Riffer::Messages::Assistant < Riffer::Messages::Base
6
- class ToolCall < Struct[untyped]
7
- attr_accessor call_id(): untyped
6
+ # The reasoning part types +reasoning_text+ reads; the rest carry no prose.
7
+ REASONING_TEXT_TYPES: Array[Symbol]
8
8
 
9
- attr_accessor name(): untyped
10
-
11
- attr_accessor arguments(): untyped
12
-
13
- def self.new: (?untyped call_id, ?untyped name, ?untyped arguments) -> instance
14
- | (?call_id: untyped, ?name: untyped, ?arguments: untyped) -> instance
15
- end
9
+ # Builds an Assistant message from a hash, or returns +msg+ unchanged when it
10
+ # is already an Assistant message. Raises Riffer::ArgumentError on an invalid
11
+ # tool call, reasoning part, or +finish_reason+.
12
+ # --
13
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::Assistant)) -> Riffer::Messages::Assistant
14
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::Assistant) -> Riffer::Messages::Assistant
16
15
 
17
16
  # Array of tool calls requested by the assistant.
18
17
  attr_reader tool_calls: Array[Riffer::Messages::Assistant::ToolCall]
19
18
 
19
+ # The model's reasoning blocks for this response, in the order the provider
20
+ # emitted them.
21
+ attr_reader reasoning: Array[Riffer::Messages::Assistant::ReasoningPart]
22
+
20
23
  # Token usage data for this response.
21
24
  attr_reader token_usage: Riffer::Providers::TokenUsage?
22
25
 
@@ -38,12 +41,13 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
38
41
  # String,
39
42
  # ?id: String?,
40
43
  # ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall],
44
+ # ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart],
41
45
  # ?token_usage: Riffer::Providers::TokenUsage?,
42
46
  # ?structured_output: Hash[Symbol, untyped]?,
43
47
  # ?finish_reason: Symbol?,
44
48
  # ?finish_reason_raw: String?
45
49
  # ) -> void
46
- def initialize: (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::Providers::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?, ?finish_reason: Symbol?, ?finish_reason_raw: String?) -> void
50
+ def initialize: (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart], ?token_usage: Riffer::Providers::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?, ?finish_reason: Symbol?, ?finish_reason_raw: String?) -> void
47
51
 
48
52
  # --
49
53
  # : () -> Symbol
@@ -57,6 +61,16 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
57
61
  # : () -> bool
58
62
  def has_tool_calls?: () -> bool
59
63
 
64
+ # --
65
+ # : () -> bool
66
+ def reasoning?: () -> bool
67
+
68
+ # The readable reasoning across this message's +:text+ and +:summary+ parts,
69
+ # joined by blank lines, or nil when it carries none.
70
+ # --
71
+ # : () -> String?
72
+ def reasoning_text: () -> String?
73
+
60
74
  # --
61
75
  # : (Riffer::Messages::Assistant) -> Riffer::Messages::Assistant
62
76
  def +: (Riffer::Messages::Assistant) -> Riffer::Messages::Assistant
@@ -2,6 +2,12 @@
2
2
 
3
3
  # Represents a system message (instructions) in a conversation.
4
4
  class Riffer::Messages::System < Riffer::Messages::Base
5
+ # Builds a System message from a hash, or returns +msg+ unchanged when it is
6
+ # already a System message.
7
+ # --
8
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::System)) -> Riffer::Messages::System
9
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::System) -> Riffer::Messages::System
10
+
5
11
  # --
6
12
  # : () -> Symbol
7
13
  def role: () -> Symbol
@@ -2,6 +2,12 @@
2
2
 
3
3
  # Represents a tool execution result in a conversation.
4
4
  class Riffer::Messages::Tool < Riffer::Messages::Base
5
+ # Builds a Tool message from a hash, or returns +msg+ unchanged when it is
6
+ # already a Tool message.
7
+ # --
8
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::Tool)) -> Riffer::Messages::Tool
9
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::Tool) -> Riffer::Messages::Tool
10
+
5
11
  # The ID of the tool call this result responds to.
6
12
  attr_reader tool_call_id: String
7
13
 
@@ -1,8 +1,8 @@
1
- # Generated from lib/riffer/messages/file_part.rb with RBS::Inline
1
+ # Generated from lib/riffer/messages/user/file_part.rb with RBS::Inline
2
2
 
3
3
  # Represents a file attachment (image or document) — from a URL (+from_url+) or
4
4
  # raw base64 data (+new+).
5
- class Riffer::Messages::FilePart
5
+ class Riffer::Messages::User::FilePart
6
6
  @data_bytes: String?
7
7
 
8
8
  @downloaded_data: String?
@@ -35,15 +35,20 @@ class Riffer::Messages::FilePart
35
35
  # Creates a FilePart from a URL, detecting +media_type+ from the path
36
36
  # extension when omitted. Raises Riffer::ArgumentError if it can't be detected.
37
37
  # --
38
- # : (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::FilePart
39
- def self.from_url: (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::FilePart
38
+ # : (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::User::FilePart
39
+ def self.from_url: (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::User::FilePart
40
40
 
41
41
  # Builds a FilePart from a +{url:, media_type:}+ or +{data:, media_type:}+ hash,
42
42
  # or returns +file+ unchanged when it is already a FilePart. Raises
43
43
  # Riffer::ArgumentError on an invalid hash.
44
44
  # --
45
- # : ((Hash[Symbol, untyped] | Riffer::Messages::FilePart)) -> Riffer::Messages::FilePart
46
- def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::FilePart) -> Riffer::Messages::FilePart
45
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::User::FilePart)) -> Riffer::Messages::User::FilePart
46
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::User::FilePart) -> Riffer::Messages::User::FilePart
47
+
48
+ # Raises Riffer::ArgumentError when the URL's extension isn't a known media type.
49
+ # --
50
+ # : (String) -> String
51
+ def self.detect_media_type: (String) -> String
47
52
 
48
53
  # The base64-encoded contents - caller-supplied, or filled in by the file
49
54
  # resolver after a download. Nil for a URL source riffer hasn't fetched.
@@ -2,12 +2,18 @@
2
2
 
3
3
  # Represents a user message in a conversation.
4
4
  class Riffer::Messages::User < Riffer::Messages::Base
5
+ # Builds a User message from a hash, or returns +msg+ unchanged when it is
6
+ # already a User message. Raises Riffer::ArgumentError on an invalid file.
7
+ # --
8
+ # : ((Hash[Symbol, untyped] | Riffer::Messages::User)) -> Riffer::Messages::User
9
+ def self.from_hash: (Hash[Symbol, untyped] | Riffer::Messages::User) -> Riffer::Messages::User
10
+
5
11
  # File attachments for this message.
6
- attr_reader files: Array[Riffer::Messages::FilePart]
12
+ attr_reader files: Array[Riffer::Messages::User::FilePart]
7
13
 
8
14
  # --
9
- # : (String, ?id: String?, ?files: Array[Riffer::Messages::FilePart]) -> void
10
- def initialize: (String, ?id: String?, ?files: Array[Riffer::Messages::FilePart]) -> void
15
+ # : (String, ?id: String?, ?files: Array[Riffer::Messages::User::FilePart]) -> void
16
+ def initialize: (String, ?id: String?, ?files: Array[Riffer::Messages::User::FilePart]) -> void
11
17
 
12
18
  # --
13
19
  # : () -> Symbol
@@ -84,6 +84,13 @@ class Riffer::Params::Param
84
84
 
85
85
  private
86
86
 
87
+ # +dup+ would leave the copy sharing this one's nested Params, enum list and
88
+ # default, so defining a parameter or editing either value on one would reach
89
+ # the other.
90
+ # --
91
+ # : (Riffer::Params::Param) -> void
92
+ def initialize_copy: (Riffer::Params::Param) -> void
93
+
87
94
  # --
88
95
  # : () -> void
89
96
  def validate_strict_shape!: () -> void
@@ -62,6 +62,12 @@ class Riffer::Params
62
62
 
63
63
  private
64
64
 
65
+ # +dup+ would leave the copy sharing this one's parameters, so a caller
66
+ # defining a parameter on either would reach the other.
67
+ # --
68
+ # : (Riffer::Params) -> void
69
+ def initialize_copy: (Riffer::Params) -> void
70
+
65
71
  # --
66
72
  # : (Module, Module?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
67
73
  def build_nested: (Module, Module?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
@@ -25,8 +25,8 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
25
25
  def initialize: () -> void
26
26
 
27
27
  # --
28
- # : (Riffer::Messages::FilePart) -> Symbol
29
- def file_delivery: (Riffer::Messages::FilePart) -> Symbol
28
+ # : (Riffer::Messages::User::FilePart) -> Symbol
29
+ def file_delivery: (Riffer::Messages::User::FilePart) -> Symbol
30
30
 
31
31
  private
32
32
 
@@ -145,8 +145,8 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
145
145
  def append_tool_result: (Array[Hash[Symbol, untyped]], Riffer::Messages::Tool) -> void
146
146
 
147
147
  # --
148
- # : (Riffer::Messages::FilePart) -> Hash[Symbol, untyped]
149
- def convert_file_part_to_bedrock_format: (Riffer::Messages::FilePart) -> Hash[Symbol, untyped]
148
+ # : (Riffer::Messages::User::FilePart) -> Hash[Symbol, untyped]
149
+ def convert_file_part_to_bedrock_format: (Riffer::Messages::User::FilePart) -> Hash[Symbol, untyped]
150
150
 
151
151
  BEDROCK_FORMAT_MAP: Hash[String, String]
152
152