riffer 0.28.0 → 0.29.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/architecture.md +18 -11
  3. data/.agents/code-style.md +1 -1
  4. data/.agents/rbs-inline.md +2 -2
  5. data/.agents/testing.md +9 -5
  6. data/.release-please-manifest.json +1 -1
  7. data/AGENTS.md +17 -10
  8. data/CHANGELOG.md +19 -0
  9. data/README.md +17 -18
  10. data/Steepfile +7 -1
  11. data/docs/03_AGENTS.md +34 -3
  12. data/docs/04_AGENT_LIFECYCLE.md +87 -86
  13. data/docs/05_AGENT_LOOP.md +2 -2
  14. data/docs/06_TOOLS.md +9 -4
  15. data/docs/07_TOOL_ADVANCED.md +17 -17
  16. data/docs/08_MESSAGES.md +25 -32
  17. data/docs/09_STREAM_EVENTS.md +1 -1
  18. data/docs/10_CONFIGURATION.md +7 -18
  19. data/docs/providers/01_PROVIDERS.md +6 -0
  20. data/docs/providers/06_MOCK_PROVIDER.md +2 -1
  21. data/docs/providers/07_CUSTOM_PROVIDERS.md +4 -4
  22. data/docs/providers/08_GEMINI.md +2 -2
  23. data/docs/providers/09_OPENROUTER.md +242 -0
  24. data/lib/riffer/agent/config.rb +173 -0
  25. data/lib/riffer/agent/context.rb +125 -0
  26. data/lib/riffer/agent/run.rb +308 -0
  27. data/lib/riffer/agent/session/repair.rb +112 -0
  28. data/lib/riffer/agent/session.rb +268 -0
  29. data/lib/riffer/{structured_output → agent/structured_output}/result.rb +1 -1
  30. data/lib/riffer/{structured_output.rb → agent/structured_output.rb} +4 -4
  31. data/lib/riffer/agent.rb +234 -923
  32. data/lib/riffer/config.rb +14 -7
  33. data/lib/riffer/evals/evaluator.rb +13 -3
  34. data/lib/riffer/evals/judge.rb +2 -2
  35. data/lib/riffer/evals/run_result.rb +2 -1
  36. data/lib/riffer/evals/scenario_result.rb +2 -1
  37. data/lib/riffer/guardrails/runner.rb +3 -2
  38. data/lib/riffer/helpers/call_or_value.rb +16 -0
  39. data/lib/riffer/helpers.rb +0 -1
  40. data/lib/riffer/mcp/authenticated_tool.rb +4 -0
  41. data/lib/riffer/mcp/client.rb +1 -1
  42. data/lib/riffer/mcp/registration.rb +2 -3
  43. data/lib/riffer/mcp/registry.rb +3 -1
  44. data/lib/riffer/mcp/tool_factory.rb +5 -0
  45. data/lib/riffer/messages/assistant.rb +9 -3
  46. data/lib/riffer/messages/base.rb +22 -0
  47. data/lib/riffer/messages/converter.rb +6 -6
  48. data/lib/riffer/{file_part.rb → messages/file_part.rb} +5 -5
  49. data/lib/riffer/messages/tool.rb +1 -1
  50. data/lib/riffer/messages/user.rb +4 -4
  51. data/lib/riffer/{boolean.rb → params/boolean.rb} +3 -3
  52. data/lib/riffer/{param.rb → params/param.rb} +6 -6
  53. data/lib/riffer/params.rb +27 -21
  54. data/lib/riffer/providers/amazon_bedrock.rb +19 -20
  55. data/lib/riffer/providers/anthropic.rb +27 -28
  56. data/lib/riffer/providers/base.rb +10 -9
  57. data/lib/riffer/providers/gemini.rb +15 -12
  58. data/lib/riffer/providers/mock.rb +41 -13
  59. data/lib/riffer/providers/open_ai.rb +24 -22
  60. data/lib/riffer/providers/open_router.rb +318 -0
  61. data/lib/riffer/providers/repository.rb +1 -0
  62. data/lib/riffer/{token_usage.rb → providers/token_usage.rb} +4 -4
  63. data/lib/riffer/providers.rb +1 -0
  64. data/lib/riffer/runner/fibers.rb +4 -3
  65. data/lib/riffer/runner/sequential.rb +1 -1
  66. data/lib/riffer/runner/threaded.rb +1 -1
  67. data/lib/riffer/runner.rb +1 -1
  68. data/lib/riffer/skills/activate_tool.rb +4 -3
  69. data/lib/riffer/skills/config.rb +1 -1
  70. data/lib/riffer/skills/context.rb +3 -3
  71. data/lib/riffer/skills/filesystem_backend.rb +7 -5
  72. data/lib/riffer/skills/markdown_adapter.rb +1 -1
  73. data/lib/riffer/skills/xml_adapter.rb +1 -1
  74. data/lib/riffer/stream_events/interrupt.rb +1 -1
  75. data/lib/riffer/stream_events/token_usage_done.rb +2 -2
  76. data/lib/riffer/stream_events/web_search_status.rb +1 -1
  77. data/lib/riffer/tool.rb +3 -3
  78. data/lib/riffer/{tool_runtime → tools/runtime}/fibers.rb +2 -2
  79. data/lib/riffer/{tool_runtime → tools/runtime}/inline.rb +1 -1
  80. data/lib/riffer/{tool_runtime → tools/runtime}/threaded.rb +2 -2
  81. data/lib/riffer/{tool_runtime.rb → tools/runtime.rb} +9 -9
  82. data/lib/riffer/{toolable.rb → tools/toolable.rb} +12 -9
  83. data/lib/riffer/version.rb +1 -1
  84. data/lib/riffer.rb +2 -1
  85. data/sig/generated/riffer/agent/config.rbs +119 -0
  86. data/sig/generated/riffer/agent/context.rbs +91 -0
  87. data/sig/generated/riffer/agent/run.rbs +144 -0
  88. data/sig/generated/riffer/agent/session/repair.rbs +51 -0
  89. data/sig/generated/riffer/agent/session.rbs +145 -0
  90. data/sig/generated/riffer/{structured_output → agent/structured_output}/result.rbs +2 -2
  91. data/sig/generated/riffer/{structured_output.rbs → agent/structured_output.rbs} +6 -6
  92. data/sig/generated/riffer/agent.rbs +143 -342
  93. data/sig/generated/riffer/config.rbs +17 -5
  94. data/sig/generated/riffer/evals/judge.rbs +2 -2
  95. data/sig/generated/riffer/helpers/call_or_value.rbs +9 -0
  96. data/sig/generated/riffer/helpers.rbs +0 -1
  97. data/sig/generated/riffer/messages/assistant.rbs +7 -3
  98. data/sig/generated/riffer/messages/base.rbs +18 -0
  99. data/sig/generated/riffer/messages/converter.rbs +4 -4
  100. data/sig/generated/riffer/{file_part.rbs → messages/file_part.rbs} +5 -5
  101. data/sig/generated/riffer/messages/user.rbs +4 -4
  102. data/sig/generated/riffer/params/boolean.rbs +10 -0
  103. data/sig/generated/riffer/{param.rbs → params/param.rbs} +3 -3
  104. data/sig/generated/riffer/params.rbs +15 -15
  105. data/sig/generated/riffer/providers/amazon_bedrock.rbs +22 -22
  106. data/sig/generated/riffer/providers/anthropic.rbs +4 -4
  107. data/sig/generated/riffer/providers/base.rbs +10 -10
  108. data/sig/generated/riffer/providers/gemini.rbs +4 -4
  109. data/sig/generated/riffer/providers/mock.rbs +25 -5
  110. data/sig/generated/riffer/providers/open_ai.rbs +4 -4
  111. data/sig/generated/riffer/providers/open_router.rbs +85 -0
  112. data/sig/generated/riffer/{token_usage.rbs → providers/token_usage.rbs} +5 -5
  113. data/sig/generated/riffer/providers.rbs +1 -0
  114. data/sig/generated/riffer/runner/fibers.rbs +2 -2
  115. data/sig/generated/riffer/runner/sequential.rbs +2 -2
  116. data/sig/generated/riffer/runner/threaded.rbs +2 -2
  117. data/sig/generated/riffer/runner.rbs +2 -2
  118. data/sig/generated/riffer/skills/activate_tool.rbs +4 -3
  119. data/sig/generated/riffer/skills/config.rbs +1 -1
  120. data/sig/generated/riffer/skills/context.rbs +2 -2
  121. data/sig/generated/riffer/stream_events/token_usage_done.rbs +3 -3
  122. data/sig/generated/riffer/tool.rbs +5 -5
  123. data/sig/generated/riffer/{tool_runtime → tools/runtime}/fibers.rbs +3 -3
  124. data/sig/generated/riffer/{tool_runtime → tools/runtime}/inline.rbs +2 -2
  125. data/sig/generated/riffer/{tool_runtime → tools/runtime}/threaded.rbs +3 -3
  126. data/sig/generated/riffer/{tool_runtime.rbs → tools/runtime.rbs} +12 -12
  127. data/sig/generated/riffer/{toolable.rbs → tools/toolable.rbs} +6 -6
  128. data/sig/stubs/agent_ivars.rbs +7 -0
  129. data/sig/stubs/async.rbs +24 -0
  130. data/sig/stubs/aws-sdk-core/seahorse_request_context.rbs +7 -0
  131. data/sig/stubs/aws-sdk-core/static_token_provider.rbs +5 -0
  132. data/sig/stubs/extend_self.rbs +11 -0
  133. data/sig/stubs/lib_ivars.rbs +101 -0
  134. data/sig/stubs/mcp_sdk.rbs +22 -0
  135. data/sig/stubs/provider_ivars.rbs +36 -0
  136. data/sig/stubs/provider_sdk_methods.rbs +50 -0
  137. data/sig/stubs/zeitwerk.rbs +12 -0
  138. metadata +54 -33
  139. data/lib/riffer/core.rb +0 -28
  140. data/lib/riffer/helpers/validations.rb +0 -18
  141. data/sig/generated/riffer/boolean.rbs +0 -10
  142. data/sig/generated/riffer/core.rbs +0 -19
  143. data/sig/generated/riffer/helpers/validations.rbs +0 -12
