riffer 0.13.0 → 0.14.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/architecture.md +43 -8
  3. data/.agents/providers.md +166 -14
  4. data/.release-please-manifest.json +1 -1
  5. data/CHANGELOG.md +22 -0
  6. data/docs/01_OVERVIEW.md +22 -1
  7. data/docs/03_AGENTS.md +280 -5
  8. data/docs/05_MESSAGES.md +62 -0
  9. data/docs/06_STREAM_EVENTS.md +89 -0
  10. data/docs/07_CONFIGURATION.md +9 -7
  11. data/docs/08_EVALS.md +116 -156
  12. data/docs/09_GUARDRAILS.md +3 -22
  13. data/docs_providers/01_PROVIDERS.md +13 -13
  14. data/docs_providers/02_AMAZON_BEDROCK.md +15 -22
  15. data/docs_providers/03_ANTHROPIC.md +39 -0
  16. data/docs_providers/04_OPENAI.md +41 -0
  17. data/docs_providers/{05_TEST_PROVIDER.md → 05_MOCK_PROVIDER.md} +25 -8
  18. data/docs_providers/06_CUSTOM_PROVIDERS.md +159 -81
  19. data/lib/riffer/agent/response.rb +22 -2
  20. data/lib/riffer/agent.rb +297 -79
  21. data/lib/riffer/evals/evaluator.rb +48 -19
  22. data/lib/riffer/evals/evaluator_runner.rb +80 -0
  23. data/lib/riffer/evals/judge.rb +39 -13
  24. data/lib/riffer/evals/result.rb +2 -2
  25. data/lib/riffer/evals/run_result.rb +26 -70
  26. data/lib/riffer/evals/scenario_result.rb +61 -0
  27. data/lib/riffer/evals.rb +2 -2
  28. data/lib/riffer/file_part.rb +116 -0
  29. data/lib/riffer/messages/converter.rb +33 -1
  30. data/lib/riffer/messages/user.rb +21 -0
  31. data/lib/riffer/{tools/param.rb → param.rb} +2 -2
  32. data/lib/riffer/{tools/params.rb → params.rb} +7 -6
  33. data/lib/riffer/providers/amazon_bedrock.rb +183 -140
  34. data/lib/riffer/providers/anthropic.rb +226 -131
  35. data/lib/riffer/providers/base.rb +62 -20
  36. data/lib/riffer/providers/mock.rb +136 -0
  37. data/lib/riffer/providers/open_ai.rb +195 -111
  38. data/lib/riffer/providers/repository.rb +1 -1
  39. data/lib/riffer/providers.rb +1 -1
  40. data/lib/riffer/stream_events/interrupt.rb +25 -0
  41. data/lib/riffer/stream_events/web_search_done.rb +25 -0
  42. data/lib/riffer/stream_events/web_search_status.rb +32 -0
  43. data/lib/riffer/structured_output/result.rb +35 -0
  44. data/lib/riffer/structured_output.rb +39 -0
  45. data/lib/riffer/tool.rb +2 -2
  46. data/lib/riffer/tools.rb +0 -2
  47. data/lib/riffer/version.rb +1 -1
  48. data/sig/generated/riffer/agent/response.rbs +17 -2
  49. data/sig/generated/riffer/agent.rbs +90 -11
  50. data/sig/generated/riffer/evals/evaluator.rbs +27 -16
  51. data/sig/generated/riffer/evals/evaluator_runner.rbs +41 -0
  52. data/sig/generated/riffer/evals/judge.rbs +16 -6
  53. data/sig/generated/riffer/evals/result.rbs +2 -2
  54. data/sig/generated/riffer/evals/run_result.rbs +11 -43
  55. data/sig/generated/riffer/evals/scenario_result.rbs +42 -0
  56. data/sig/generated/riffer/evals.rbs +2 -2
  57. data/sig/generated/riffer/file_part.rbs +70 -0
  58. data/sig/generated/riffer/messages/converter.rbs +12 -0
  59. data/sig/generated/riffer/messages/user.rbs +14 -0
  60. data/sig/generated/riffer/{tools/param.rbs → param.rbs} +3 -3
  61. data/sig/generated/riffer/{tools/params.rbs → params.rbs} +6 -5
  62. data/sig/generated/riffer/providers/amazon_bedrock.rbs +39 -13
  63. data/sig/generated/riffer/providers/anthropic.rbs +50 -12
  64. data/sig/generated/riffer/providers/base.rbs +32 -11
  65. data/sig/generated/riffer/providers/{test.rbs → mock.rbs} +19 -10
  66. data/sig/generated/riffer/providers/open_ai.rbs +46 -20
  67. data/sig/generated/riffer/providers.rbs +1 -1
  68. data/sig/generated/riffer/stream_events/interrupt.rbs +17 -0
  69. data/sig/generated/riffer/stream_events/web_search_done.rbs +18 -0
  70. data/sig/generated/riffer/stream_events/web_search_status.rbs +21 -0
  71. data/sig/generated/riffer/structured_output/result.rbs +31 -0
  72. data/sig/generated/riffer/structured_output.rbs +25 -0
  73. data/sig/generated/riffer/tool.rbs +2 -2
  74. data/sig/generated/riffer/tools.rbs +0 -2
  75. metadata +28 -24
  76. data/lib/riffer/evals/evaluators/answer_relevancy.rb +0 -59
  77. data/lib/riffer/evals/evaluators.rb +0 -6
  78. data/lib/riffer/evals/metric.rb +0 -65
  79. data/lib/riffer/evals/profile.rb +0 -99
  80. data/lib/riffer/evals/runner.rb +0 -44
  81. data/lib/riffer/guardrails/max_length.rb +0 -67
  82. data/lib/riffer/providers/test.rb +0 -114
  83. data/sig/generated/riffer/evals/evaluators/answer_relevancy.rbs +0 -24
  84. data/sig/generated/riffer/evals/evaluators.rbs +0 -5
  85. data/sig/generated/riffer/evals/metric.rbs +0 -43
  86. data/sig/generated/riffer/evals/profile.rbs +0 -59
  87. data/sig/generated/riffer/evals/runner.rbs +0 -27
  88. data/sig/generated/riffer/guardrails/max_length.rbs +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7a4f27800f9f5a266d408a1b8b8d6d96f6c59307b4761d34d3a1365b9b71bb8
