riffer 0.23.0 → 0.24.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c8de18bc9f5531012f3ccbf37098925f0f58a446b1ee528cc2bb60a13ef5989
4
- data.tar.gz: 32343e215229cb1a02f19ffe61d5d0f128654f2d4bf128b2a23e7ef67908b0aa
3
+ metadata.gz: c825d1e5ba230247483b2d111a711b4d9edfbe87b03b1acd0da2cdf88db99bd8
4
+ data.tar.gz: a56a7bc07700971525cf326b7f931ad1e44ac8c561f0ef300b8046420ab091fd
5
5
  SHA512:
6
- metadata.gz: 7f8f6bdbe623eabacc481f8264f9d43472c1d152d2383736087102db93e6d55047506d2fd6e7ec63e6647ad03aafb6f08861fcfed5ae6e44d54acd1a722041f1
7
- data.tar.gz: 2ca5412c0f1c6a4f86e53f7d53495dcf8073e85d6173a92e0dd58a40c111cd2ed1e7f658d92f1d1bbdcd194bbf8c5bc17d00fa890dfc9186ec86885baacf5991
6
+ metadata.gz: 60a98ad7eae9708363ec90ab9ad3f8c75d734a9389879870c0eac8fa0401d876ace8a5c53442d0607e276ac60acefe8bdf43aa1c1087225e8743010d378ff7db
7
+ data.tar.gz: 32f552a9848e8cdd0bba3f111ac045bfbb8cc2f32c4852aae1cd9c49c6e8e9d6809589bbb5c1b15497e8ba0f1891af72fba0df339c8257e168c1ad2c47a822db
@@ -112,6 +112,7 @@ On resume, `execute_pending_tool_calls` detects tool calls from the last assista
112
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
113
 
114
114
  Built-in runners:
115
+
115
116
  - `Sequential` — processes items in the current thread via `Array#map`
116
117
  - `Threaded` — processes items concurrently using a thread pool with configurable `max_concurrency`
117
118
 
@@ -125,6 +126,7 @@ runner.map(items, context: ctx) { |item| process(item) }
125
126
  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
 
127
128
  Built-in runtimes:
129
+
128
130
  - `Inline` — uses `Runner::Sequential` (default)
129
131
  - `Threaded` — uses `Runner::Threaded`
130
132
 
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.23.0"
2
+ ".": "0.24.1"
3
3
  }
data/AGENTS.md CHANGED
@@ -8,6 +8,7 @@ Ruby gem framework for building AI-powered agents with LLM provider adapters.
8
8
  - **Lint + Test**: `bundle exec rake`
9
9
  - **Autoloading**: Zeitwerk (file paths must match module/class names)
10
10
  - **Model format**: `provider/model` (e.g., `openai/gpt-4`)
11
+ - **Docs**: when adding a public config option or message attribute, update the matching page in `docs/` (e.g., `docs/10_CONFIGURATION.md`, `docs/08_MESSAGES.md`). RDoc ≠ user docs.
11
12
 
12
13
  ## Topic Guides
13
14
 
@@ -20,12 +21,12 @@ Ruby gem framework for building AI-powered agents with LLM provider adapters.
20
21
 
21
22
  ## Commands
22
23
 