data/lib/riffer/tool.rb CHANGED
@@ -24,7 +24,7 @@ require "timeout"
24
24
  # end
25
25
  #
26
26
  class Riffer::Tool
27
- extend Riffer::Toolable
27
+ extend Riffer::Tools::Toolable
28
28
 
29
29
  kind :tool
30
30
 
@@ -33,7 +33,7 @@ class Riffer::Tool
33
33
  # Raises NotImplementedError if not implemented by subclass.
34
34
  #
35
35
  #--
36
- #: (context: Hash[Symbol, untyped]?, **untyped) -> Riffer::Tools::Response
36
+ #: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
37
37
  def call(context:, **kwargs)
38
38
  raise NotImplementedError, "#{self.class} must implement #call"
39
39
  end
@@ -69,7 +69,7 @@ class Riffer::Tool
69
69
  # Raises Riffer::Error if the tool does not return a Response object.
70
70
  #
71
71
  #--
72
- #: (context: Hash[Symbol, untyped]?, **untyped) -> Riffer::Tools::Response
72
+ #: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
73
73
  def call_with_validation(context:, **kwargs)
74
74
  params_builder = self.class.params
75
75
  validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
@@ -4,10 +4,10 @@
4
4
  # Executes tool calls concurrently using fibers via the +async+ gem.
