riffer 0.23.0 → 0.24.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.
- checksums.yaml +4 -4
- data/.agents/architecture.md +2 -0
- data/.release-please-manifest.json +1 -1
- data/AGENTS.md +10 -9
- data/CHANGELOG.md +7 -0
- data/Steepfile +5 -2
- data/docs/08_MESSAGES.md +38 -8
- data/docs/10_CONFIGURATION.md +33 -0
- data/lib/riffer/agent.rb +22 -3
- data/lib/riffer/config.rb +26 -0
- data/lib/riffer/messages/assistant.rb +4 -3
- data/lib/riffer/messages/base.rb +21 -3
- data/lib/riffer/messages/converter.rb +6 -4
- data/lib/riffer/messages/tool.rb +4 -3
- data/lib/riffer/messages/user.rb +4 -3
- data/lib/riffer/version.rb +1 -1
- data/sig/generated/riffer/agent.rbs +4 -0
- data/sig/generated/riffer/config.rbs +19 -0
- data/sig/generated/riffer/messages/assistant.rbs +2 -2
- data/sig/generated/riffer/messages/base.rbs +10 -2
- data/sig/generated/riffer/messages/tool.rbs +2 -2
- data/sig/generated/riffer/messages/user.rbs +2 -2
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd2f811bba1934e2285de7f4ec122259039dcb61f6d6141e3c26992d38ba2e07
|
|
4
|
+
data.tar.gz: 877941ede9824184db9e7eb99ee901096995298937033f65c87562afeec7d208
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2477d9fcba22f212f7a83b6e7a400476aa7e4cc7c842d631d9044d7771827fcf903355f4015d9fa357e752ceb41a26fcfd62afd3711134c61a9745c3005dfe3c
|
|
7
|
+
data.tar.gz: d06abbd9da19f04cfc97449e72f283297ae07ab5dface13119c92cdf2121515c1de83ed6992daa1ab49ec460e15c5afaf4883b4224c9ed7c838f64b7f9ca816a
|
data/.agents/architecture.md
CHANGED
|
@@ -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
|
|
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
|
|
24
|
-
|
|
25
|
-
| `bundle exec rake`
|
|
26
|
-
| `bundle exec rake test`
|
|
27
|
-
| `bundle exec rake standard`
|
|
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`
|
|
31
|
-
| `bin/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,13 @@ 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.0](https://github.com/janeapp/riffer/compare/riffer/v0.23.0...riffer/v0.24.0) (2026-04-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add optional message id generation ([#210](https://github.com/janeapp/riffer/issues/210)) ([0f9e20d](https://github.com/janeapp/riffer/commit/0f9e20dcd2d69a49fe5da71be3f0b9deb0a046a4))
|
|
14
|
+
|
|
8
15
|
## [0.23.0](https://github.com/janeapp/riffer/compare/riffer/v0.22.0...riffer/v0.23.0) (2026-04-15)
|
|
9
16
|
|
|
10
17
|
|
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
|
|
256
|
-
|
|
257
|
-
| `System`
|
|
258
|
-
| `User`
|
|
259
|
-
| `Assistant`
|
|
260
|
-
| `Tool`
|
|
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
|
```
|
data/docs/10_CONFIGURATION.md
CHANGED
|
@@ -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
|
-
|
|
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?
|
data/lib/riffer/messages/base.rb
CHANGED
|
@@ -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
|
data/lib/riffer/messages/tool.rb
CHANGED
|
@@ -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
|
data/lib/riffer/messages/user.rb
CHANGED
|
@@ -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
|
data/lib/riffer/version.rb
CHANGED
|
@@ -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.
|
|
4
|
+
version: 0.24.0
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
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.
|