4
- data.tar.gz: 6719e5013f8d83c78f2647808dd0d27a024ec432085311af02b1dc7cfbdaacfb
3
+ metadata.gz: ae2818fb04f43e8b5bd3a4a2ecfc2302af15ac073985e08f3fd36c461bafdf8c
4
+ data.tar.gz: e5ec190ea0c927eb38604849798b44fc96a03ba80510376d1d0e2eb2f5138fd0
5
5
  SHA512:
6
- metadata.gz: 24356e56e024eaf5223c91c97b72bf4e90a9dd74721d12dac6b5d0f294bea305914d6f074467e915dfd64adbf3eea5a07484147a394f51613f7fe16e828e4c36
7
- data.tar.gz: 6c8b8d464d94b56f044196f81ec298b01824dea114aca266b82de020c82521ce975451c643c5307d532c23f6fb85e2d38f4931f931cf13dee5e54b53ba5e8211
6
+ metadata.gz: c841653977d2277537ae2930a874d32748f874a4995fa058fd2abdff798d70f99cecdc4e75058179194f7b78502e209c4c97d916b4f568cab5935b9f4d470e84
7
+ data.tar.gz: 383682e8b929bc8f164c9bce1ae0b6bbec9a6ea7a30a5282bde79becfe886b22e7177f45ac5421ec97aad7fdfd2adf0150d649d0791c14f931c59ab737b36940
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Agent (`lib/riffer/agent.rb`)
6
6
 
7
- Base class for AI agents. Subclass and use DSL methods `model` and `instructions` to configure. Orchestrates message flow, LLM calls, and tool execution via a generate/stream loop.
7
+ Base class for AI agents. Subclass and use DSL methods `model`, `instructions`, and `structured_output` to configure. Orchestrates message flow, LLM calls, tool execution, and structured output parsing via a generate/stream loop.
8
8
 