23
- | Command | Description |
24
- |---------|-------------|
25
- | `bundle exec rake` | Run tests + lint (default) |
26
- | `bundle exec rake test` | Run tests only |
27
- | `bundle exec rake standard` | Check code style |
28
- | `bundle exec rake standard:fix` | Auto-fix style issues |
29
- | `bundle exec rake rbs:generate` | Generate RBS type signatures |
30
- | `bundle exec rake rbs:watch` | Watch and regenerate RBS files |
31
- | `bin/console` | Interactive console |
24
+ | Command | Description |
25
+ | ------------------------------- | ------------------------------ |
26
+ | `bundle exec rake` | Run tests + lint (default) |
27
+ | `bundle exec rake test` | Run tests only |
28
+ | `bundle exec rake standard` | Check code style |
29
+ | `bundle exec rake standard:fix` | Auto-fix style issues |
30
+ | `bundle exec rake rbs:generate` | Generate RBS type signatures |
31
+ | `bundle exec rake rbs:watch` | Watch and regenerate RBS files |
32
+ | `bin/console` | Interactive console |
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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.24.1](https://github.com/janeapp/riffer/compare/riffer/v0.24.0...riffer/v0.24.1) (2026-04-23)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * disable gzip on Anthropic stream to avoid SSE buffering ([#220](https://github.com/janeapp/riffer/issues/220)) ([ee2aaca](https://github.com/janeapp/riffer/commit/ee2aaca06eaefb12e5b6cbc3bde48ba8f3ea4ee8))
14
+
15
+ ## [0.24.0](https://github.com/janeapp/riffer/compare/riffer/v0.23.0...riffer/v0.24.0) (2026-04-20)
16
+
17
+
18
+ ### Features
19
+
20
+ * add optional message id generation ([#210](https://github.com/janeapp/riffer/issues/210)) ([0f9e20d](https://github.com/janeapp/riffer/commit/0f9e20dcd2d69a49fe5da71be3f0b9deb0a046a4))
21
+
8
22
  ## [0.23.0](https://github.com/janeapp/riffer/compare/riffer/v0.22.0...riffer/v0.23.0) (2026-04-15)
9
23
 
10
24
 
data/Steepfile CHANGED
@@ -5,10 +5,13 @@ target :lib do
5
5
 
6
6
  check "lib"
7
7
 
8
- library "logger"
9
8
  library "anthropic"
10
- library "openai"
11
9
  library "aws-sdk-bedrockruntime"
10
+ library "aws-sdk-core"
11
+ library "logger"
12
+ library "net-http"
13
+ library "openai"
14
+ library "uri"
12
15
 
13
16
  configure_code_diagnostics(D::Ruby.lenient)
14
17
  end
data/docs/08_MESSAGES.md CHANGED
@@ -252,12 +252,12 @@ Without this step, the same model can receive different input depending on the p
252
252
 
253
253
  ### Merge rules
254
254
 
255
- | Message type | Content | Auxiliary data | Merged? |
256
- |--------------|---------|----------------|---------|
257
- | `System` | Joined with `"\n\n"` | — | Yes |
258
- | `User` | Joined with `"\n\n"` | `files` arrays concatenated | Yes |
259
- | `Assistant` | Joined with `"\n\n"` | `tool_calls` arrays concatenated | Yes |
260
- | `Tool` | — | — | Never (each has a unique `tool_call_id`) |
255
+ | Message type | Content | Auxiliary data | Merged? |
256
+ | ------------ | -------------------- | -------------------------------- | ---------------------------------------- |
257
+ | `System` | Joined with `"\n\n"` | — | Yes |
258
+ | `User` | Joined with `"\n\n"` | `files` arrays concatenated | Yes |
259
+ | `Assistant` | Joined with `"\n\n"` | `tool_calls` arrays concatenated | Yes |
260
+ | `Tool` | — | — | Never (each has a unique `tool_call_id`) |
261
261
 
262
262
  ### Example
263
263
 
@@ -278,20 +278,50 @@ agent.generate(messages)
278
278
 
279
279
  Merging happens at serialization time only. The agent's `messages` array still contains the original separate messages for logging, evals, and debugging.
280
280
 
281
+ ## IDs
282
+
283
+ Every message carries an optional `id` attribute. By default ids are disabled (`message.id` returns `nil` and `:id` is omitted from `to_h`). Enable them globally by setting `Riffer.config.message_id_strategy`:
284
+
285
+ ```ruby
286
+ Riffer.configure { |c| c.message_id_strategy = :uuidv7 }
287
+
288
+ msg = Riffer::Messages::User.new("Hello")
289
+ msg.id # => "0195a2e1-..." (auto-generated UUIDv7)
290
+ msg.to_h # => {role: :user, content: "Hello", id: "0195a2e1-..."}
291
+ ```
292
+
293
+ Supported strategies: `:none` (default), `:uuid`, `:uuidv7`. See [Configuration — Message ID Strategy](10_CONFIGURATION.md#message-id-strategy) for the full reference.
294
+
295
+ Ids pass through to subclass constructors via an `id:` kwarg and are preserved when set explicitly:
296
+
297
+ ```ruby
298
+ msg = Riffer::Messages::Assistant.new("Done.", id: "reply-42")
299
+ msg.id # => "reply-42"
300
+ ```
301
+
302
+ When seeding an agent with existing conversation history and the strategy is enabled, every seeded message must include an id — Riffer raises `Riffer::ArgumentError` on missing ids rather than fabricating them.
303
+
281
304
  ## Base Class
282
305
 
283
306
  All messages inherit from `Riffer::Messages::Base`:
284
307
 
285
308
  ```ruby
286
309
  class Riffer::Messages::Base
287
- attr_reader :content
310
+ attr_reader :content, :id
311
+
312
+ def initialize(content, id: nil)
313
+ @content = content
314
+ @id = id || generate_id # uses Riffer.config.message_id_strategy
315
+ end
288
316
 
289
317
  def role
290
318
  raise NotImplementedError
291
319
  end
292
320
 
293
321
  def to_h
294
- {role: role, content: content}
322
+ hash = {role: role, content: content}
323
+ hash[:id] = id unless id.nil?
324
+ hash
295
325
  end
296
326
  end
297
327
  ```
@@ -54,6 +54,39 @@ end
54
54
 
55
55
  Per-agent configuration overrides this global default. See [Advanced Tool Configuration — Tool Runtime](07_TOOL_ADVANCED.md#tool-runtime-experimental) for details.
56
56
 
57
+ ### Message ID Strategy
58
+
59
+ Opt in to stable identifiers on every message for logging, persistence, or replay:
60
+
61
+ ```ruby
62
+ Riffer.configure do |config|
63
+ config.message_id_strategy = :uuidv7
64
+ end
65
+ ```
66
+
67
+ | Value | Description |
68
+ | ----------------- | -------------------------------------------------------------------------------- |
69
+ | `:none` (default) | No id is generated; `message.id` returns `nil` and `:id` is omitted from `to_h`. |
70
+ | `:uuid` | UUIDv4 via `SecureRandom.uuid`. |
71
+ | `:uuidv7` | Time-ordered UUIDv7 via `SecureRandom.uuid_v7` (Ruby 3.3+). |
72
+
73
+ When the strategy is not `:none`, every `Riffer::Messages::Base` instance — user prompts, system instructions, assistant responses, and tool results — gets an auto-generated `id` at construction time. IDs are included in `message.to_h` when present and omitted when `nil`. Provider API payloads are unaffected; the `id` stays on the Ruby side.
74
+
75
+ Seeded messages passed to `agent.generate([...])` must carry their own `:id` when the strategy is enabled — Riffer never fabricates identifiers for pre-existing history:
76
+
77
+ ```ruby
78
+ Riffer.configure { |c| c.message_id_strategy = :uuidv7 }
79
+
80
+ agent.generate([
81
+ {role: :user, content: "Hi", id: "msg-001"},
82
+ {role: :assistant, content: "Hello!", id: "msg-002"}
83
+ ])
84
+ ```
85
+
86
+ Missing ids raise `Riffer::ArgumentError` with the offending index.
87
+
88
+ See [Messages — IDs](08_MESSAGES.md#ids) for more details.
89
+
57
90
  ## Agent-Level Configuration
58
91
 
59
92
  Override global configuration at the agent level:
data/lib/riffer/agent.rb CHANGED
@@ -176,7 +176,7 @@ class Riffer::Agent
176
176
  #--
177
177
  #: (String) -> singleton(Riffer::Agent)?
178
178
  def self.find(identifier)
179
- subclasses.find { |agent_class| agent_class.identifier == identifier.to_s }
179
+ all.find { |agent_class| agent_class.identifier == identifier.to_s }
180
180
  end
181
181
 
182
182
  # Returns all agent subclasses.
@@ -184,7 +184,7 @@ class Riffer::Agent
184
184
  #--
185
185
  #: () -> Array[singleton(Riffer::Agent)]
186
186
  def self.all
187
- subclasses
187
+ subclasses #: Array[singleton(Riffer::Agent)]
188
188
  end
189
189
 
190
190
  # Generates a response using a new agent instance.
@@ -433,6 +433,7 @@ class Riffer::Agent
433
433
  if prompt_or_messages.is_a?(Array)
434
434
  raise Riffer::ArgumentError, "cannot pass an array of messages on an agent with existing messages; use a string to continue the conversation or a new agent instance to start fresh" if @messages.any?
435
435
  raise Riffer::ArgumentError, "cannot provide both files and messages; attach files to individual messages instead" if files && !files.empty?
436
+ validate_seed_ids!(prompt_or_messages)
436
437
  @messages = prompt_or_messages.map { |item| convert_to_message_object(item) }
437
438
  elsif @messages.any?
438
439
  file_parts = (files || []).map { |f| convert_to_file_part(f) }
@@ -448,6 +449,24 @@ class Riffer::Agent
448
449
  end
449
450
  end
450
451
 
452
+ #--
453
+ #: (Array[Hash[Symbol, untyped] | Riffer::Messages::Base]) -> void
454
+ def validate_seed_ids!(items)
455
+ strategy = Riffer.config.message_id_strategy
456
+ return if strategy == :none
457
+
458
+ items.each_with_index do |item, idx|
459
+ raw_id = case item
460
+ when Hash then item[:id]
461
+ when Riffer::Messages::Base then item.id
462
+ else next # type errors surface later via convert_to_message_object
463
+ end
464
+ next unless raw_id.nil?
465
+ raise Riffer::ArgumentError,
466
+ "seeded message at index #{idx} is missing :id (required when Riffer.config.message_id_strategy = #{strategy.inspect})"
467
+ end
468
+ end
469
+
451
470
  #--
452
471
  #: (?Hash[Symbol, untyped]?) -> Riffer::Messages::System?
453
472
  def build_instruction_message(context = @context)
@@ -783,7 +802,7 @@ class Riffer::Agent
783
802
  #--
784
803
  #: () -> Riffer::Messages::Assistant?
785
804
  def extract_final_response
786
- @messages.reverse.find { |msg| msg.is_a?(Riffer::Messages::Assistant) }
805
+ @messages.reverse.find { |msg| msg.is_a?(Riffer::Messages::Assistant) } #: Riffer::Messages::Assistant?
787
806
  end
788
807
 
789
808
  #--
data/lib/riffer/config.rb CHANGED
@@ -22,6 +22,8 @@ class Riffer::Config
22
22
  OpenAI = Struct.new(:api_key, keyword_init: true)
23
23
  Evals = Struct.new(:judge_model, keyword_init: true)
24
24
 
25
+ VALID_MESSAGE_ID_STRATEGIES = %i[none uuid uuidv7].freeze
26
+
25
27
  # Amazon Bedrock configuration (Struct with +api_token+ and +region+).
26
28
  attr_reader :amazon_bedrock #: Riffer::Config::AmazonBedrock
27
29
 
@@ -59,6 +61,29 @@ class Riffer::Config
59
61
  @tool_runtime = value
60
62
  end
61
63
 
64
+ # Strategy for auto-generating message ids. One of +:none+ (default, no id),
65
+ # +:uuid+ (UUIDv4), or +:uuidv7+ (time-ordered UUIDv7).
66
+ #
67
+ # When set to anything other than +:none+, each +Riffer::Messages::Base+
68
+ # instance gets an +id+ populated at construction time, and seeded messages
69
+ # passed to +Riffer::Agent#generate+ must carry their own +:id+.
70
+ attr_reader :message_id_strategy #: Symbol
71
+
72
+ # Sets the message id strategy.
73
+ #
74
+ # Raises +Riffer::ArgumentError+ if the value is not one of
75
+ # +:none+, +:uuid+, or +:uuidv7+.
76
+ #
77
+ #--
78
+ #: (Symbol) -> void
79
+ def message_id_strategy=(value)
80
+ unless VALID_MESSAGE_ID_STRATEGIES.include?(value)
81
+ raise Riffer::ArgumentError,
82
+ "message_id_strategy must be one of #{VALID_MESSAGE_ID_STRATEGIES.inspect}, got #{value.inspect}"
83
+ end
84
+ @message_id_strategy = value
85
+ end
86
+
62
87
  #--
63
88
  #: () -> void
64
89
  def initialize
@@ -69,5 +94,6 @@ class Riffer::Config
69
94
  @openai = OpenAI.new
70
95
  @evals = Evals.new
71
96
  @tool_runtime = Riffer::ToolRuntime::Inline.new
97
+ @message_id_strategy = :none
72
98
  end
73
99
  end
@@ -23,9 +23,9 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
23
23
  attr_reader :structured_output #: Hash[Symbol, untyped]?
24
24
 
25
25
  #--
26
- #: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
27
- def initialize(content, tool_calls: [], token_usage: nil, structured_output: nil)
28
- super(content)
26
+ #: (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
27
+ def initialize(content, id: nil, tool_calls: [], token_usage: nil, structured_output: nil)
28
+ super(content, id: id)
29
29
  @tool_calls = tool_calls
30
30
  @token_usage = token_usage
31
31
  @structured_output = structured_output
@@ -55,6 +55,7 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
55
55
  #: () -> Hash[Symbol, untyped]
56
56
  def to_h
57
57
  hash = {role: role, content: content}
58
+ hash[:id] = id unless id.nil?
58
59
  hash[:tool_calls] = tool_calls.map(&:to_h) unless tool_calls.empty?
59
60
  hash[:token_usage] = token_usage.to_h if token_usage
60
61
  hash[:structured_output] = structured_output if structured_output?
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
+ require "securerandom"
5
+
4
6
  # Base class for all message types in the Riffer framework.
5
7
  #
6
8
  # Subclasses must implement the +role+ method.
@@ -8,10 +10,14 @@ class Riffer::Messages::Base
8
10
  # The message content.
9
11
  attr_reader :content #: String
10
12
 
13
+ # The message id, or nil when +Riffer.config.message_id_strategy+ is +:none+.
14
+ attr_reader :id #: String?
15
+
11
16
  #--
12
- #: (String) -> void
13
- def initialize(content)
17
+ #: (String, ?id: String?) -> void
18
+ def initialize(content, id: nil)
14
19
  @content = content
20
+ @id = id || generate_id
15
21
  end
16
22
 
17
23
  # Converts the message to a hash.
@@ -19,7 +25,9 @@ class Riffer::Messages::Base
19
25
  #--
20
26
  #: () -> Hash[Symbol, untyped]
21
27
  def to_h
22
- {role: role, content: content}
28
+ hash = {role: role, content: content}
29
+ hash[:id] = id unless id.nil?
30
+ hash
23
31
  end
24
32
 
25
33
  # Returns the message role.
@@ -31,4 +39,14 @@ class Riffer::Messages::Base
31
39
  def role
32
40
  raise NotImplementedError, "Subclasses must implement #role"
33
41
  end
42
+
43
+ private
44
+
45
+ #: () -> String?
46
+ def generate_id
47
+ case Riffer.config.message_id_strategy
48
+ when :uuid then SecureRandom.uuid
49
+ when :uuidv7 then SecureRandom.uuid_v7
50
+ end
51
+ end
34
52
  end
@@ -65,22 +65,24 @@ module Riffer::Messages::Converter
65
65
  raise Riffer::ArgumentError, "Message hash must include a 'role' key"
66
66
  end
67
67
 
68
+ id = hash[:id]
69
+
68
70
  case role.to_sym
69
71
  when :user
70
72
  files = (hash[:files] || []).map { |f| convert_to_file_part(f) }
71
- Riffer::Messages::User.new(content, files: files)
73
+ Riffer::Messages::User.new(content, id: id, files: files)
72
74
  when :assistant
73
75
  tool_calls = (hash[:tool_calls] || []).map { |tc|
74
76
  tc.is_a?(Riffer::Messages::Assistant::ToolCall) ? tc : Riffer::Messages::Assistant::ToolCall.new(**tc)
75
77
  }
76
78
  structured_output = hash[:structured_output]
77
- Riffer::Messages::Assistant.new(content, tool_calls: tool_calls, structured_output: structured_output)
79
+ Riffer::Messages::Assistant.new(content, id: id, tool_calls: tool_calls, structured_output: structured_output)
78
80
  when :system
79
- Riffer::Messages::System.new(content)
81
+ Riffer::Messages::System.new(content, id: id)
80
82
  when :tool
81
83
  tool_call_id = hash[:tool_call_id]
82
84
  name = hash[:name]
83
- Riffer::Messages::Tool.new(content, tool_call_id: tool_call_id, name: name)
85
+ Riffer::Messages::Tool.new(content, id: id, tool_call_id: tool_call_id, name: name)
84
86
  else
85
87
  raise Riffer::ArgumentError, "Unknown message role: #{role}"
86
88
  end
@@ -26,9 +26,9 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
26
26
  attr_reader :error_type #: Symbol?
27
27
 
28
28
  #--
29
- #: (String, tool_call_id: String, name: String, ?error: String?, ?error_type: Symbol?) -> void
30
- def initialize(content, tool_call_id:, name:, error: nil, error_type: nil)
31
- super(content)
29
+ #: (String, tool_call_id: String, name: String, ?id: String?, ?error: String?, ?error_type: Symbol?) -> void
30
+ def initialize(content, tool_call_id:, name:, id: nil, error: nil, error_type: nil)
31
+ super(content, id: id)
32
32
  @tool_call_id = tool_call_id
33
33
  @name = name
34
34
  @error = error
@@ -55,6 +55,7 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
55
55
  #: () -> Hash[Symbol, untyped]
56
56
  def to_h
57
57
  hash = {role: role, content: content, tool_call_id: tool_call_id, name: name}
58
+ hash[:id] = id unless id.nil?
58
59
  if error?
59
60
  hash[:error] = error
60
61
  hash[:error_type] = error_type
@@ -17,9 +17,9 @@ class Riffer::Messages::User < Riffer::Messages::Base
17
17
  # Initializes a user message.
18
18
  #
19
19
  #--
20
- #: (String, ?files: Array[Riffer::FilePart]) -> void
21
- def initialize(content, files: [])
22
- super(content)
20
+ #: (String, ?id: String?, ?files: Array[Riffer::FilePart]) -> void
21
+ def initialize(content, id: nil, files: [])
22
+ super(content, id: id)
23
23
  @files = files
24
24
  end
25
25
 
@@ -39,6 +39,7 @@ class Riffer::Messages::User < Riffer::Messages::Base
39
39
  #: () -> Hash[Symbol, untyped]
40
40
  def to_h
41
41
  hash = {role: role, content: content}
42
+ hash[:id] = id unless id.nil?
42
43
  hash[:files] = files.map(&:to_h) unless files.empty?
43
44
  hash
44
45
  end
@@ -144,7 +144,12 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
144
144
  web_search_query: nil
145
145
  }
146
146
 
147
- stream = @client.messages.stream(**params)
147
+ # Workaround for anthropics/anthropic-sdk-ruby#182: force identity
148
+ # encoding so Net::HTTP/Zlib doesn't buffer SSE chunks until EOF.
149
+ stream = @client.messages.stream(
150
+ **params,
151
+ request_options: {extra_headers: {"accept-encoding" => "identity"}}
152
+ )
148
153
  current_state[:stream] = stream
149
154
 
150
155
  stream.each do |event|
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.23.0" #: String
5
+ VERSION = "0.24.1" #: String
6
6
  end
@@ -247,6 +247,10 @@ class Riffer::Agent
247
247
  # : ((String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base]), ?files: Array[Hash[Symbol, untyped] | Riffer::FilePart]?) -> void
248
248
  def initialize_messages: (String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base], ?files: Array[Hash[Symbol, untyped] | Riffer::FilePart]?) -> void
249
249
 
250
+ # --
251
+ # : (Array[Hash[Symbol, untyped] | Riffer::Messages::Base]) -> void
252
+ def validate_seed_ids!: (Array[Hash[Symbol, untyped] | Riffer::Messages::Base]) -> void
253
+
250
254
  # --
251
255
  # : (?Hash[Symbol, untyped]?) -> Riffer::Messages::System?
252
256
  def build_instruction_message: (?Hash[Symbol, untyped]?) -> Riffer::Messages::System?
@@ -63,6 +63,8 @@ class Riffer::Config
63
63
  | ({ ?judge_model: untyped }) -> instance
64
64
  end
65
65
 
66
+ VALID_MESSAGE_ID_STRATEGIES: untyped
67
+
66
68
  # Amazon Bedrock configuration (Struct with +api_token+ and +region+).
67
69
  attr_reader amazon_bedrock: Riffer::Config::AmazonBedrock
68
70
 
@@ -96,6 +98,23 @@ class Riffer::Config
96
98
  # : ((singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc)) -> void
97
99
  def tool_runtime=: (singleton(Riffer::ToolRuntime) | Riffer::ToolRuntime | Proc) -> void
98
100
 
101
+ # Strategy for auto-generating message ids. One of +:none+ (default, no id),
102
+ # +:uuid+ (UUIDv4), or +:uuidv7+ (time-ordered UUIDv7).
103
+ #
104
+ # When set to anything other than +:none+, each +Riffer::Messages::Base+
105
+ # instance gets an +id+ populated at construction time, and seeded messages
106
+ # passed to +Riffer::Agent#generate+ must carry their own +:id+.
107
+ attr_reader message_id_strategy: Symbol
108
+
109
+ # Sets the message id strategy.
110
+ #
111
+ # Raises +Riffer::ArgumentError+ if the value is not one of
112
+ # +:none+, +:uuid+, or +:uuidv7+.
113
+ #
114
+ # --
115
+ # : (Symbol) -> void
116
+ def message_id_strategy=: (Symbol) -> void
117
+
99
118
  # --
100
119
  # : () -> void
101
120
  def initialize: () -> void
@@ -30,8 +30,8 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
30
30
  attr_reader structured_output: Hash[Symbol, untyped]?
31
31
 
32
32
  # --
33
- # : (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
34
- def initialize: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
33
+ # : (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
34
+ def initialize: (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
35
35
 
36
36
  # --
37
37
  # : () -> Symbol
@@ -7,9 +7,12 @@ class Riffer::Messages::Base
7
7
  # The message content.
8
8
  attr_reader content: String
9
9
 
10
+ # The message id, or nil when +Riffer.config.message_id_strategy+ is +:none+.
11
+ attr_reader id: String?
12
+
10
13
  # --
11
- # : (String) -> void
12
- def initialize: (String) -> void
14
+ # : (String, ?id: String?) -> void
15
+ def initialize: (String, ?id: String?) -> void
13
16
 
14
17
  # Converts the message to a hash.
15
18
  #
@@ -24,4 +27,9 @@ class Riffer::Messages::Base
24
27
  # --
25
28
  # : () -> Symbol
26
29
  def role: () -> Symbol
30
+
31
+ private
32
+
33
+ # : () -> String?
34
+ def generate_id: () -> String?
27
35
  end
@@ -24,8 +24,8 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
24
24
  attr_reader error_type: Symbol?
25
25
 
26
26
  # --
27
- # : (String, tool_call_id: String, name: String, ?error: String?, ?error_type: Symbol?) -> void
28
- def initialize: (String, tool_call_id: String, name: String, ?error: String?, ?error_type: Symbol?) -> void
27
+ # : (String, tool_call_id: String, name: String, ?id: String?, ?error: String?, ?error_type: Symbol?) -> void
28
+ def initialize: (String, tool_call_id: String, name: String, ?id: String?, ?error: String?, ?error_type: Symbol?) -> void
29
29
 
30
30
  # Returns true if the tool execution resulted in an error.
31
31
  #
@@ -15,8 +15,8 @@ class Riffer::Messages::User < Riffer::Messages::Base
15
15
  # Initializes a user message.
16
16
  #
17
17
  # --
18
- # : (String, ?files: Array[Riffer::FilePart]) -> void
19
- def initialize: (String, ?files: Array[Riffer::FilePart]) -> void
18
+ # : (String, ?id: String?, ?files: Array[Riffer::FilePart]) -> void
19
+ def initialize: (String, ?id: String?, ?files: Array[Riffer::FilePart]) -> void
20
20
 
21
21
  # --
22
22
  # : () -> Symbol
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.0
4
+ version: 0.24.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - "~>"
37
37
  - !ruby/object:Gem::Version
38
- version: 1.32.0
38
+ version: 1.35.0
39
39
  type: :development
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
- version: 1.32.0
45
+ version: 1.35.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: aws-sdk-bedrockruntime
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -63,14 +63,14 @@ dependencies:
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: 0.58.0
66
+ version: 0.59.0
67
67
  type: :development
68
68
  prerelease: false
69
69
  version_requirements: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: 0.58.0
73
+ version: 0.59.0
74
74
  - !ruby/object:Gem::Dependency
75
75
  name: async
76
76
  requirement: !ruby/object:Gem::Requirement
@@ -195,14 +195,14 @@ dependencies:
195
195
  requirements:
196
196
  - - "~>"
197
197
  - !ruby/object:Gem::Version
198
- version: 1.10.0
198
+ version: '2.0'
199
199
  type: :development
200
200
  prerelease: false
201
201
  version_requirements: !ruby/object:Gem::Requirement
202
202
  requirements:
203
203
  - - "~>"
204
204
  - !ruby/object:Gem::Version
205
- version: 1.10.0
205
+ version: '2.0'
206
206
  description: Riffer is a comprehensive Ruby framework designed to simplify the development
207
207
  of AI-powered applications and agents. It provides a complete toolkit for integrating
208
208
  artificial intelligence capabilities into your Ruby projects.