riffer 0.18.0 → 0.20.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/architecture.md +23 -0
  3. data/.agents/rdoc.md +16 -4
  4. data/.release-please-manifest.json +1 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +21 -0
  7. data/README.md +1 -1
  8. data/Rakefile +1 -2
  9. data/docs/01_OVERVIEW.md +2 -2
  10. data/docs/02_GETTING_STARTED.md +2 -2
  11. data/docs/03_AGENTS.md +20 -19
  12. data/docs/04_TOOLS.md +35 -5
  13. data/docs/06_STREAM_EVENTS.md +1 -1
  14. data/docs/07_CONFIGURATION.md +8 -8
  15. data/docs/08_EVALS.md +4 -3
  16. data/docs/10_SKILLS.md +1 -1
  17. data/docs_providers/01_PROVIDERS.md +19 -13
  18. data/docs_providers/02_AMAZON_BEDROCK.md +7 -7
  19. data/docs_providers/03_ANTHROPIC.md +7 -7
  20. data/docs_providers/04_OPENAI.md +7 -9
  21. data/docs_providers/05_AZURE_OPENAI.md +165 -0
  22. data/lib/riffer/agent/response.rb +18 -9
  23. data/lib/riffer/agent.rb +60 -7
  24. data/lib/riffer/config.rb +8 -1
  25. data/lib/riffer/core.rb +2 -0
  26. data/lib/riffer/evals/evaluator.rb +13 -5
  27. data/lib/riffer/evals/evaluator_runner.rb +12 -6
  28. data/lib/riffer/evals/judge.rb +13 -4
  29. data/lib/riffer/evals/result.rb +3 -0
  30. data/lib/riffer/evals/run_result.rb +3 -0
  31. data/lib/riffer/evals/scenario_result.rb +11 -3
  32. data/lib/riffer/file_part.rb +7 -0
  33. data/lib/riffer/guardrail.rb +14 -9
  34. data/lib/riffer/guardrails/modification.rb +5 -3
  35. data/lib/riffer/guardrails/result.rb +15 -7
  36. data/lib/riffer/guardrails/runner.rb +10 -5
  37. data/lib/riffer/guardrails/tripwire.rb +6 -4
  38. data/lib/riffer/helpers/class_name_converter.rb +1 -0
  39. data/lib/riffer/helpers/dependencies.rb +1 -0
  40. data/lib/riffer/helpers/validations.rb +1 -0
  41. data/lib/riffer/messages/assistant.rb +4 -0
  42. data/lib/riffer/messages/base.rb +3 -0
  43. data/lib/riffer/messages/converter.rb +5 -2
  44. data/lib/riffer/messages/system.rb +1 -0
  45. data/lib/riffer/messages/tool.rb +4 -0
  46. data/lib/riffer/messages/user.rb +3 -0
  47. data/lib/riffer/param.rb +7 -3
  48. data/lib/riffer/params.rb +10 -0
  49. data/lib/riffer/providers/amazon_bedrock.rb +19 -0
  50. data/lib/riffer/providers/anthropic.rb +23 -0
  51. data/lib/riffer/providers/azure_open_ai.rb +41 -0
  52. data/lib/riffer/providers/base.rb +20 -6
  53. data/lib/riffer/providers/mock.rb +10 -0
  54. data/lib/riffer/providers/open_ai.rb +21 -0
  55. data/lib/riffer/providers/repository.rb +2 -0
  56. data/lib/riffer/providers.rb +1 -0
  57. data/lib/riffer/runner/fibers.rb +62 -0
  58. data/lib/riffer/runner/sequential.rb +3 -2
  59. data/lib/riffer/runner/threaded.rb +5 -3
  60. data/lib/riffer/runner.rb +6 -4
  61. data/lib/riffer/skills/activate_tool.rb +3 -2
  62. data/lib/riffer/skills/adapter.rb +3 -1
  63. data/lib/riffer/skills/backend.rb +3 -1
  64. data/lib/riffer/skills/config.rb +4 -0
  65. data/lib/riffer/skills/context.rb +8 -3
  66. data/lib/riffer/skills/filesystem_backend.rb +6 -2
  67. data/lib/riffer/skills/frontmatter.rb +9 -3
  68. data/lib/riffer/skills/markdown_adapter.rb +2 -1
  69. data/lib/riffer/skills/xml_adapter.rb +2 -1
  70. data/lib/riffer/stream_events/base.rb +2 -0
  71. data/lib/riffer/stream_events/guardrail_modification.rb +7 -2
  72. data/lib/riffer/stream_events/guardrail_tripwire.rb +7 -2
  73. data/lib/riffer/stream_events/interrupt.rb +2 -0
  74. data/lib/riffer/stream_events/reasoning_delta.rb +2 -0
  75. data/lib/riffer/stream_events/reasoning_done.rb +2 -0
  76. data/lib/riffer/stream_events/skill_activation.rb +2 -0
  77. data/lib/riffer/stream_events/text_delta.rb +2 -0
  78. data/lib/riffer/stream_events/text_done.rb +2 -0
  79. data/lib/riffer/stream_events/token_usage_done.rb +2 -0
  80. data/lib/riffer/stream_events/tool_call_delta.rb +2 -0
  81. data/lib/riffer/stream_events/tool_call_done.rb +2 -0
  82. data/lib/riffer/stream_events/web_search_done.rb +2 -0
  83. data/lib/riffer/stream_events/web_search_status.rb +2 -0
  84. data/lib/riffer/structured_output/result.rb +3 -0
  85. data/lib/riffer/structured_output.rb +3 -0
  86. data/lib/riffer/token_usage.rb +5 -0
  87. data/lib/riffer/tool.rb +11 -0
  88. data/lib/riffer/tool_runtime/fibers.rb +19 -0
  89. data/lib/riffer/tool_runtime/inline.rb +1 -0
  90. data/lib/riffer/tool_runtime/threaded.rb +2 -1
  91. data/lib/riffer/tool_runtime.rb +14 -9
  92. data/lib/riffer/tools/response.rb +8 -0
  93. data/lib/riffer/version.rb +1 -1
  94. data/lib/riffer.rb +5 -1
  95. data/sig/generated/riffer/agent/response.rbs +17 -9
  96. data/sig/generated/riffer/agent.rbs +59 -6
  97. data/sig/generated/riffer/config.rbs +15 -1
  98. data/sig/generated/riffer/core.rbs +2 -0
  99. data/sig/generated/riffer/evals/evaluator.rbs +13 -5
  100. data/sig/generated/riffer/evals/evaluator_runner.rbs +8 -4
  101. data/sig/generated/riffer/evals/judge.rbs +13 -4
  102. data/sig/generated/riffer/evals/result.rbs +3 -0
  103. data/sig/generated/riffer/evals/run_result.rbs +3 -0
  104. data/sig/generated/riffer/evals/scenario_result.rbs +8 -2
  105. data/sig/generated/riffer/file_part.rbs +7 -0
  106. data/sig/generated/riffer/guardrail.rbs +14 -9
  107. data/sig/generated/riffer/guardrails/modification.rbs +5 -3
  108. data/sig/generated/riffer/guardrails/result.rbs +15 -7
  109. data/sig/generated/riffer/guardrails/runner.rbs +10 -5
  110. data/sig/generated/riffer/guardrails/tripwire.rbs +6 -4
  111. data/sig/generated/riffer/helpers/class_name_converter.rbs +1 -0
  112. data/sig/generated/riffer/helpers/dependencies.rbs +1 -0
  113. data/sig/generated/riffer/helpers/validations.rbs +1 -0
  114. data/sig/generated/riffer/messages/assistant.rbs +4 -0
  115. data/sig/generated/riffer/messages/base.rbs +3 -0
  116. data/sig/generated/riffer/messages/converter.rbs +5 -2
  117. data/sig/generated/riffer/messages/system.rbs +1 -0
  118. data/sig/generated/riffer/messages/tool.rbs +4 -0
  119. data/sig/generated/riffer/messages/user.rbs +3 -0
  120. data/sig/generated/riffer/param.rbs +7 -3
  121. data/sig/generated/riffer/params.rbs +10 -0
  122. data/sig/generated/riffer/providers/amazon_bedrock.rbs +19 -0
  123. data/sig/generated/riffer/providers/anthropic.rbs +23 -0
  124. data/sig/generated/riffer/providers/azure_open_ai.rbs +25 -0
  125. data/sig/generated/riffer/providers/base.rbs +20 -6
  126. data/sig/generated/riffer/providers/mock.rbs +10 -0
  127. data/sig/generated/riffer/providers/open_ai.rbs +21 -0
  128. data/sig/generated/riffer/providers/repository.rbs +1 -0
  129. data/sig/generated/riffer/providers.rbs +1 -0
  130. data/sig/generated/riffer/runner/fibers.rbs +27 -0
  131. data/sig/generated/riffer/runner/sequential.rbs +3 -2
  132. data/sig/generated/riffer/runner/threaded.rbs +5 -3
  133. data/sig/generated/riffer/runner.rbs +6 -4
  134. data/sig/generated/riffer/skills/activate_tool.rbs +3 -2
  135. data/sig/generated/riffer/skills/adapter.rbs +3 -1
  136. data/sig/generated/riffer/skills/backend.rbs +3 -1
  137. data/sig/generated/riffer/skills/config.rbs +4 -0
  138. data/sig/generated/riffer/skills/context.rbs +8 -3
  139. data/sig/generated/riffer/skills/filesystem_backend.rbs +6 -2
  140. data/sig/generated/riffer/skills/frontmatter.rbs +9 -3
  141. data/sig/generated/riffer/skills/markdown_adapter.rbs +2 -1
  142. data/sig/generated/riffer/skills/xml_adapter.rbs +2 -1
  143. data/sig/generated/riffer/stream_events/base.rbs +2 -0
  144. data/sig/generated/riffer/stream_events/guardrail_modification.rbs +7 -2
  145. data/sig/generated/riffer/stream_events/guardrail_tripwire.rbs +7 -2
  146. data/sig/generated/riffer/stream_events/interrupt.rbs +2 -0
  147. data/sig/generated/riffer/stream_events/reasoning_delta.rbs +2 -0
  148. data/sig/generated/riffer/stream_events/reasoning_done.rbs +2 -0
  149. data/sig/generated/riffer/stream_events/skill_activation.rbs +2 -0
  150. data/sig/generated/riffer/stream_events/text_delta.rbs +2 -0
  151. data/sig/generated/riffer/stream_events/text_done.rbs +2 -0
  152. data/sig/generated/riffer/stream_events/token_usage_done.rbs +2 -0
  153. data/sig/generated/riffer/stream_events/tool_call_delta.rbs +2 -0
  154. data/sig/generated/riffer/stream_events/tool_call_done.rbs +2 -0
  155. data/sig/generated/riffer/stream_events/web_search_done.rbs +2 -0
  156. data/sig/generated/riffer/stream_events/web_search_status.rbs +2 -0
  157. data/sig/generated/riffer/structured_output/result.rbs +3 -0
  158. data/sig/generated/riffer/structured_output.rbs +3 -0
  159. data/sig/generated/riffer/token_usage.rbs +5 -0
  160. data/sig/generated/riffer/tool.rbs +11 -0
  161. data/sig/generated/riffer/tool_runtime/fibers.rbs +15 -0
  162. data/sig/generated/riffer/tool_runtime/inline.rbs +1 -0
  163. data/sig/generated/riffer/tool_runtime/threaded.rbs +2 -1
  164. data/sig/generated/riffer/tool_runtime.rbs +13 -8
  165. data/sig/generated/riffer/tools/response.rbs +8 -0
  166. data/sig/generated/riffer.rbs +3 -0
  167. metadata +48 -7
  168. /data/docs_providers/{05_MOCK_PROVIDER.md → 06_MOCK_PROVIDER.md} +0 -0
  169. /data/docs_providers/{06_CUSTOM_PROVIDERS.md → 07_CUSTOM_PROVIDERS.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12ca19d6aeb04239a6938aa523a9e7254a1a5599a9629e6a333fc385a0d94de3
4
- data.tar.gz: dec4506d7e4800b9cf12aa817e5df14c65b8822930d50ad75b0c912a6d5099bf
3
+ metadata.gz: '09506e8a2dfa346e49231fa598f1e899066a5597ddd5b6738758ca49594ca7c2'
4
+ data.tar.gz: 684dc3caf2f6a59d052adb51a5c33bdf0a5542a2a375f188a1bc0a600a7df5ef
5
5
  SHA512:
6
- metadata.gz: 84b914f6160009c13ba5ac1ed6b659fe45ba13dea14ccb1dd94d529b9de4526c0c7afff84e6bbeac565b81d529e42689c07874da89f6509e4510d59756cad073
7
- data.tar.gz: df04dc8a8ee28e70b95073b790d268c7488f0a9cd16131e1acafbfa651a9254d8868d9042607a73a5f4572e5e0a5eaee84b27965034894e4022294344d89be1f
6
+ metadata.gz: 2b1c4119f8afac1367aac6a7d9caddd656a598dab1ad44c921802864e73815d19c3a4f96e554483d9062c9e87864bd870e8af260b9d7f5922331bdcc714ccb52
7
+ data.tar.gz: 5f8387e02a6b2638550e59265980c8de6ce9422b77f905dcc019360e039d59ffb5b8d76310ba90819f7f7b51a5af34588b0de40782255c57215d4bf32ca862c8
@@ -107,6 +107,29 @@ MyAgent.new.stream(persisted_messages) # cross-process resume
107
107
 
108
108
  On resume, `execute_pending_tool_calls` detects tool calls from the last assistant message that lack corresponding tool result messages and executes them before entering the LLM loop. This handles the case where an interrupt fired mid-way through tool execution.
109
109
 
110
+ ### Runner (`lib/riffer/runner.rb`)
111
+
112
+ Concurrency primitive for batch execution. Subclasses implement `#map(items, context: nil, &block)` to control how items are processed. The `context` keyword carries the agent's context hash, enabling runners that need it for job serialization or routing.
113
+
114
+ Built-in runners:
115
+ - `Sequential` — processes items in the current thread via `Array#map`
116
+ - `Threaded` — processes items concurrently using a thread pool with configurable `max_concurrency`
117
+
118
+ ```ruby
119
+ runner = Riffer::Runner::Threaded.new(max_concurrency: 3)
120
+ runner.map(items, context: ctx) { |item| process(item) }
121
+ ```
122
+
123
+ ### ToolRuntime (`lib/riffer/tool_runtime.rb`)
124
+
125
+ Composes with a Runner to execute tool calls. Provides `#execute` as the public entry point and `#around_tool_call` as a hook for instrumentation. Passes the agent context through to the runner.
126
+
127
+ Built-in runtimes:
128
+ - `Inline` — uses `Runner::Sequential` (default)
129
+ - `Threaded` — uses `Runner::Threaded`
130
+
131
+ Context flow: `Agent#execute_tool_calls` → `ToolRuntime#execute(tool_calls, tools:, context:)` → `Runner#map(tool_calls, context:) { dispatch }` → `Tool#call(context:, **args)`
132
+
110
133
  ## Key Patterns
111
134
 
112
135
  - Model config accepts a `provider/model` string (e.g., `openai/gpt-4`) or a Proc/lambda that returns one
data/.agents/rdoc.md CHANGED
@@ -4,18 +4,30 @@ Use RDoc prose comments for public API descriptions and RBS inline annotations f
4
4
 
5
5
  ## Parameters and Return Types
6
6
 
7
- Describe parameters in the RDoc prose comment. Use a single `#:` line for the RBS method signature (see [rbs-inline.md](rbs-inline.md) for the full type annotation syntax):
7
+ Describe parameters using RDoc labeled list syntax. Use a single `#:` line for the RBS method signature (see [rbs-inline.md](rbs-inline.md) for the full type annotation syntax):
8
8
 
9
9
  ```ruby
10
10
  # Creates a new agent.
11
11
  #
12
- # +name+ - the agent name.
13
- # +options+ - optional configuration.
12
+ # [name] the agent name.
13
+ # [options] optional configuration.
14
14
  #
15
+ #--
15
16
  #: (String, ?options: Hash[Symbol, untyped]) -> void
16
17
  def initialize(name, options: {})
17
18
  ```
18
19
 
20
+ Always add `#--` (RDoc stop directive) on the line before a standalone `#:` type annotation. Without it, RDoc treats `#:` as a label-list marker and corrupts the preceding comment into a `<pre>` block. Inline `#:` on the same line as code (e.g., `attr_reader :name #: String`) does not need this.
21
+
22
+ ## Inline Code
23
+
24
+ Use `+word+` for single-word inline code. For multi-word expressions (containing spaces, colons, or brackets), use `<tt>multi word expression</tt>`:
25
+
26
+ ```ruby
27
+ # Returns +nil+ when no instructions are configured.
28
+ # Equivalent to <tt>throw :riffer_interrupt, reason</tt>.
29
+ ```
30
+
19
31
  ## Attributes and Constants
20
32
 
21
33
  Use `#:` inline syntax (on the same line) for attribute and constant types:
@@ -37,7 +49,7 @@ Document with prose:
37
49
 
38
50
  ## Examples
39
51
 
40
- Include usage examples as indented code blocks:
52
+ Include usage examples as indented code blocks (2 extra spaces of indent):
41
53
 
42
54
  ```ruby
43
55
  # Creates a new agent.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.18.0"
2
+ ".": "0.20.0"
3
3
  }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.8
1
+ 3.4.8
data/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.20.0](https://github.com/janeapp/riffer/compare/riffer/v0.19.0...riffer/v0.20.0) (2026-03-26)
9
+
10
+
11
+ ### Features
12
+
13
+ * add fibers tool runtime using async gem ([#178](https://github.com/janeapp/riffer/issues/178)) ([67fd344](https://github.com/janeapp/riffer/commit/67fd34493126559b99a00cc3402a6adabefc14ea))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * correct RDoc formatting for docs.riffer.ai ([#182](https://github.com/janeapp/riffer/issues/182)) ([2f2fbc9](https://github.com/janeapp/riffer/commit/2f2fbc997281ef4a546a58e44233e325a25c41d5))
19
+
20
+ ## [0.19.0](https://github.com/janeapp/riffer/compare/riffer/v0.18.0...riffer/v0.19.0) (2026-03-25)
21
+
22
+
23
+ ### Features
24
+
25
+ * Add Azure OpenAI provider ([#167](https://github.com/janeapp/riffer/issues/167)) ([5d34fcd](https://github.com/janeapp/riffer/commit/5d34fcd4a98a6bcfa768c50fea7c25959eca5f1d))
26
+ * expose message history in eval results ([#171](https://github.com/janeapp/riffer/issues/171)) ([c8b1aec](https://github.com/janeapp/riffer/commit/c8b1aeceb40a173c70d4be445fb82bba10113b87))
27
+ * provide agent context to runners ([#181](https://github.com/janeapp/riffer/issues/181)) ([23c9282](https://github.com/janeapp/riffer/commit/23c9282d4f2076d1ff6185f99bcf855f158ccb0d))
28
+
8
29
  ## [0.18.0](https://github.com/janeapp/riffer/compare/riffer/v0.17.0...riffer/v0.18.0) (2026-03-13)
9
30
 
10
31
 
data/README.md CHANGED
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  # Define an agent
36
36
  class EchoAgent < Riffer::Agent
37
- model 'openai/gpt-4o'
37
+ model 'openai/gpt-5-mini'
38
38
  instructions 'You are an assistant that repeats what the user says.'
39
39
  end
40
40
 
data/Rakefile CHANGED
@@ -17,8 +17,7 @@ RDoc::Task.new do |rdoc|
17
17
  rdoc.rdoc_files.include("README.md", "CHANGELOG.md", "LICENSE.txt", "docs/**/*.md", "docs_providers/**/*.md")
18
18
  rdoc.rdoc_files.include("lib/**/*.rb")
19
19
 
20
- # Use Markdown where available and ensure UTF-8
21
- rdoc.options << "--charset" << "utf-8" << "--markup" << "markdown"
20
+ rdoc.options << "--charset" << "utf-8"
22
21
  end
23
22
 
24
23
  task docs: :rdoc
data/docs/01_OVERVIEW.md CHANGED
@@ -10,7 +10,7 @@ The Agent is the central orchestrator for AI interactions. It manages messages,
10
10
 
11
11
  ```ruby
12
12
  class MyAgent < Riffer::Agent
13
- model 'openai/gpt-4o'
13
+ model 'openai/gpt-5-mini'
14
14
  instructions 'You are a helpful assistant.'
15
15
  end
16
16
  ```
@@ -43,7 +43,7 @@ Agents can return structured JSON responses that conform to a schema. The respon
43
43
 
44
44
  ```ruby
45
45
  class SentimentAgent < Riffer::Agent
46
- model 'openai/gpt-4o'
46
+ model 'openai/gpt-5-mini'
47
47
  structured_output do
48
48
  required :sentiment, String
49
49
  required :score, Float
@@ -68,7 +68,7 @@ Riffer.configure do |config|
68
68
  end
69
69
 
70
70
  class GreetingAgent < Riffer::Agent
71
- model 'openai/gpt-4o'
71
+ model 'openai/gpt-5-mini'
72
72
  instructions 'You are a friendly assistant. Greet the user warmly.'
73
73
  end
74
74
 
@@ -109,7 +109,7 @@ class TimeTool < Riffer::Tool
109
109
  end
110
110
 
111
111
  class TimeAgent < Riffer::Agent
112
- model 'openai/gpt-4o'
112
+ model 'openai/gpt-5-mini'
113
113
  instructions 'You can tell the user the current time.'
114
114
  uses_tools [TimeTool]
115
115
  end
data/docs/03_AGENTS.md CHANGED
@@ -8,7 +8,7 @@ Create an agent by subclassing `Riffer::Agent`:
8
8
 
9
9
  ```ruby
10
10
  class MyAgent < Riffer::Agent
11
- model 'openai/gpt-4o'
11
+ model 'openai/gpt-5-mini'
12
12
  instructions 'You are a helpful assistant.'
13
13
  end
14
14
  ```
@@ -21,9 +21,9 @@ Sets the provider and model in `provider/model` format:
21
21
 
22
22
  ```ruby
23
23
  class MyAgent < Riffer::Agent
24
- model 'openai/gpt-4o' # OpenAI
24
+ model 'openai/gpt-5-mini' # OpenAI
25
25
  # or
26
- model 'amazon_bedrock/anthropic.claude-3-sonnet-20240229-v1:0' # Bedrock
26
+ model 'amazon_bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0' # Bedrock
27
27
  # or
28
28
  model 'mock/any' # Mock provider
29
29
  end
@@ -33,7 +33,7 @@ Models can also be resolved dynamically with a lambda:
33
33
 
34
34
  ```ruby
35
35
  class MyAgent < Riffer::Agent
36
- model -> { "anthropic/claude-sonnet-4-20250514" }
36
+ model -> { "anthropic/claude-haiku-4-5-20251001" }
37
37
  end
38
38
  ```
39
39
 
@@ -42,7 +42,7 @@ When the lambda accepts a parameter, it receives the `context`:
42
42
  ```ruby
43
43
  class MyAgent < Riffer::Agent
44
44
  model ->(context) {
45
- context&.dig(:premium) ? "anthropic/claude-sonnet-4-20250514" : "anthropic/claude-haiku-4-5-20251001"
45
+ context&.dig(:premium) ? "anthropic/claude-sonnet-4-5-20250929" : "anthropic/claude-haiku-4-5-20251001"
46
46
  }
47
47
  end
48
48
  ```
@@ -55,7 +55,7 @@ Sets system instructions for the agent:
55
55
 
56
56
  ```ruby
57
57
  class MyAgent < Riffer::Agent
58
- model 'openai/gpt-4o'
58
+ model 'openai/gpt-5-mini'
59
59
  instructions 'You are an expert Ruby programmer. Provide concise answers.'
60
60
  end
61
61
  ```
@@ -64,7 +64,7 @@ Instructions can also be resolved dynamically with a lambda:
64
64
 
65
65
  ```ruby
66
66
  class MyAgent < Riffer::Agent
67
- model 'openai/gpt-4o'
67
+ model 'openai/gpt-5-mini'
68
68
  instructions -> { "Today is #{Date.today}. You are a helpful assistant." }
69
69
  end
70
70
  ```
@@ -73,7 +73,7 @@ When the lambda accepts a parameter, it receives the `context`:
73
73
 
74
74
  ```ruby
75
75
  class MyAgent < Riffer::Agent
76
- model 'openai/gpt-4o'
76
+ model 'openai/gpt-5-mini'
77
77
  instructions ->(ctx) { "You are assisting #{ctx[:name]}" }
78
78
  end
79
79
 
@@ -88,7 +88,7 @@ Sets a custom identifier (defaults to snake_case class name):
88
88
 
89
89
  ```ruby
90
90
  class MyAgent < Riffer::Agent
91
- model 'openai/gpt-4o'
91
+ model 'openai/gpt-5-mini'
92
92
  identifier 'custom_agent_name'
93
93
  end
94
94
 
@@ -101,7 +101,7 @@ Registers tools the agent can use:
101
101
 
102
102
  ```ruby
103
103
  class MyAgent < Riffer::Agent
104
- model 'openai/gpt-4o'
104
+ model 'openai/gpt-5-mini'
105
105
  uses_tools [WeatherTool, TimeTool]
106
106
  end
107
107
  ```
@@ -110,7 +110,7 @@ Tools can also be resolved dynamically with a lambda:
110
110
 
111
111
  ```ruby
112
112
  class MyAgent < Riffer::Agent
113
- model 'openai/gpt-4o'
113
+ model 'openai/gpt-5-mini'
114
114
 
115
115
  uses_tools ->(context) {
116
116
  tools = [PublicTool]
@@ -126,7 +126,7 @@ Passes options to the provider client:
126
126
 
127
127
  ```ruby
128
128
  class MyAgent < Riffer::Agent
129
- model 'openai/gpt-4o'
129
+ model 'openai/gpt-5-mini'
130
130
  provider_options api_key: ENV['CUSTOM_OPENAI_KEY']
131
131
  end
132
132
  ```
@@ -137,7 +137,7 @@ Passes options to each LLM request:
137
137
 
138
138
  ```ruby
139
139
  class MyAgent < Riffer::Agent
140
- model 'openai/gpt-4o'
140
+ model 'openai/gpt-5-mini'
141
141
  model_options reasoning: 'medium', temperature: 0.7, web_search: true
142
142
  end
143
143
  ```
@@ -148,7 +148,7 @@ Sets the maximum number of LLM call steps in the tool-use loop. When the limit i
148
148
 
149
149
  ```ruby
150
150
  class MyAgent < Riffer::Agent
151
- model 'openai/gpt-4o'
151
+ model 'openai/gpt-5-mini'
152
152
  max_steps 8
153
153
  end
154
154
  ```
@@ -159,7 +159,7 @@ Configures the agent to return structured JSON responses conforming to a schema.
159
159
 
160
160
  ```ruby
161
161
  class SentimentAgent < Riffer::Agent
162
- model 'openai/gpt-4o'
162
+ model 'openai/gpt-5-mini'
163
163
  instructions 'Analyze the sentiment of the given text.'
164
164
  structured_output do
165
165
  required :sentiment, String, description: "positive, negative, or neutral"
@@ -249,7 +249,7 @@ Configures how tool calls are executed. Defaults to sequential (inline) executio
249
249
 
250
250
  ```ruby
251
251
  class MyAgent < Riffer::Agent
252
- model 'openai/gpt-4o'
252
+ model 'openai/gpt-5-mini'
253
253
  uses_tools [WeatherTool, SearchTool]
254
254
  tool_runtime Riffer::ToolRuntime::Threaded
255
255
  end
@@ -263,7 +263,7 @@ Registers guardrails for pre/post processing of messages. Pass the guardrail cla
263
263
 
264
264
  ```ruby
265
265
  class MyAgent < Riffer::Agent
266
- model 'openai/gpt-4o'
266
+ model 'openai/gpt-5-mini'
267
267
 
268
268
  # Input-only guardrail
269
269
  guardrail :before, with: InputValidator
@@ -544,6 +544,7 @@ Returns `nil` if the provider doesn't report usage, or a `Riffer::TokenUsage` ob
544
544
  | `modifications` | `Array` | List of guardrail modifications applied |
545
545
  | `interrupted?` | `Boolean` | `true` if the loop was interrupted |
546
546
  | `interrupt_reason` | `String` / `Symbol` / `nil` | The reason passed to `throw :riffer_interrupt` |
547
+ | `messages` | `Array` | Full message history from the conversation |
547
548
 
548
549
  ### response.structured_output
549
550
 
@@ -629,7 +630,7 @@ Guardrails are registered at class definition time and run automatically on ever
629
630
 
630
631
  ```ruby
631
632
  class MyAgent < Riffer::Agent
632
- model 'openai/gpt-4o'
633
+ model 'openai/gpt-5-mini'
633
634
  guardrail :before, with: ContentPolicy
634
635
  end
635
636
 
@@ -670,7 +671,7 @@ The `max_steps` class method caps the number of LLM call steps in the tool-use l
670
671
 
671
672
  ```ruby
672
673
  class MyAgent < Riffer::Agent
673
- model 'openai/gpt-4o'
674
+ model 'openai/gpt-5-mini'
674
675
  max_steps 8
675
676
  end
676
677
 
data/docs/04_TOOLS.md CHANGED
@@ -324,7 +324,7 @@ WeatherTool.parameters_schema
324
324
 
325
325
  ```ruby
326
326
  class MyAgent < Riffer::Agent
327
- model 'openai/gpt-4o'
327
+ model 'openai/gpt-5-mini'
328
328
  uses_tools [WeatherTool, SearchTool]
329
329
  end
330
330
  ```
@@ -335,7 +335,7 @@ Use a lambda for context-aware tool resolution:
335
335
 
336
336
  ```ruby
337
337
  class MyAgent < Riffer::Agent
338
- model 'openai/gpt-4o'
338
+ model 'openai/gpt-5-mini'
339
339
 
340
340
  uses_tools ->(context) {
341
341
  tools = [PublicSearchTool]
@@ -384,6 +384,7 @@ By default, tool calls are executed sequentially in the current thread using `Ri
384
384
  | ------------------------------- | ---------------------------------------------- |
385
385
  | `Riffer::ToolRuntime::Inline` | Executes tool calls sequentially (default) |
386
386
  | `Riffer::ToolRuntime::Threaded` | Executes tool calls concurrently using threads |
387
+ | `Riffer::ToolRuntime::Fibers` | Executes tool calls concurrently using fibers |
387
388
 
388
389
  ### Per-Agent Configuration
389
390
 
@@ -391,7 +392,7 @@ Use the `tool_runtime` class method on your agent:
391
392
 
392
393
  ```ruby
393
394
  class MyAgent < Riffer::Agent
394
- model 'openai/gpt-4o'
395
+ model 'openai/gpt-5-mini'
395
396
  uses_tools [WeatherTool, SearchTool]
396
397
  tool_runtime Riffer::ToolRuntime::Threaded
397
398
  end
@@ -409,7 +410,7 @@ Use a lambda for context-aware runtime selection:
409
410
 
410
411
  ```ruby
411
412
  class MyAgent < Riffer::Agent
412
- model 'openai/gpt-4o'
413
+ model 'openai/gpt-5-mini'
413
414
  uses_tools [WeatherTool, SearchTool]
414
415
 
415
416
  tool_runtime ->(context) {
@@ -444,12 +445,41 @@ The threaded runtime accepts a `max_concurrency` option (default: 5):
444
445
 
445
446
  ```ruby
446
447
  class MyAgent < Riffer::Agent
447
- model 'openai/gpt-4o'
448
+ model 'openai/gpt-5-mini'
448
449
  uses_tools [WeatherTool, SearchTool]
449
450
  tool_runtime Riffer::ToolRuntime::Threaded.new(max_concurrency: 3)
450
451
  end
451
452
  ```
452
453
 
454
+ ### Fibers Runtime
455
+
456
+ The fibers runtime uses the [async](https://github.com/socketry/async) gem for lightweight, cooperative concurrency. It requires the `async` gem to be installed:
457
+
458
+ ```ruby
459
+ # Gemfile
460
+ gem "async"
461
+ ```
462
+
463
+ ```ruby
464
+ class MyAgent < Riffer::Agent
465
+ model 'openai/gpt-5-mini'
466
+ uses_tools [WeatherTool, SearchTool]
467
+ tool_runtime Riffer::ToolRuntime::Fibers
468
+ end
469
+ ```
470
+
471
+ By default, all tool calls run as fibers without a concurrency limit. You can optionally set a limit:
472
+
473
+ ```ruby
474
+ class MyAgent < Riffer::Agent
475
+ model 'openai/gpt-5-mini'
476
+ uses_tools [WeatherTool, SearchTool]
477
+ tool_runtime Riffer::ToolRuntime::Fibers.new(max_concurrency: 10)
478
+ end
479
+ ```
480
+
481
+ Fibers use cooperative scheduling — they yield control at I/O boundaries (network calls, file reads, sleep). CPU-bound tools will not benefit from the fibers runtime. Be mindful of fiber-local state (`Fiber.[]`) and note that `Thread.current[]` values are shared across all fibers in the same thread.
482
+
453
483
  ### Custom Runtimes
454
484
 
455
485
  Create a custom runtime by subclassing `Riffer::ToolRuntime` and overriding the private `dispatch_tool_call` method:
@@ -266,7 +266,7 @@ end
266
266
 
267
267
  ```ruby
268
268
  class WeatherAgent < Riffer::Agent
269
- model 'openai/gpt-4o'
269
+ model 'openai/gpt-5-mini'
270
270
  instructions 'You are a weather assistant.'
271
271
  uses_tools [WeatherTool]
272
272
  end
@@ -102,7 +102,7 @@ Pass options directly to the provider client:
102
102
 
103
103
  ```ruby
104
104
  class MyAgent < Riffer::Agent
105
- model 'openai/gpt-4o'
105
+ model 'openai/gpt-5-mini'
106
106
 
107
107
  # Override API key for this agent only
108
108
  provider_options api_key: ENV['CUSTOM_OPENAI_KEY']
@@ -115,7 +115,7 @@ Pass options to each LLM request:
115
115
 
116
116
  ```ruby
117
117
  class MyAgent < Riffer::Agent
118
- model 'openai/gpt-4o'
118
+ model 'openai/gpt-5-mini'
119
119
 
120
120
  # These options are sent with every generate/stream call
121
121
  model_options temperature: 0.7, reasoning: 'medium'
@@ -136,7 +136,7 @@ end
136
136
 
137
137
  ```ruby
138
138
  class MyAgent < Riffer::Agent
139
- model 'openai/gpt-4o'
139
+ model 'openai/gpt-5-mini'
140
140
  model_options temperature: 0.7, reasoning: 'medium'
141
141
  end
142
142
  ```
@@ -152,7 +152,7 @@ Options are passed through to the [Bedrock Converse API](https://docs.aws.amazon
152
152
 
153
153
  ```ruby
154
154
  class MyAgent < Riffer::Agent
155
- model 'amazon_bedrock/anthropic.claude-3-sonnet-20240229-v1:0'
155
+ model 'amazon_bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0'
156
156
  model_options inference_config: {temperature: 0.7, max_tokens: 4096}
157
157
  end
158
158
  ```
@@ -170,13 +170,13 @@ end
170
170
 
171
171
  ```ruby
172
172
  class MyAgent < Riffer::Agent
173
- model 'anthropic/claude-3-5-sonnet-20241022'
173
+ model 'anthropic/claude-haiku-4-5-20251001'
174
174
  model_options temperature: 0.7, max_tokens: 4096
175
175
  end
176
176
 
177
177
  # With extended thinking (Claude 3.7+)
178
178
  class ReasoningAgent < Riffer::Agent
179
- model 'anthropic/claude-3-7-sonnet-20250219'
179
+ model 'anthropic/claude-haiku-4-5-20251001'
180
180
  model_options thinking: {type: "enabled", budget_tokens: 10000}
181
181
  end
182
182
  ```
@@ -205,12 +205,12 @@ For different environments or use cases, use agent-level overrides:
205
205
 
206
206
  ```ruby
207
207
  class ProductionAgent < Riffer::Agent
208
- model 'openai/gpt-4o'
208
+ model 'openai/gpt-5-mini'
209
209
  provider_options api_key: ENV['PRODUCTION_OPENAI_KEY']
210
210
  end
211
211
 
212
212
  class DevelopmentAgent < Riffer::Agent
213
- model 'openai/gpt-4o-mini'
213
+ model 'openai/gpt-5-mini'
214
214
  provider_options api_key: ENV['DEV_OPENAI_KEY']
215
215
  model_options temperature: 0.0 # Deterministic for testing
216
216
  end
data/docs/08_EVALS.md CHANGED
@@ -120,6 +120,7 @@ scenario.output # => "The capital of France is Paris."
120
120
  scenario.ground_truth # => "Paris"
121
121
  scenario.scores # => { EvaluatorClass => score } for this scenario
122
122
  scenario.results # => Array of Result objects
123
+ scenario.messages # => Array of Message objects (system, user, assistant, tool)
123
124
  scenario.to_h # => Hash representation
124
125
  ```
125
126
 
@@ -183,7 +184,7 @@ Class methods:
183
184
 
184
185
  Instance methods:
185
186
 
186
- - `evaluate(input:, output:, ground_truth:)` - Override for custom logic; default calls judge with `instructions`
187
+ - `evaluate(input:, output:, ground_truth:, messages:)` - Override for custom logic; default calls judge with `instructions`
187
188
  - `judge` - Returns a Judge instance for LLM-as-judge calls
188
189
  - `result(score:, reason:, metadata:)` - Helper to build Result objects
189
190
 
@@ -196,7 +197,7 @@ class CustomEvaluator < Riffer::Evals::Evaluator
196
197
  higher_is_better true
197
198
  judge_model "anthropic/claude-opus-4-5-20251101"
198
199
 
199
- def evaluate(input:, output:, ground_truth: nil)
200
+ def evaluate(input:, output:, ground_truth: nil, messages: [])
200
201
  evaluation = judge.evaluate(
201
202
  instructions: "Custom evaluation criteria...",
202
203
  input: input,
@@ -217,7 +218,7 @@ Evaluators don't have to use LLM-as-judge:
217
218
  class LengthEvaluator < Riffer::Evals::Evaluator
218
219
  higher_is_better true
219
220
 
220
- def evaluate(input:, output:, ground_truth: nil)
221
+ def evaluate(input:, output:, ground_truth: nil, messages: [])
221
222
  min_length = 50
222
223
  max_length = 500
223
224
 
data/docs/10_SKILLS.md CHANGED
@@ -44,7 +44,7 @@ Use the `skills` block DSL to configure skills:
44
44
 
45
45
  ```ruby
46
46
  class MyAgent < Riffer::Agent
47
- model "openai/gpt-4o"
47
+ model "openai/gpt-5-mini"
48
48
  instructions "You are a helpful assistant."
49
49
  skills do
50
50
  backend Riffer::Skills::FilesystemBackend.new(".skills")
@@ -7,6 +7,7 @@ Providers are adapters that connect Riffer to LLM services. They implement a com
7
7
  | Provider | Identifier | Gem Required |
8
8
  | -------------- | ---------------- | ------------------------ |
9
9
  | OpenAI | `openai` | `openai` |
10
+ | Azure OpenAI | `azure_openai` | `openai` |
10
11
  | Amazon Bedrock | `amazon_bedrock` | `aws-sdk-bedrockruntime` |
11
12
  | Anthropic | `anthropic` | `anthropic` |
12
13
  | Mock | `mock` | None |
@@ -17,10 +18,11 @@ Agents specify providers using the `provider/model` format:
17
18
 
18
19
  ```ruby
19
20
  class MyAgent < Riffer::Agent
20
- model 'openai/gpt-4o' # OpenAI
21
- model 'amazon_bedrock/anthropic.claude-3-sonnet-20240229-v1:0' # Bedrock
22
- model 'anthropic/claude-3-5-sonnet-20241022' # Anthropic
23
- model 'mock/any' # Mock provider
21
+ model 'openai/gpt-5-mini' # OpenAI
22
+ model 'azure_openai/gpt-5-mini' # Azure OpenAI
23
+ model 'amazon_bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0' # Bedrock
24
+ model 'anthropic/claude-haiku-4-5-20251001' # Anthropic
25
+ model 'mock/any' # Mock provider
24
26
  end
25
27
  ```
26
28
 
@@ -37,14 +39,14 @@ provider = Riffer::Providers::OpenAI.new(api_key: "...")
37
39
 
38
40
  response = provider.generate_text(
39
41
  prompt: "Hello!",
40
- model: "gpt-4o"
42
+ model: "gpt-5-mini"
41
43
  )
42
44
  # => Riffer::Messages::Assistant
43
45
 
44
46
  # Or with messages
45
47
  response = provider.generate_text(
46
48
  messages: [Riffer::Messages::User.new("Hello!")],
47
- model: "gpt-4o"
49
+ model: "gpt-5-mini"
48
50
  )
49
51
  ```
50
52
 
@@ -53,7 +55,7 @@ response = provider.generate_text(
53
55
  Streams a response as an Enumerator:
54
56
 
55
57
  ```ruby
56
- provider.stream_text(prompt: "Tell me a story", model: "gpt-4o").each do |event|
58
+ provider.stream_text(prompt: "Tell me a story", model: "gpt-5-mini").each do |event|
57
59
  case event
58
60
  when Riffer::StreamEvents::TextDelta
59
61
  print event.content
@@ -90,7 +92,7 @@ provider = Riffer::Providers::OpenAI.new
90
92
  # Simple prompt
91
93
  response = provider.generate_text(
92
94
  prompt: "What is Ruby?",
93
- model: "gpt-4o"
95
+ model: "gpt-5-mini"
94
96
  )
95
97
  puts response.content
96
98
 
@@ -98,7 +100,7 @@ puts response.content
98
100
  response = provider.generate_text(
99
101
  prompt: "Explain recursion",
100
102
  system: "You are a programming tutor. Use simple language.",
101
- model: "gpt-4o"
103
+ model: "gpt-5-mini"
102
104
  )
103
105
 
104
106
  # With message history
@@ -111,7 +113,7 @@ messages = [
111
113
 
112
114
  response = provider.generate_text(
113
115
  messages: messages,
114
- model: "gpt-4o"
116
+ model: "gpt-5-mini"
115
117
  )
116
118
  ```
117
119
 
@@ -132,7 +134,7 @@ end
132
134
 
133
135
  response = provider.generate_text(
134
136
  prompt: "What's the weather in Tokyo?",
135
- model: "gpt-4o",
137
+ model: "gpt-5-mini",
136
138
  tools: [WeatherTool]
137
139
  )
138
140
 
@@ -149,6 +151,9 @@ Riffer uses a registry to find providers by identifier:
149
151
  Riffer::Providers::Repository.find(:openai)
150
152
  # => Riffer::Providers::OpenAI
151
153
 
154
+ Riffer::Providers::Repository.find(:azure_openai)
155
+ # => Riffer::Providers::AzureOpenAI
156
+
152
157
  Riffer::Providers::Repository.find(:amazon_bedrock)
153
158
  # => Riffer::Providers::AmazonBedrock
154
159
 
@@ -164,5 +169,6 @@ Riffer::Providers::Repository.find(:mock)
164
169
  - [Amazon Bedrock](02_AMAZON_BEDROCK.md) - Claude and other models via AWS
165
170
  - [Anthropic](03_ANTHROPIC.md) - Claude models via Anthropic API
166
171
  - [OpenAI](04_OPENAI.md) - GPT models
167
- - [Mock](05_MOCK_PROVIDER.md) - Mock provider for testing
168
- - [Custom Providers](06_CUSTOM_PROVIDERS.md) - Creating your own provider
172
+ - [Azure OpenAI](05_AZURE_OPENAI.md) - GPT models via Azure
173
+ - [Mock](06_MOCK_PROVIDER.md) - Mock provider for testing
174
+ - [Custom Providers](07_CUSTOM_PROVIDERS.md) - Creating your own provider