9
9
  ```ruby
10
10
  class EchoAgent < Riffer::Agent
@@ -18,10 +18,13 @@ puts agent.generate('Hello world')
18
18
 
19
19
  ### Providers (`lib/riffer/providers/`)
20
20
 
21
- Adapters for LLM APIs. Each provider extends `Riffer::Providers::Base` and implements:
21
+ Adapters for LLM APIs. The base class uses a template-method pattern — `generate_text` and `stream_text` orchestrate the flow, delegating to five hook methods each provider implements:
22
22
 
23
- - `perform_generate_text(messages, model:)` - returns `Riffer::Messages::Assistant`
24
- - `perform_stream_text(messages, model:)` - returns an `Enumerator` yielding stream events
23
+ - `build_request_params(messages, model, options)` convert messages, tools, and options into SDK params
24
+ - `execute_generate(params)` call the SDK and return the raw response
25
+ - `execute_stream(params, yielder)` — call the streaming SDK, mapping events to the yielder
26
+ - `extract_token_usage(response)` — pull token counts from the SDK response
27
+ - `extract_assistant_message(response, token_usage)` — parse the SDK response into an `Assistant` message
25
28
 
26
29
  Providers are registered in `Riffer::Providers::Repository::REPO` with identifiers (e.g., `openai`, `amazon_bedrock`).
27
30
 
@@ -30,11 +33,13 @@ Providers are registered in `Riffer::Providers::Repository::REPO` with identifie
30
33
  Typed message objects that extend `Riffer::Messages::Base`:
31
34
 
32
35
  - `System` - system instructions
33
- - `User` - user input
36
+ - `User` - user input (supports file attachments via `Riffer::FilePart`)
34
37
  - `Assistant` - AI responses
35
38
  - `Tool` - tool execution results
36
39
 
37
- The `Converter` module handles hash-to-object conversion.
40
+ `Riffer::FilePart` represents file attachments (images and documents) that can be included with User messages. Supports file paths, URLs, and raw base64 data.
41
+
42
+ The `Converter` module handles hash-to-object conversion, including file hash-to-`FilePart` conversion.
38
43
 
39
44
  ### StreamEvents (`lib/riffer/stream_events/`)
40
45
 
@@ -44,10 +49,27 @@ Structured events for streaming responses:
44
49
  - `TextDone` - completion signals
45
50
  - `ReasoningDelta` - reasoning process chunks
46
51
  - `ReasoningDone` - reasoning completion
52
+ - `WebSearchStatus` - web search status updates
53
+ - `WebSearchDone` - web search completion with query and sources
54
+ - `Interrupt` - callback interrupted the agent loop
55
+
56
+ ### Stopping the Loop Early
57
+
58
+ Two mechanisms can stop the agent loop before the LLM finishes naturally:
59
+
60
+ **Guardrail tripwires** — declarative policy enforcement registered at class level. A `:before` guardrail can block the request before the LLM is called; an `:after` guardrail can block the response. Tripwires are not resumable — the caller must change the input and start over. `Response#blocked?` returns `true`.
61
+
62
+ **Callback interrupts** — imperative flow control via `on_message` callbacks. Use `throw :riffer_interrupt` to pause the loop at any point. `Response#interrupted?` returns `true`. In streaming, yields an `Interrupt` event.
63
+
64
+ ### Resuming After an Interrupt
65
+
66
+ `agent.resume` or `agent.resume_stream` continues an interrupted loop. Both accept `messages:` for cross-process resume from persisted data.
67
+
68
+ 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.
47
69
 
48
70
  ## Key Patterns
49
71
 
50
- - Model strings use `provider/model` format (e.g., `openai/gpt-4`)
72
+ - Model config accepts a `provider/model` string (e.g., `openai/gpt-4`) or a Proc/lambda that returns one
51
73
  - Configuration via `Riffer.configure { |c| c.openai.api_key = "..." }`
52
74
  - Providers use `depends_on` helper for runtime dependency checking
53
75
  - Zeitwerk for autoloading - file structure must match module/class names
@@ -55,6 +77,9 @@ Structured events for streaming responses:
55
77
  ## Project Structure
56
78
 