5
5
  #
6
6
  # class MyAgent < Riffer::Agent
7
- # tool_runtime Riffer::ToolRuntime::Fibers
7
+ # tool_runtime Riffer::Tools::Runtime::Fibers
8
8
  # end
9
9
  #
10
- class Riffer::ToolRuntime::Fibers < Riffer::ToolRuntime
10
+ class Riffer::Tools::Runtime::Fibers < Riffer::Tools::Runtime
11
11
  # [max_concurrency] maximum number of tool calls to execute simultaneously.
12
12
  # When +nil+, all tool calls run as fibers without limit.
13
13
  #
@@ -5,7 +5,7 @@
5
5
  #
6
6
  # This is the default tool runtime used when no runtime is configured.
7
7
  #
8
- class Riffer::ToolRuntime::Inline < Riffer::ToolRuntime
8
+ class Riffer::Tools::Runtime::Inline < Riffer::Tools::Runtime
9
9
  #--
10
10
  #: () -> void
11
11
  def initialize
@@ -4,10 +4,10 @@
4
4
  # Executes tool calls concurrently using threads.
5
5
  #
6
6
  # class MyAgent < Riffer::Agent
7
- # tool_runtime Riffer::ToolRuntime::Threaded
7
+ # tool_runtime Riffer::Tools::Runtime::Threaded
8
8
  # end
9
9
  #
10
- class Riffer::ToolRuntime::Threaded < Riffer::ToolRuntime
10
+ class Riffer::Tools::Runtime::Threaded < Riffer::Tools::Runtime
11
11
  DEFAULT_MAX_CONCURRENCY = 5 #: Integer
12
12
 
13
13
  # [max_concurrency] maximum number of tool calls to execute simultaneously.
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "json"
5
5
 
6
- # Riffer::ToolRuntime handles tool call execution for an agent.
6
+ # Riffer::Tools::Runtime handles tool call execution for an agent.
7
7
  #
8
8
  # Composes with a Riffer::Runner for concurrency control and provides
9
9
  # +execute+ as the sole public entry point.
@@ -11,19 +11,19 @@ require "json"
11
11
  # Subclass and override +dispatch_tool_call+ to customize how individual
12
12
  # tool calls are dispatched (e.g., HTTP, gRPC).
13
13
  #
14
- # runtime = Riffer::ToolRuntime::Inline.new
14
+ # runtime = Riffer::Tools::Runtime::Inline.new
15
15
  # results = runtime.execute(tool_calls, tools: tools, context: context)
16
16
  #
17
- class Riffer::ToolRuntime
17
+ class Riffer::Tools::Runtime
18
18
  # [runner] the concurrency runner to use for batch execution.
19
19
  #
20
- # Subclasses must provide a runner; instantiating ToolRuntime directly
20
+ # Subclasses must provide a runner; instantiating Riffer::Tools::Runtime directly
21
21
  # raises +NotImplementedError+.