57
79
  ```
80
+ examples/
81
+ evaluators/ # Reference evaluator implementations (copy-paste)
82
+ guardrails/ # Reference guardrail implementations (copy-paste)
58
83
  lib/
59
84
  riffer.rb # Main entry point, uses Zeitwerk for autoloading
60
85
  riffer/
@@ -64,11 +89,17 @@ lib/
64
89
  agent.rb # Agent class
65
90
  messages.rb # Messages namespace/module
66
91
  providers.rb # Providers namespace/module
92
+ param.rb # Single parameter definition (shared by tools and structured output)
93
+ params.rb # Parameter collection with DSL and validation
94
+ structured_output.rb # Structured output schema wrapper
67
95
  stream_events.rb # Stream events namespace/module
96
+ structured_output/
97
+ result.rb # Parse/validation result object
68
98
  helpers/
69
99
  class_name_converter.rb # Class name conversion utilities
70
100
  dependencies.rb # Dependency management
71
101
  validations.rb # Validation helpers
102
+ file_part.rb # File attachment (images and documents)
72
103
  messages/
73
104
  base.rb # Base message class
74
105
  assistant.rb # Assistant message
@@ -80,14 +111,18 @@ lib/
80
111
  base.rb # Base provider class
81
112
  open_ai.rb # OpenAI provider
82
113
  amazon_bedrock.rb # Amazon Bedrock provider
114
+ anthropic.rb # Anthropic provider
83
115
  repository.rb # Provider registry
84
- test.rb # Test provider
116
+ mock.rb # Mock provider
85
117
  stream_events/
86
118
  base.rb # Base stream event
119
+ interrupt.rb # Interrupt event
87
120
  text_delta.rb # Text delta event
88
121
  text_done.rb # Text done event
89
122
  reasoning_delta.rb # Reasoning delta event
90
123
  reasoning_done.rb # Reasoning done event
124
+ web_search_status.rb # Web search status event
125
+ web_search_done.rb # Web search done event
91
126
  test/
92
127
  test_helper.rb # Minitest configuration with VCR
93
128
  riffer_test.rb # Main module tests
data/.agents/providers.md CHANGED
@@ -3,33 +3,185 @@
3
3
  ## Steps
4
4
 
5
5
  1. Create `lib/riffer/providers/your_provider.rb` extending `Riffer::Providers::Base`
6
- 2. Implement required methods (see below)
6
+ 2. Implement the required hook methods (see below)
7
7
  3. Register in `Riffer::Providers::Repository::REPO`
8
8
  4. Add provider config to `Riffer::Config` if needed
9
9
  5. Create tests in `test/riffer/providers/your_provider_test.rb`
10
10
 
11
- ## Required Methods
11
+ ## Architecture
12
+
13
+ The base class uses the **template method** pattern. The public methods `generate_text` and `stream_text` orchestrate the flow, delegating to five hook methods that each provider implements:
14
+
15
+ ```
16
+ generate_text
17
+ ├─ build_request_params
18
+ ├─ execute_generate
19
+ ├─ extract_token_usage
20
+ └─ extract_assistant_message
21
+
22
+ stream_text
23
+ ├─ build_request_params
24
+ └─ execute_stream
25
+ ```
26
+
27
+ ## Required Hook Methods
12
28
 
13
29
  ```ruby
14
30
  # frozen_string_literal: true
31
+ # rbs_inline: enabled
32
+
33
+ class Riffer::Providers::YourProvider < Riffer::Providers::Base
34
+ def initialize(**options)
35
+ depends_on "your-sdk-gem"
36
+ @client = YourSDK::Client.new(**options)
37
+ end
38
+
39
+ private
40
+
41
+ # Convert messages, tools, and options into SDK-specific params.
42
+ # If supporting web search, extract `web_search` from options and convert
43
+ # it to a provider-native tool format (e.g., OpenAI's `web_search_preview`
44
+ # or Anthropic's `web_search_20250305` server tool).
45
+ #
46
+ #: (Array[Riffer::Messages::Base], String?, Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
47
+ def build_request_params(messages, model, options)
48
+ # Return a hash that can be passed to both execute_generate and execute_stream
49
+ end
15
50
 
16
- module Riffer
17
- module Providers
18
- class YourProvider < Base
19
- # Returns Riffer::Messages::Assistant
20
- def perform_generate_text(messages, model:)
21
- # Implementation
22
- end
23
-
24
- # Returns Enumerator yielding stream events
25
- def perform_stream_text(messages, model:)
26
- # Implementation
27
- end
51
+ # Call the SDK and return the raw response object.
52
+ #
53
+ #: (Hash[Symbol, untyped]) -> untyped
54
+ def execute_generate(params)
55
+ @client.create(**params)
56
+ end
57
+
58
+ # Call the streaming SDK, mapping provider events to Riffer stream events.
59
+ #
60
+ #: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
61
+ def execute_stream(params, yielder)
62
+ @client.stream(**params) do |event|
63
+ # Map SDK events to Riffer::StreamEvents::* and yield them
64
+ # yielder << Riffer::StreamEvents::TextDelta.new(event.text)
28
65
  end
29
66
  end
67
+
68
+ # Extract token usage from the SDK response.
69
+ #
70
+ #: (untyped) -> Riffer::TokenUsage?
71
+ def extract_token_usage(response)
72
+ usage = response.usage
73
+ return nil unless usage
74
+
75
+ Riffer::TokenUsage.new(
76
+ input_tokens: usage.input_tokens,
77
+ output_tokens: usage.output_tokens
78
+ )
79
+ end
80
+
81
+ # Parse the SDK response into an Assistant message.
82
+ #
83
+ #: (untyped, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
84
+ def extract_assistant_message(response, token_usage = nil)
85
+ # Extract text and tool_calls from the response
86
+ Riffer::Messages::Assistant.new(text, tool_calls: tool_calls, token_usage: token_usage)
87
+ end
88
+ end
89
+ ```
90
+
91
+ ## Structured Output
92
+
93
+ When structured output is configured, the agent passes a `Riffer::StructuredOutput` instance via `options[:structured_output]`. Providers must extract it from options (and exclude it from the splat) and convert it to their SDK-specific format.
94
+
95
+ ### Extracting from options
96
+
97
+ All providers follow the same pattern:
98
+
99
+ ```ruby
100
+ def build_request_params(messages, model, options)
101
+ structured_output = options[:structured_output]
102
+
103
+ params = {
104
+ # ...
105
+ **options.except(:tools, :structured_output)
106
+ }
107
+
108
+ # Convert to provider-specific format (see below)
109
+ end
110
+ ```
111
+
112
+ ### Provider-specific formats
113
+
114
+ **OpenAI** — uses `params[:text][:format]`:
115
+
116
+ ```ruby
117
+ if structured_output
118
+ params[:text] = {
119
+ format: {
120
+ type: "json_schema",
121
+ name: "response",
122
+ schema: structured_output.json_schema,
123
+ strict: true
124
+ }
125
+ }
30
126
  end