22
22
  #
23
23
  #--
24
24
  #: (runner: Riffer::Runner) -> void
25
25
  def initialize(runner:)
26
- raise NotImplementedError, "#{self.class} is abstract — use a subclass like Riffer::ToolRuntime::Inline" if instance_of?(Riffer::ToolRuntime)
26
+ raise NotImplementedError, "#{self.class} is abstract — use a subclass like Riffer::Tools::Runtime::Inline" if instance_of?(Riffer::Tools::Runtime)
27
27
  @runner = runner
28
28
  end
29
29
 
@@ -38,7 +38,7 @@ class Riffer::ToolRuntime
38
38
  # instrumentation that needs the accompanying assistant text).
39
39
  #
40
40
  #--
41
- #: (Array[Riffer::Messages::Assistant::ToolCall], tools: Array[singleton(Riffer::Tool)], context: Hash[Symbol, untyped]?, ?assistant_message: Riffer::Messages::Assistant?) -> Array[[Riffer::Messages::Assistant::ToolCall, Riffer::Tools::Response]]
41
+ #: (Array[Riffer::Messages::Assistant::ToolCall], tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Array[[Riffer::Messages::Assistant::ToolCall, Riffer::Tools::Response]]
42
42
  def execute(tool_calls, tools:, context:, assistant_message: nil)
43
43
  @runner.map(tool_calls, context: context) do |tool_call|
44
44
  result = around_tool_call(tool_call, context: context, assistant_message: assistant_message) do
@@ -53,7 +53,7 @@ class Riffer::ToolRuntime
53
53
  #
54
54
  # The default implementation simply yields.
55
55
  #
56
- # class InstrumentedRuntime < Riffer::ToolRuntime::Inline
56
+ # class InstrumentedRuntime < Riffer::Tools::Runtime::Inline
57
57
  # private
58
58
  #
59
59
  # def around_tool_call(tool_call, context:, assistant_message: nil)
@@ -65,7 +65,7 @@ class Riffer::ToolRuntime
65
65
  # end
66
66
  #
67
67
  #--
68
- #: (Riffer::Messages::Assistant::ToolCall, context: Hash[Symbol, untyped]?, ?assistant_message: Riffer::Messages::Assistant?) { () -> Riffer::Tools::Response } -> Riffer::Tools::Response
68
+ #: (Riffer::Messages::Assistant::ToolCall, context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) { () -> Riffer::Tools::Response } -> Riffer::Tools::Response
69
69
  def around_tool_call(tool_call, context:, assistant_message: nil)
70
70
  yield
71
71
  end
@@ -82,7 +82,7 @@ class Riffer::ToolRuntime
82
82
  # call, when known.
83
83
  #
84
84
  #--
85
- #: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Hash[Symbol, untyped]?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
85
+ #: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
86
86
  def dispatch_tool_call(tool_call, tools:, context:, assistant_message: nil)
87
87
  tool_class = tools.find { |tc| tc.name == tool_call.name }
88
88
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
- # Riffer::Toolable provides the shared class-level DSL for anything that can
4
+ # Riffer::Tools::Toolable provides the shared class-level DSL for anything that can
5
5
  # present as a tool to an LLM — tools today, and subagents/workflows in the
6
6
  # future.
7
7
  #
@@ -13,7 +13,7 @@
13
13
  # are NOT part of Toolable — those belong on Riffer::Tool.
14
14
  #
15
15
  # class MyTool
16
- # extend Riffer::Toolable
16
+ # extend Riffer::Tools::Toolable
17
17
  #
18
18
  # description "Does something useful"
19
19
  #
@@ -22,7 +22,7 @@
22
22
  # end
23
23
  # end
24
24
  #
25
- module Riffer::Toolable
25
+ module Riffer::Tools::Toolable
26
26
  DEFAULT_TIMEOUT = 10 #: Integer
27
27
 
28
28
  # Tracks all classes that extend Toolable.
@@ -31,8 +31,8 @@ module Riffer::Toolable
31
31
  #: (Module) -> void
32
32
  def self.extended(base)
33
33
  base.extend Riffer::Helpers::ClassNameConverter
34
- @extenders ||= []
35
- @extenders << base
34
+ extenders = (@extenders ||= []) #: Array[Module]
35
+ extenders << base
36
36
  end
37
37
 
38
38
  # Returns all classes that have extended Toolable.
@@ -82,11 +82,12 @@ module Riffer::Toolable
82
82
  # Defines parameters using the Params DSL.
83
83
  #
84
84
  #--
85
- #: () ?{ () -> void } -> Riffer::Params?
85
+ #: () ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
86
86
  def params(&block)
87
87
  return @params_builder if block.nil?
88
- @params_builder = Riffer::Params.new
89
- @params_builder.instance_eval(&block)
88
+ builder = Riffer::Params.new
89
+ builder.instance_eval(&block)
90
+ @params_builder = builder
90
91
  end