31
127
  ```
32
128
 
129
+ **Anthropic** — uses `params[:output_config]`:
130
+
131
+ ```ruby
132
+ if structured_output
133
+ params[:output_config] = {
134
+ format: {
135
+ type: "json_schema",
136
+ schema: structured_output.json_schema
137
+ }
138
+ }
139
+ end
140
+ ```
141
+
142
+ **Amazon Bedrock** — uses `params[:output_config][:text_format]` with stringified schema:
143
+
144
+ ```ruby
145
+ if structured_output
146
+ params[:output_config] = {
147
+ text_format: {
148
+ type: "json_schema",
149
+ structure: {
150
+ json_schema: {
151
+ schema: structured_output.json_schema.to_json,
152
+ name: "response"
153
+ }
154
+ }
155
+ }
156
+ }
157
+ end
158
+ ```
159
+
160
+ ### Key details
161
+
162
+ - `structured_output.json_schema` returns a Hash with `type`, `properties`, `required`, and `additionalProperties` keys
163
+ - Bedrock requires the schema as a JSON string (`.to_json`), others use the Hash directly
164
+ - The agent handles parsing and validation of the response — providers only need to pass the schema to the SDK
165
+
166
+ ## File Handling
167
+
168
+ User messages may include `Riffer::FilePart` objects in their `files` array. Each provider's `build_request_params` (or its message conversion helpers) must convert these to provider-specific content blocks:
169
+
170
+ - **OpenAI**: `input_image` (URLs or data URIs) and `input_file` (data URIs)
171
+ - **Anthropic**: `image` and `document` blocks with `url` or `base64` source
172
+ - **Bedrock**: `image` and `document` blocks with `bytes` source (always base64, URLs are resolved)
173
+
174
+ ## Shared Utilities
175
+
176
+ The base class provides `parse_tool_arguments` for converting tool call arguments from JSON strings to hashes:
177
+
178
+ ```ruby
179
+ # Available in all providers via the base class
180
+ parse_tool_arguments('{"key":"value"}') # => {"key" => "value"}
181
+ parse_tool_arguments({"key" => "value"}) # => {"key" => "value"}
182
+ parse_tool_arguments(nil) # => {}
183
+ ```
184
+
33
185
  ## Registration
34
186
 
35
187
  Add to `Riffer::Providers::Repository::REPO`:
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.13.0"
2
+ ".": "0.14.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ 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.14.0](https://github.com/janeapp/riffer/compare/riffer/v0.13.0...riffer/v0.14.0) (2026-02-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * add file attachment support for user messages ([#129](https://github.com/janeapp/riffer/issues/129)) ([0d08eb1](https://github.com/janeapp/riffer/commit/0d08eb140e4094a729b1da5e3fddb57b251bf6f3))
14
+ * add max_steps option to Agent ([#121](https://github.com/janeapp/riffer/issues/121)) ([fbc0391](https://github.com/janeapp/riffer/commit/fbc0391d157db587c0293255587a70141a7b588f))
15
+ * add structured output support for agents ([#128](https://github.com/janeapp/riffer/issues/128)) ([99be155](https://github.com/janeapp/riffer/commit/99be15567747427830fdb75b585715f013857f6f))
16
+ * add web_search option for OpenAI and Anthropic providers ([#126](https://github.com/janeapp/riffer/issues/126)) ([7e7e793](https://github.com/janeapp/riffer/commit/7e7e793a4ff746d7238074a21b9cd62a846e7c99))
17
+ * interruptible callbacks via throw/catch ([#119](https://github.com/janeapp/riffer/issues/119)) ([f5985e6](https://github.com/janeapp/riffer/commit/f5985e627737b28ebbf7ed7262e62496836acf1f))
18
+ * replace eval prompt API with semantic fields ([#132](https://github.com/janeapp/riffer/issues/132)) ([5d99d5a](https://github.com/janeapp/riffer/commit/5d99d5af408e4e70ae66c975d1b40d60a209f5a6))
19
+ * replace Profile/Metric eval system with EvaluatorRunner ([#138](https://github.com/janeapp/riffer/issues/138)) ([ebf2696](https://github.com/janeapp/riffer/commit/ebf2696dfce814be43278aa9857285c21b3894bf))
20
+ * support dynamic model selection via lambda ([#127](https://github.com/janeapp/riffer/issues/127)) ([c59cf96](https://github.com/janeapp/riffer/commit/c59cf96efb5a4f5d0bc947441fa3aeeea3b4e5f3))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * add --comment flag to claude code review prompt ([#122](https://github.com/janeapp/riffer/issues/122)) ([534bd59](https://github.com/janeapp/riffer/commit/534bd59c389233466824090b7bdfed976870d9f1))
26
+ * add RBS annotations to web search constants and fix test provider docs ([#130](https://github.com/janeapp/riffer/issues/130)) ([7cebe05](https://github.com/janeapp/riffer/commit/7cebe0506eafaa35abe2de74997af479a3776a7d))
27
+ * correct Amazon Bedrock model options to use Converse API structure ([#133](https://github.com/janeapp/riffer/issues/133)) ([3af4562](https://github.com/janeapp/riffer/commit/3af45620fc47d1b98961eb45d79a905a7d2cac82))
28
+ * widen run_eval input type to accept String or messages array ([#124](https://github.com/janeapp/riffer/issues/124)) ([f96214c](https://github.com/janeapp/riffer/commit/f96214c5b602c3469edca2ac9d0b856d1e23c630))
29
+
8
30
  ## [0.13.0](https://github.com/janeapp/riffer/compare/riffer/v0.12.0...riffer/v0.13.0) (2026-02-12)
9
31
 
10
32
 
data/docs/01_OVERVIEW.md CHANGED
@@ -37,13 +37,32 @@ end
37
37
 
38
38
  See [Tools](04_TOOLS.md) for details.
39
39
 
40
+ ### Structured Output
41
+
42
+ Agents can return structured JSON responses that conform to a schema. The response is automatically parsed and validated:
43
+
44
+ ```ruby
45
+ class SentimentAgent < Riffer::Agent
46
+ model 'openai/gpt-4o'
47
+ structured_output do
48
+ required :sentiment, String
49
+ required :score, Float
50
+ end
51
+ end
52
+
53
+ response = SentimentAgent.generate('Analyze: "I love this!"')
54
+ response.structured_output # => {sentiment: "positive", score: 0.95}
55
+ ```
56
+
57
+ See the [structured output section in Agents](03_AGENTS.md#structured_output) for details.
58
+
40
59
  ### Provider
41
60
 
42
61
  Providers are adapters that connect to LLM services. Riffer supports:
43
62
 
44
63
  - **OpenAI** - GPT models via the OpenAI API
45
64
  - **Amazon Bedrock** - Claude and other models via AWS Bedrock
46
- - **Test** - Mock provider for testing
65
+ - **Mock** - Mock provider for testing
47
66
 
48
67
  See [Providers](providers/01_PROVIDERS.md) for details.
49
68
 
@@ -66,6 +85,8 @@ When streaming responses, Riffer emits typed events:
66
85
  - `TextDone` - Complete text
67
86
  - `ToolCallDelta` - Incremental tool call arguments
68
87
  - `ToolCallDone` - Complete tool call
88
+ - `WebSearchStatus` - Web search progress updates
89
+ - `WebSearchDone` - Web search completion with sources
69
90
 
70
91
  See [Stream Events](06_STREAM_EVENTS.md) for details.
71
92