91
92
 
92
93
  # Returns the JSON Schema for the tool's parameters.
@@ -135,6 +136,8 @@ module Riffer::Toolable
135
136
  private
136
137
 
137
138
  def empty_schema # :nodoc:
138
- {type: "object", properties: {}, required: [], additionalProperties: false}
139
+ properties = {} #: Hash[Symbol, untyped]
140
+ required = [] #: Array[untyped]
141
+ {type: "object", properties: properties, required: required, additionalProperties: false}
139
142
  end
140
143
  end
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.28.0" #: String
5
+ VERSION = "0.29.0" #: String
6
6
  end
data/lib/riffer.rb CHANGED
@@ -11,7 +11,8 @@ require "zeitwerk"
11
11
  loader = Zeitwerk::Loader.for_gem
12
12
  loader.inflector.inflect(
13
13
  "open_ai" => "OpenAI",
14
- "azure_open_ai" => "AzureOpenAI"
14
+ "azure_open_ai" => "AzureOpenAI",
15
+ "open_router" => "OpenRouter"
15
16
  )
16
17
  loader.setup
17
18
 
@@ -0,0 +1,119 @@
1
+ # Generated from lib/riffer/agent/config.rb with RBS::Inline
2
+
3
+ # Typed configuration object holding every class-level DSL setting on a
4
+ # Riffer::Agent subclass.
5
+ #
6
+ # Each subclass of Riffer::Agent owns one Config, accessible via the class
7
+ # method <tt>config</tt>. The class-level DSL (+model+, +instructions+, +uses_tools+,
8
+ # etc.) reads and mutates this Config in place. Append-style DSL methods
9
+ # (+use_mcp+, +guardrail+) are handled by the +add_mcp+ and +add_guardrail+
10
+ # helpers below.
11
+ #
12
+ # Config stores Procs unresolved. Per-instance resolution happens elsewhere
13
+ # (instructions, model, tools, tool runtime, skills).
14
+ class Riffer::Agent::Config
15
+ DEFAULT_MAX_STEPS: Integer
16
+
17
+ attr_reader identifier: String?
18
+
19
+ attr_reader model: (String | Proc)?
20
+
21
+ attr_reader instructions: (String | Proc)?
22
+
23
+ attr_accessor provider_options: Hash[Symbol, untyped]
24
+
25
+ attr_accessor model_options: Hash[Symbol, untyped]
26
+
27
+ attr_reader structured_output: Riffer::Params?
28
+
29
+ attr_accessor max_steps: Numeric
30
+
31
+ attr_accessor tools_config: (Array[singleton(Riffer::Tool)] | Proc)?
32
+
33
+ attr_reader mcp_configs: Array[Hash[Symbol, untyped]]
34
+
35
+ attr_reader tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc
36
+
37
+ attr_accessor skills_config: Riffer::Skills::Config?
38
+
39
+ attr_reader guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]
40
+
41
+ # Builds a new Config. All fields are optional; unset fields take the
42
+ # documented defaults.
43
+ #
44
+ # Raises Riffer::ArgumentError if +model+ or +instructions+ is provided
45
+ # as a non-String, non-Proc value (or as an empty String).
46
+ #
47
+ # --
48
+ # : (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?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
49
+ def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?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
50
+
51
+ # Sets +identifier+. Accepts +nil+ or any value, coerced to String.
52
+ #
53
+ # --
54
+ # : (untyped) -> String?
55
+ def identifier=: (untyped) -> String?
56
+
57
+ # Sets +structured_output+. Accepts a Riffer::Params instance or +nil+.
58
+ #
59
+ # Raises Riffer::ArgumentError on any other type.
60
+ #
61
+ # --
62
+ # : (Riffer::Params?) -> Riffer::Params?
63
+ def structured_output=: (Riffer::Params?) -> Riffer::Params?
64
+
65
+ # Sets +tool_runtime+. Accepts a Riffer::Tools::Runtime subclass, a
66
+ # Riffer::Tools::Runtime instance, or a Proc.
67
+ #
68
+ # Raises Riffer::ArgumentError on any other type.
69
+ #
70
+ # --
71
+ # : ((singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
72
+ def tool_runtime=: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
73
+
74
+ # Sets +model+. Accepts a String ("provider/model"), a Proc, or +nil+.
75
+ #
76
+ # Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
77
+ #
78
+ # --
79
+ # : ((String | Proc)?) -> (String | Proc)?
80
+ def model=: ((String | Proc)?) -> (String | Proc)?
81
+
82
+ # Sets +instructions+. Accepts a String, a Proc, or +nil+.
83
+ #
84
+ # Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
85
+ #
86
+ # --
87
+ # : ((String | Proc)?) -> (String | Proc)?
88
+ def instructions=: ((String | Proc)?) -> (String | Proc)?
89
+
90
+ # Appends an MCP tag entry to +mcp_configs+.
91
+ #
92
+ # --
93
+ # : (String | Symbol) -> Array[Hash[Symbol, untyped]]
94
+ def add_mcp: (String | Symbol) -> Array[Hash[Symbol, untyped]]
95
+
96
+ # Appends a guardrail entry to +guardrails+ for the given phase.
97
+ #
98
+ # [phase] +:before+, +:after+, or +:around+. +:around+ appends to both
99
+ # +:before+ and +:after+.
100
+ # [klass] the Riffer::Guardrail subclass to register.
101
+ # [options] options forwarded to the guardrail at runtime.
102
+ #
103
+ # Raises Riffer::ArgumentError on an invalid phase or non-Guardrail class.
104
+ #
105
+ # --
106
+ # : (Symbol, klass: singleton(Riffer::Guardrail), ?options: Hash[Symbol, untyped]) -> void
107
+ def add_guardrail: (Symbol, klass: singleton(Riffer::Guardrail), ?options: Hash[Symbol, untyped]) -> void
108
+
109
+ # Returns the guardrail entries for the given phase, or +[]+ if none.
110
+ #
111
+ # --
112
+ # : (Symbol) -> Array[Hash[Symbol, untyped]]
113
+ def guardrails_for: (Symbol) -> Array[Hash[Symbol, untyped]]
114
+
115
+ private
116
+
117
+ # : (untyped, String) -> void
118
+ def validate_string_or_proc!: (untyped, String) -> void
119
+ end
@@ -0,0 +1,91 @@
1
+ # Generated from lib/riffer/agent/context.rb with RBS::Inline
2
+
3
+ # Typed value object wrapping the runtime context Hash held by a
4
+ # Riffer::Agent. Exposes first-class accessors for the framework-managed
5
+ # entries — +skills+ and +token_usage+ — and preserves +#[]+ / +#dig+
6
+ # reads so tools (which receive +context:+ as a keyword) keep working
7
+ # with both built-in and caller-provided keys.
8
+ #
9
+ # Reserved keys (+:skills+, +:token_usage+) cannot be set by the caller
10
+ # at construction; they are owned by Riffer and written through the typed
11
+ # setters. Type invariants are enforced on write — +skills+ must be a
12
+ # +Riffer::Skills::Context+ (or nil); +token_usage+ must be a
13
+ # +Riffer::Providers::TokenUsage+ (or nil).
14
+ #
15
+ # context = Riffer::Agent::Context.new(user_id: 42)
16
+ # context[:user_id] # => 42
17
+ # context.skills # => nil
18
+ # context.token_usage # => nil
19
+ class Riffer::Agent::Context
20
+ # Keys reserved for framework use. Passing any of these to the
21
+ # constructor raises +Riffer::ArgumentError+.
22
+ RESERVED_KEYS: Array[Symbol]
23
+
24
+ # Builds a new context.
25
+ #
26
+ # [data] caller-provided Hash passed as <tt>Agent.new(context:)</tt>.
27
+ # Duped before storage so caller mutations do not affect the
28
+ # agent. Must not contain any +RESERVED_KEYS+.
29
+ #
30
+ # Raises Riffer::ArgumentError when +data+ contains a reserved key.
31
+ #
32
+ # --
33
+ # : (?Hash[Symbol, untyped]) -> void
34
+ def initialize: (?Hash[Symbol, untyped]) -> void
35
+
36
+ # The agent's resolved +Riffer::Skills::Context+, or +nil+ when skills
37
+ # are not configured.
38
+ #
39
+ # --
40
+ # : () -> Riffer::Skills::Context?
41
+ def skills: () -> Riffer::Skills::Context?
42
+
43
+ # Sets the resolved skills context. Called once by +Riffer::Agent+
44
+ # during construction.
45
+ #
46
+ # Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
47
+ # +Riffer::Skills::Context+.
48
+ #
49
+ # --
50
+ # : (Riffer::Skills::Context?) -> Riffer::Skills::Context?
51
+ def skills=: (Riffer::Skills::Context?) -> Riffer::Skills::Context?
52
+
53
+ # The cumulative +Riffer::Providers::TokenUsage+ across every Run on this agent,
54
+ # or +nil+ before the first response is recorded.
55
+ #
56
+ # --
57
+ # : () -> Riffer::Providers::TokenUsage?
58
+ def token_usage: () -> Riffer::Providers::TokenUsage?
59
+
60
+ # Sets the cumulative token usage. Called by +Riffer::Agent::Run+ after
61
+ # each LLM response.
62
+ #
63
+ # Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
64
+ # +Riffer::Providers::TokenUsage+.
65
+ #
66
+ # --
67
+ # : (Riffer::Providers::TokenUsage?) -> Riffer::Providers::TokenUsage?
68
+ def token_usage=: (Riffer::Providers::TokenUsage?) -> Riffer::Providers::TokenUsage?
69
+
70
+ # Hash-style read. Preserved so downstream tool runtimes pulling
71
+ # caller-provided keys via <tt>context[:agent]</tt> or
72
+ # <tt>context[:tenant]</tt> keep working unchanged.
73
+ #
74
+ # --
75
+ # : (Symbol) -> untyped
76
+ def []: (Symbol) -> untyped
77
+
78
+ # Hash-style dig. Preserved for tools using
79
+ # <tt>context&.dig(:user_id)</tt>.
80
+ #
81
+ # --
82
+ # : (*Symbol) -> untyped
83
+ def dig: (*Symbol) -> untyped
84
+
85
+ # Returns a copy of the underlying Hash. Mutating the result does not
86
+ # affect this context.
87
+ #
88
+ # --
89
+ # : () -> Hash[Symbol, untyped]
90
+ def to_h: () -> Hash[Symbol, untyped]
91
+ end
@@ -0,0 +1,144 @@
1
+ # Generated from lib/riffer/agent/run.rb with RBS::Inline
2
+
3
+ # Riffer::Agent::Run is the generation loop. A pure module of functions over an
4
+ # +agent+ — Agent owns every per-call value (provider, model, tools, tool
5
+ # runtime, structured output, session, context); Run just orchestrates.
6
+ #
7
+ # Tools and user code see the agent's +context+ (a +Riffer::Agent::Context+)
8
+ # unchanged through the loop, so downstream tool runtimes can read
9
+ # caller-provided keys via <tt>context[:agent]</tt> /
10
+ # <tt>context.dig(:key)</tt>, or the framework built-ins via
11
+ # +context.skills+. Cumulative token usage is updated into
12
+ # +agent.context.token_usage+ as the loop progresses.
13
+ #
14
+ # Riffer::Agent::Run.generate(agent: my_agent, prompt: "Hello")
15
+ # Riffer::Agent::Run.stream(agent: my_agent, prompt: "Hello")
16
+ module Riffer::Agent::Run
17
+ include Riffer::Messages::Converter
18
+
19
+ # Runs the generate loop for the given agent. See Riffer::Agent#generate
20
+ # for prompt/files semantics.
21
+ #
22
+ # --
23
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
24
+ def generate: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
25
+
26
+ # Runs the streaming loop for the given agent. See Riffer::Agent#stream
27
+ # for prompt/files semantics.
28
+ #
29
+ # --
30
+ # : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
31
+ def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
32
+
33
+ private
34
+
35
+ # The generation loop. When +stream_yielder+ is provided, per-step events are
36
+ # pushed to it (and +stream+ discards the return value). When +stream_yielder+
37
+ # is +nil+, no events are emitted and +generate+ returns the Response
38
+ # directly. The two modes share every step of the loop — the only
39
+ # divergences are the LLM call shape (atomic vs. accumulated stream)
40
+ # and whether per-step events are emitted.
41
+ #
42
+ # --
43
+ # : (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
44
+ def run_loop: (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
45
+
46
+ # Consumes one provider stream, forwarding every event to +stream_yielder+
47
+ # and folding it into an +Assistant+ message.
48
+ #
49
+ # --
50
+ # : (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
51
+ def accumulate_streamed_response: (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
52
+
53
+ # Appends +new_modifications+ to +all_modifications+ and emits a
54
+ # +GuardrailModification+ event for each one when streaming.
55
+ #
56
+ # --
57
+ # : (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
58
+ def record_modifications!: (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
59
+
60
+ # Emits a +GuardrailTripwire+ event when streaming and returns the
61
+ # short-circuit +Response+ for a tripped guardrail.
62
+ #
63
+ # --
64
+ # : (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
65
+ def tripwire_response: (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
66
+
67
+ # Builds the final +Response+ from the session's last assistant
68
+ # message, validating structured output when configured. +extra+
69
+ # carries the interrupt-only fields (+interrupted:+, +interrupt_reason:+,
70
+ # +healed_tool_call_ids:+) on the interrupt exit path.
71
+ #
72
+ # --
73
+ # : (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
74
+ def final_response: (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
75
+
76
+ # --
77
+ # : (Riffer::Agent) -> Riffer::Messages::Assistant
78
+ def call_llm: (Riffer::Agent) -> Riffer::Messages::Assistant
79
+
80
+ # --
81
+ # : (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
82
+ def call_llm_stream: (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
83
+
84
+ # --
85
+ # : (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
86
+ def execute_tool_calls: (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
87
+
88
+ # Executes tool calls left unfinished by a prior interrupt.
89
+ #
90
+ # Detects gaps between the last assistant message's requested tool calls
91
+ # and the tool result messages that follow it, executing any that are
92
+ # missing. Safe to call unconditionally.
93
+ #
94
+ # --
95
+ # : (Riffer::Agent) -> void
96
+ def execute_pending_tool_calls: (Riffer::Agent) -> void
97
+
98
+ # Runs the +:before+ guardrail phase. Records any modifications into
99
+ # +all_modifications+ (and emits them when streaming). When a tripwire
100
+ # fires, yields the short-circuit +Response+ — the caller's block is
101
+ # expected to +return+ it from +run_loop+.
102
+ #
103
+ # --
104
+ # : (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
105
+ def run_before_guardrails: (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
106
+
107
+ # Runs the +:after+ guardrail phase against the assistant +response+.
108
+ # Records any modifications into +all_modifications+ (and emits them
109
+ # when streaming). When a tripwire fires, yields the short-circuit
110
+ # +Response+ — the caller's block is expected to +return+ it from
111
+ # +run_loop+. Otherwise returns the post-guardrails assistant message.
112
+ #
113
+ # --
114
+ # : (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
115
+ def run_after_guardrails: (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
116
+
117
+ # --
118
+ # : (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
119
+ def validate_structured_output: (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
120
+
121
+ # --
122
+ # : (Riffer::Agent) -> Hash[Symbol, untyped]
123
+ def merged_model_options: (Riffer::Agent) -> Hash[Symbol, untyped]
124
+
125
+ # --
126
+ # : (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
127
+ def build_response: (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
128
+
129
+ # Appends a +User+ message to the session. No-ops when +prompt+ is nil
130
+ # and +files+ is empty (the caller had nothing to add). Raises when
131
+ # +files+ are supplied without a +prompt+ — the provider needs text to
132
+ # anchor the attachments.
133
+ #
134
+ # --
135
+ # : (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
136
+ def append_user_message: (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
137
+
138
+ # Accumulates token usage into +agent.context.token_usage+. Updates the
139
+ # context so cumulative usage persists across every run on the agent.
140
+ #
141
+ # --
142
+ # : (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
143
+ def track_token_usage: (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
144
+ end
@@ -0,0 +1,51 @@
1
+ # Generated from lib/riffer/agent/session/repair.rb with RBS::Inline
2
+
3
+ # Riffer::Agent::Session::Repair holds the pure transformations that keep the
4
+ # +tool_use+ ↔ +tool_result+ invariant on a message array. No state, no
5
+ # instance — module-level functions only. Each entry point is gated by
6
+ # +Riffer.config.experimental_history_healing+: when the flag is off the
7
+ # function returns its input unchanged.
8
+ #
9
+ # Two seams:
10
+ #
11
+ # - +fill_orphans+ — fills orphan +tool_use+ blocks with placeholder
12
+ # results. Used on interrupt (caller-issued or +max_steps+).
13
+ # - +prune_orphans+ — drops orphan +tool_use+ blocks and parentless Tool
14
+ # messages from a caller-provided seed so it is well-formed before the
15
+ # next inference call. Used at construction time when
16
+ # +Riffer::Agent.new(session:)+ receives a session.
17
+ module Riffer::Agent::Session::Repair
18
+ # Placeholder used to fill orphan +tool_use+ blocks. Emitted as the
19
+ # +Riffer::Tools::Response+ body for each filled call_id.
20
+ ORPHAN_PLACEHOLDER: ^(Riffer::Messages::Assistant::ToolCall) -> Riffer::Tools::Response
21
+
22
+ # Fills any orphaned +tool_use+ in +messages+ with the
23
+ # +ORPHAN_PLACEHOLDER+ response. Each placeholder Tool message is
24
+ # inserted immediately after its parent assistant message. Returns
25
+ # +[new_messages, filled_call_ids]+; +filled_call_ids+ is empty when
26
+ # there are no orphans.
27
+ #
28
+ # No-op when +Riffer.config.experimental_history_healing+ is off:
29
+ # returns +[messages, []]+ with the same array reference.
30
+ #
31
+ # --
32
+ # : (Array[Riffer::Messages::Base]) -> [Array[Riffer::Messages::Base], Array[String]]
33
+ def self.fill_orphans: (Array[Riffer::Messages::Base]) -> [ Array[Riffer::Messages::Base], Array[String] ]
34
+
35
+ # Prunes a seeded message array so the +tool_use+ ↔ +tool_result+
36
+ # invariant holds. Drops orphaned tool exchanges (assistant +tool_call+
37
+ # with no matching Tool result) and parentless Tool messages. Returns a
38
+ # new array; the input is not mutated.
39
+ #
40
+ # Pending tool_calls on the resume boundary — the last assistant whose
41
+ # tail is purely Tool results (or empty) — are preserved. They get
42
+ # swept up by +execute_pending_tool_calls+ at the start of the next
43
+ # generate/stream call.
44
+ #
45
+ # No-op when +Riffer.config.experimental_history_healing+ is off:
46
+ # returns +messages+ unchanged.
47
+ #
48
+ # --
49
+ # : (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
50
+ def self.prune_orphans: (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
51
+ end