riffer 0.14.0 → 0.15.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/providers.md +15 -8
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +7 -0
- data/docs/03_AGENTS.md +13 -0
- data/docs/05_MESSAGES.md +22 -0
- data/lib/riffer/agent.rb +15 -16
- data/lib/riffer/messages/assistant.rb +12 -2
- data/lib/riffer/messages/converter.rb +2 -1
- data/lib/riffer/providers/amazon_bedrock.rb +19 -15
- data/lib/riffer/providers/anthropic.rb +18 -13
- data/lib/riffer/providers/base.rb +29 -5
- data/lib/riffer/providers/mock.rb +8 -11
- data/lib/riffer/providers/open_ai.rb +17 -14
- data/lib/riffer/version.rb +1 -1
- data/sig/generated/riffer/agent.rbs +5 -5
- data/sig/generated/riffer/messages/assistant.rbs +8 -2
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +5 -2
- data/sig/generated/riffer/providers/anthropic.rbs +5 -2
- data/sig/generated/riffer/providers/base.rbs +10 -3
- data/sig/generated/riffer/providers/mock.rbs +5 -2
- data/sig/generated/riffer/providers/open_ai.rbs +5 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89f3c7419e5a1cde4cf2a7ade73a224dc30ac5ae900041938eb165a31e568e81
|
|
4
|
+
data.tar.gz: a07f353544bbdd3904092b34d2c4c03e1dadaad6374876f2ef3dc3767f3373d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85e7b09d7491c6d670ac76f7fa981410fec26a04c8d73dfe7c4ca4a4f6b9a722c58ebbecfd9527cf3df30784e3c0ffa90098e3e9545d0bafdf3efd6997b0133d
|
|
7
|
+
data.tar.gz: 5aaac2e175f09429587fc2e7d6878aaa97114ef5a9d8a038ca6647662f364ef1403d5b43a5731b4fbed263b1a08d7ba4a42a459d630b6bc9039ca09a40c0f513
|
data/.agents/providers.md
CHANGED
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
## Architecture
|
|
12
12
|
|
|
13
|
-
The base class uses the **template method** pattern. The public methods `generate_text` and `stream_text` orchestrate the flow, delegating to
|
|
13
|
+
The base class uses the **template method** pattern. The public methods `generate_text` and `stream_text` orchestrate the flow, delegating to hook methods that each provider implements:
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
generate_text
|
|
17
17
|
├─ build_request_params
|
|
18
18
|
├─ execute_generate
|
|
19
|
-
├─
|
|
20
|
-
|
|
19
|
+
├─ extract_content
|
|
20
|
+
├─ extract_tool_calls
|
|
21
|
+
└─ extract_token_usage
|
|
21
22
|
|
|
22
23
|
stream_text
|
|
23
24
|
├─ build_request_params
|
|
@@ -78,12 +79,18 @@ class Riffer::Providers::YourProvider < Riffer::Providers::Base
|
|
|
78
79
|
)
|
|
79
80
|
end
|
|
80
81
|
|
|
81
|
-
#
|
|
82
|
+
# Extract text content from the SDK response.
|
|
82
83
|
#
|
|
83
|
-
#: (untyped
|
|
84
|
-
def
|
|
85
|
-
#
|
|
86
|
-
|
|
84
|
+
#: (untyped) -> String
|
|
85
|
+
def extract_content(response)
|
|
86
|
+
# Return the text content from the response
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Extract tool calls from the SDK response.
|
|
90
|
+
#
|
|
91
|
+
#: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
92
|
+
def extract_tool_calls(response)
|
|
93
|
+
# Return an array of ToolCall structs
|
|
87
94
|
end
|
|
88
95
|
end
|
|
89
96
|
```
|
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.15.0](https://github.com/janeapp/riffer/compare/riffer/v0.14.0...riffer/v0.15.0) (2026-02-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* store structured output in assistant messages ([#140](https://github.com/janeapp/riffer/issues/140)) ([9cdb2e2](https://github.com/janeapp/riffer/commit/9cdb2e2adad5eab6745212e573534d3369528ccb))
|
|
14
|
+
|
|
8
15
|
## [0.14.0](https://github.com/janeapp/riffer/compare/riffer/v0.13.0...riffer/v0.14.0) (2026-02-24)
|
|
9
16
|
|
|
10
17
|
|
data/docs/03_AGENTS.md
CHANGED
|
@@ -425,6 +425,19 @@ response.structured_output # => {sentiment: "positive", score: 0.95}
|
|
|
425
425
|
|
|
426
426
|
Returns `nil` when structured output is not configured or when validation fails.
|
|
427
427
|
|
|
428
|
+
The assistant message in the message history stores the parsed hash, so you can access structured output directly from persisted messages:
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
agent = SentimentAgent.new
|
|
432
|
+
agent.generate('Analyze: "I love this!"')
|
|
433
|
+
|
|
434
|
+
msg = agent.messages.last
|
|
435
|
+
msg.structured_output? # => true
|
|
436
|
+
msg.structured_output # => {sentiment: "positive", score: 0.95}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
See [Messages — Structured Output on Messages](05_MESSAGES.md#structured-output-on-messages) for details.
|
|
440
|
+
|
|
428
441
|
## Class Methods
|
|
429
442
|
|
|
430
443
|
### find
|
data/docs/05_MESSAGES.md
CHANGED
|
@@ -65,6 +65,28 @@ if msg.token_usage
|
|
|
65
65
|
end
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
#### Structured Output on Messages
|
|
69
|
+
|
|
70
|
+
When an agent has `structured_output` configured, the final assistant message stores the parsed hash directly. The `structured_output?` predicate checks for a non-nil value:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
msg = Riffer::Messages::Assistant.new('{"sentiment":"positive"}', structured_output: {sentiment: "positive"})
|
|
74
|
+
msg.structured_output? # => true
|
|
75
|
+
msg.structured_output # => {sentiment: "positive"}
|
|
76
|
+
|
|
77
|
+
# When not provided, structured_output returns nil
|
|
78
|
+
msg = Riffer::Messages::Assistant.new('{"sentiment":"positive"}')
|
|
79
|
+
msg.structured_output? # => false
|
|
80
|
+
msg.structured_output # => nil
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `to_h` representation includes `structured_output` only when present:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
msg = Riffer::Messages::Assistant.new('{"sentiment":"positive"}', structured_output: {sentiment: "positive"})
|
|
87
|
+
msg.to_h # => {role: :assistant, content: '{"sentiment":"positive"}', structured_output: {sentiment: "positive"}}
|
|
88
|
+
```
|
|
89
|
+
|
|
68
90
|
### Tool
|
|
69
91
|
|
|
70
92
|
Tool messages contain the results of tool executions:
|
data/lib/riffer/agent.rb
CHANGED
|
@@ -320,17 +320,17 @@ class Riffer::Agent
|
|
|
320
320
|
execute_tool_calls(processed_response)
|
|
321
321
|
end
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return build_response(content, modifications: all_modifications, structured_output:
|
|
323
|
+
response = extract_final_response
|
|
324
|
+
|
|
325
|
+
return build_response(response.content, modifications: all_modifications, structured_output: validate_structured_output(response))
|
|
326
326
|
end
|
|
327
327
|
|
|
328
328
|
# catch returns the thrown value when throw :riffer_interrupt fires;
|
|
329
329
|
# the return above exits on the successful (non-interrupted) path.
|
|
330
330
|
@interrupted = true
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
build_response(content, modifications: all_modifications, interrupted: true, interrupt_reason: reason, structured_output:
|
|
331
|
+
response = extract_final_response
|
|
332
|
+
|
|
333
|
+
build_response(response.content, modifications: all_modifications, interrupted: true, interrupt_reason: reason, structured_output: validate_structured_output(response))
|
|
334
334
|
end
|
|
335
335
|
|
|
336
336
|
#: (Riffer::Messages::Base) -> void
|
|
@@ -614,10 +614,9 @@ class Riffer::Agent
|
|
|
614
614
|
args.transform_keys(&:to_sym)
|
|
615
615
|
end
|
|
616
616
|
|
|
617
|
-
#: () ->
|
|
617
|
+
#: () -> Riffer::Messages::Assistant?
|
|
618
618
|
def extract_final_response
|
|
619
|
-
|
|
620
|
-
last_assistant_message&.content || ""
|
|
619
|
+
@messages.reverse.find { |msg| msg.is_a?(Riffer::Messages::Assistant) }
|
|
621
620
|
end
|
|
622
621
|
|
|
623
622
|
#: () -> [Riffer::Guardrails::Tripwire?, Array[Riffer::Guardrails::Modification]]
|
|
@@ -645,6 +644,13 @@ class Riffer::Agent
|
|
|
645
644
|
[processed_response, tripwire, modifications]
|
|
646
645
|
end
|
|
647
646
|
|
|
647
|
+
#: (Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
|
|
648
|
+
def validate_structured_output(response)
|
|
649
|
+
return unless response&.structured_output? && @structured_output
|
|
650
|
+
|
|
651
|
+
@structured_output.parse_and_validate(response.content).object
|
|
652
|
+
end
|
|
653
|
+
|
|
648
654
|
#: () -> Riffer::StructuredOutput?
|
|
649
655
|
def resolve_structured_output
|
|
650
656
|
params = self.class.structured_output
|
|
@@ -658,13 +664,6 @@ class Riffer::Agent
|
|
|
658
664
|
opts
|
|
659
665
|
end
|
|
660
666
|
|
|
661
|
-
#: (String) -> Hash[Symbol, untyped]?
|
|
662
|
-
def parse_structured_content(content)
|
|
663
|
-
return nil unless @structured_output
|
|
664
|
-
result = @structured_output.parse_and_validate(content)
|
|
665
|
-
result.object
|
|
666
|
-
end
|
|
667
|
-
|
|
668
667
|
#: (String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
|
|
669
668
|
def build_response(content, tripwire: nil, modifications: [], interrupted: false, interrupt_reason: nil, structured_output: nil)
|
|
670
669
|
Riffer::Agent::Response.new(content, tripwire: tripwire, modifications: modifications, interrupted: interrupted, interrupt_reason: interrupt_reason, structured_output: structured_output)
|
|
@@ -19,11 +19,15 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
19
19
|
# Token usage data for this response.
|
|
20
20
|
attr_reader :token_usage #: Riffer::TokenUsage?
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
# Parsed structured output hash, or nil when not applicable.
|
|
23
|
+
attr_reader :structured_output #: Hash[Symbol, untyped]?
|
|
24
|
+
|
|
25
|
+
#: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
|
|
26
|
+
def initialize(content, tool_calls: [], token_usage: nil, structured_output: nil)
|
|
24
27
|
super(content)
|
|
25
28
|
@tool_calls = tool_calls
|
|
26
29
|
@token_usage = token_usage
|
|
30
|
+
@structured_output = structured_output
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
#: () -> Symbol
|
|
@@ -31,6 +35,11 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
31
35
|
:assistant
|
|
32
36
|
end
|
|
33
37
|
|
|
38
|
+
# @rbs () -> bool
|
|
39
|
+
def structured_output?
|
|
40
|
+
!@structured_output.nil?
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
# Converts the message to a hash.
|
|
35
44
|
#
|
|
36
45
|
#: () -> Hash[Symbol, untyped]
|
|
@@ -38,6 +47,7 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
38
47
|
hash = {role: role, content: content}
|
|
39
48
|
hash[:tool_calls] = tool_calls.map(&:to_h) unless tool_calls.empty?
|
|
40
49
|
hash[:token_usage] = token_usage.to_h if token_usage
|
|
50
|
+
hash[:structured_output] = structured_output if structured_output?
|
|
41
51
|
hash
|
|
42
52
|
end
|
|
43
53
|
end
|
|
@@ -68,7 +68,8 @@ module Riffer::Messages::Converter
|
|
|
68
68
|
Riffer::Messages::User.new(content, files: files)
|
|
69
69
|
when :assistant
|
|
70
70
|
tool_calls = hash[:tool_calls] || hash["tool_calls"] || []
|
|
71
|
-
|
|
71
|
+
structured_output = hash[:structured_output] || hash["structured_output"]
|
|
72
|
+
Riffer::Messages::Assistant.new(content, tool_calls: tool_calls, structured_output: structured_output)
|
|
72
73
|
when :system
|
|
73
74
|
Riffer::Messages::System.new(content)
|
|
74
75
|
when :tool
|
|
@@ -84,21 +84,29 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
84
84
|
)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
#: (Aws::BedrockRuntime::Types::ConverseResponse
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
content_blocks = output.message.content
|
|
93
|
-
raise Riffer::Error, "No content returned from Bedrock API" if content_blocks.nil? || content_blocks.empty?
|
|
87
|
+
#: (Aws::BedrockRuntime::Types::ConverseResponse) -> String
|
|
88
|
+
def extract_content(response)
|
|
89
|
+
content_blocks = response.output&.message&.content
|
|
90
|
+
return "" if content_blocks.nil? || content_blocks.empty?
|
|
94
91
|
|
|
95
92
|
text_content = ""
|
|
93
|
+
|
|
94
|
+
content_blocks.each do |block|
|
|
95
|
+
text_content = block.text if block.respond_to?(:text) && block.text
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
text_content
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#: (Aws::BedrockRuntime::Types::ConverseResponse) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
102
|
+
def extract_tool_calls(response)
|
|
103
|
+
content_blocks = response.output&.message&.content
|
|
104
|
+
return [] if content_blocks.nil? || content_blocks.empty?
|
|
105
|
+
|
|
96
106
|
tool_calls = []
|
|
97
107
|
|
|
98
108
|
content_blocks.each do |block|
|
|
99
|
-
if block.respond_to?(:
|
|
100
|
-
text_content = block.text
|
|
101
|
-
elsif block.respond_to?(:tool_use) && block.tool_use
|
|
109
|
+
if block.respond_to?(:tool_use) && block.tool_use
|
|
102
110
|
tool_calls << Riffer::Messages::Assistant::ToolCall.new(
|
|
103
111
|
id: block.tool_use.tool_use_id,
|
|
104
112
|
call_id: block.tool_use.tool_use_id,
|
|
@@ -108,11 +116,7 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
108
116
|
end
|
|
109
117
|
end
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
raise Riffer::Error, "No content returned from Bedrock API"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
Riffer::Messages::Assistant.new(text_content, tool_calls: tool_calls, token_usage: token_usage)
|
|
119
|
+
tool_calls
|
|
116
120
|
end
|
|
117
121
|
|
|
118
122
|
#: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -81,20 +81,29 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
81
81
|
)
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
#: (Anthropic::Models::Message
|
|
85
|
-
def
|
|
84
|
+
#: (Anthropic::Models::Message) -> String
|
|
85
|
+
def extract_content(response)
|
|
86
86
|
content_blocks = response.content
|
|
87
|
-
|
|
87
|
+
return "" if content_blocks.nil? || content_blocks.empty?
|
|
88
88
|
|
|
89
89
|
text_content = ""
|
|
90
|
+
|
|
91
|
+
content_blocks.each do |block|
|
|
92
|
+
text_content = block.text if block.type.to_s == "text"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
text_content
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#: (Anthropic::Models::Message) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
99
|
+
def extract_tool_calls(response)
|
|
100
|
+
content_blocks = response.content
|
|
101
|
+
return [] if content_blocks.nil? || content_blocks.empty?
|
|
102
|
+
|
|
90
103
|
tool_calls = []
|
|
91
104
|
|
|
92
105
|
content_blocks.each do |block|
|
|
93
|
-
|
|
94
|
-
case block_type
|
|
95
|
-
when "text"
|
|
96
|
-
text_content = block.text
|
|
97
|
-
when "tool_use"
|
|
106
|
+
if block.type.to_s == "tool_use"
|
|
98
107
|
tool_calls << Riffer::Messages::Assistant::ToolCall.new(
|
|
99
108
|
id: block.id,
|
|
100
109
|
call_id: block.id,
|
|
@@ -104,11 +113,7 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
104
113
|
end
|
|
105
114
|
end
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
raise Riffer::Error, "No content returned from Anthropic API"
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
Riffer::Messages::Assistant.new(text_content, tool_calls: tool_calls, token_usage: token_usage)
|
|
116
|
+
tool_calls
|
|
112
117
|
end
|
|
113
118
|
|
|
114
119
|
#: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -14,7 +14,8 @@ require "json"
|
|
|
14
14
|
# - +execute_generate+ — call the SDK and return the raw response
|
|
15
15
|
# - +execute_stream+ — call the streaming SDK, mapping events to the yielder
|
|
16
16
|
# - +extract_token_usage+ — pull token counts from the SDK response
|
|
17
|
-
# - +
|
|
17
|
+
# - +extract_content+ — extract text content from the SDK response
|
|
18
|
+
# - +extract_tool_calls+ — extract tool calls from the SDK response
|
|
18
19
|
class Riffer::Providers::Base
|
|
19
20
|
include Riffer::Helpers::Dependencies
|
|
20
21
|
include Riffer::Messages::Converter
|
|
@@ -28,7 +29,18 @@ class Riffer::Providers::Base
|
|
|
28
29
|
validate_normalized_messages!(messages)
|
|
29
30
|
params = build_request_params(messages, model, options)
|
|
30
31
|
response = execute_generate(params)
|
|
31
|
-
|
|
32
|
+
|
|
33
|
+
content = extract_content(response)
|
|
34
|
+
tool_calls = extract_tool_calls(response)
|
|
35
|
+
token_usage = extract_token_usage(response)
|
|
36
|
+
structured_output = parse_structured_output(content) if options[:structured_output] && tool_calls.empty?
|
|
37
|
+
|
|
38
|
+
Riffer::Messages::Assistant.new(
|
|
39
|
+
content,
|
|
40
|
+
tool_calls: tool_calls,
|
|
41
|
+
token_usage: token_usage,
|
|
42
|
+
structured_output: structured_output
|
|
43
|
+
)
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
# Streams text from the provider.
|
|
@@ -66,9 +78,21 @@ class Riffer::Providers::Base
|
|
|
66
78
|
raise NotImplementedError, "Subclasses must implement #extract_token_usage"
|
|
67
79
|
end
|
|
68
80
|
|
|
69
|
-
#: (untyped
|
|
70
|
-
def
|
|
71
|
-
raise NotImplementedError, "Subclasses must implement #
|
|
81
|
+
#: (untyped) -> String
|
|
82
|
+
def extract_content(response)
|
|
83
|
+
raise NotImplementedError, "Subclasses must implement #extract_content"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
#: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
87
|
+
def extract_tool_calls(response)
|
|
88
|
+
raise NotImplementedError, "Subclasses must implement #extract_tool_calls"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#: (String) -> Hash[Symbol, untyped]?
|
|
92
|
+
def parse_structured_output(content)
|
|
93
|
+
JSON.parse(content, symbolize_names: true)
|
|
94
|
+
rescue JSON::ParserError
|
|
95
|
+
nil
|
|
72
96
|
end
|
|
73
97
|
|
|
74
98
|
#: ((String | Hash[String, untyped])?) -> Hash[String, untyped]
|
|
@@ -67,17 +67,14 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
67
67
|
response[:token_usage]
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
#: (untyped
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
else
|
|
79
|
-
response
|
|
80
|
-
end
|
|
70
|
+
#: (untyped) -> String
|
|
71
|
+
def extract_content(response)
|
|
72
|
+
response.is_a?(Hash) ? (response[:content] || "") : response.content
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
76
|
+
def extract_tool_calls(response)
|
|
77
|
+
response.is_a?(Hash) ? (response[:tool_calls] || []) : response.tool_calls
|
|
81
78
|
end
|
|
82
79
|
|
|
83
80
|
#: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -77,19 +77,26 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
|
|
|
77
77
|
)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
#: (OpenAI::Models::Responses::Response
|
|
81
|
-
def
|
|
82
|
-
output_items = response.output
|
|
83
|
-
|
|
80
|
+
#: (OpenAI::Models::Responses::Response) -> String
|
|
81
|
+
def extract_content(response)
|
|
84
82
|
text_content = ""
|
|
85
|
-
tool_calls = []
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
when :message
|
|
84
|
+
response.output.each do |item|
|
|
85
|
+
if item.type == :message
|
|
90
86
|
text_block = item.content&.find { |c| c.type == :output_text }
|
|
91
87
|
text_content = text_block&.text || "" if text_block
|
|
92
|
-
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
text_content
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#: (OpenAI::Models::Responses::Response) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
95
|
+
def extract_tool_calls(response)
|
|
96
|
+
tool_calls = []
|
|
97
|
+
|
|
98
|
+
response.output.each do |item|
|
|
99
|
+
if item.type == :function_call
|
|
93
100
|
tool_calls << Riffer::Messages::Assistant::ToolCall.new(
|
|
94
101
|
id: item.id,
|
|
95
102
|
call_id: item.call_id,
|
|
@@ -99,11 +106,7 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
|
|
|
99
106
|
end
|
|
100
107
|
end
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
raise Riffer::Error, "No output returned from OpenAI API"
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
Riffer::Messages::Assistant.new(text_content, tool_calls: tool_calls, token_usage: token_usage)
|
|
109
|
+
tool_calls
|
|
107
110
|
end
|
|
108
111
|
|
|
109
112
|
#: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
data/lib/riffer/version.rb
CHANGED
|
@@ -231,8 +231,8 @@ class Riffer::Agent
|
|
|
231
231
|
# : ((String | Hash[String, untyped])?) -> Hash[Symbol, untyped]
|
|
232
232
|
def parse_tool_arguments: ((String | Hash[String, untyped])?) -> Hash[Symbol, untyped]
|
|
233
233
|
|
|
234
|
-
# : () ->
|
|
235
|
-
def extract_final_response: () ->
|
|
234
|
+
# : () -> Riffer::Messages::Assistant?
|
|
235
|
+
def extract_final_response: () -> Riffer::Messages::Assistant?
|
|
236
236
|
|
|
237
237
|
# : () -> [Riffer::Guardrails::Tripwire?, Array[Riffer::Guardrails::Modification]]
|
|
238
238
|
def run_before_guardrails: () -> [ Riffer::Guardrails::Tripwire?, Array[Riffer::Guardrails::Modification] ]
|
|
@@ -240,15 +240,15 @@ class Riffer::Agent
|
|
|
240
240
|
# : (Riffer::Messages::Assistant) -> [untyped, Riffer::Guardrails::Tripwire?, Array[Riffer::Guardrails::Modification]]
|
|
241
241
|
def run_after_guardrails: (Riffer::Messages::Assistant) -> [ untyped, Riffer::Guardrails::Tripwire?, Array[Riffer::Guardrails::Modification] ]
|
|
242
242
|
|
|
243
|
+
# : (Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
|
|
244
|
+
def validate_structured_output: (Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
|
|
245
|
+
|
|
243
246
|
# : () -> Riffer::StructuredOutput?
|
|
244
247
|
def resolve_structured_output: () -> Riffer::StructuredOutput?
|
|
245
248
|
|
|
246
249
|
# : () -> Hash[Symbol, untyped]
|
|
247
250
|
def merged_model_options: () -> Hash[Symbol, untyped]
|
|
248
251
|
|
|
249
|
-
# : (String) -> Hash[Symbol, untyped]?
|
|
250
|
-
def parse_structured_content: (String) -> Hash[Symbol, untyped]?
|
|
251
|
-
|
|
252
252
|
# : (String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
|
|
253
253
|
def build_response: (String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?) -> Riffer::Agent::Response
|
|
254
254
|
end
|
|
@@ -28,12 +28,18 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
28
28
|
# Token usage data for this response.
|
|
29
29
|
attr_reader token_usage: Riffer::TokenUsage?
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
|
|
31
|
+
# Parsed structured output hash, or nil when not applicable.
|
|
32
|
+
attr_reader structured_output: Hash[Symbol, untyped]?
|
|
33
|
+
|
|
34
|
+
# : (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
|
|
35
|
+
def initialize: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?) -> void
|
|
33
36
|
|
|
34
37
|
# : () -> Symbol
|
|
35
38
|
def role: () -> Symbol
|
|
36
39
|
|
|
40
|
+
# @rbs () -> bool
|
|
41
|
+
def structured_output?: () -> bool
|
|
42
|
+
|
|
37
43
|
# Converts the message to a hash.
|
|
38
44
|
#
|
|
39
45
|
# : () -> Hash[Symbol, untyped]
|
|
@@ -22,8 +22,11 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
22
22
|
# : (Aws::BedrockRuntime::Types::ConverseResponse) -> Riffer::TokenUsage?
|
|
23
23
|
def extract_token_usage: (Aws::BedrockRuntime::Types::ConverseResponse) -> Riffer::TokenUsage?
|
|
24
24
|
|
|
25
|
-
# : (Aws::BedrockRuntime::Types::ConverseResponse
|
|
26
|
-
def
|
|
25
|
+
# : (Aws::BedrockRuntime::Types::ConverseResponse) -> String
|
|
26
|
+
def extract_content: (Aws::BedrockRuntime::Types::ConverseResponse) -> String
|
|
27
|
+
|
|
28
|
+
# : (Aws::BedrockRuntime::Types::ConverseResponse) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
29
|
+
def extract_tool_calls: (Aws::BedrockRuntime::Types::ConverseResponse) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
27
30
|
|
|
28
31
|
# : (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
29
32
|
def execute_stream: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -24,8 +24,11 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
24
24
|
# : (Anthropic::Models::Message) -> Riffer::TokenUsage?
|
|
25
25
|
def extract_token_usage: (Anthropic::Models::Message) -> Riffer::TokenUsage?
|
|
26
26
|
|
|
27
|
-
# : (Anthropic::Models::Message
|
|
28
|
-
def
|
|
27
|
+
# : (Anthropic::Models::Message) -> String
|
|
28
|
+
def extract_content: (Anthropic::Models::Message) -> String
|
|
29
|
+
|
|
30
|
+
# : (Anthropic::Models::Message) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
31
|
+
def extract_tool_calls: (Anthropic::Models::Message) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
29
32
|
|
|
30
33
|
# : (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
31
34
|
def execute_stream: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
# - +execute_generate+ — call the SDK and return the raw response
|
|
12
12
|
# - +execute_stream+ — call the streaming SDK, mapping events to the yielder
|
|
13
13
|
# - +extract_token_usage+ — pull token counts from the SDK response
|
|
14
|
-
# - +
|
|
14
|
+
# - +extract_content+ — extract text content from the SDK response
|
|
15
|
+
# - +extract_tool_calls+ — extract tool calls from the SDK response
|
|
15
16
|
class Riffer::Providers::Base
|
|
16
17
|
include Riffer::Helpers::Dependencies
|
|
17
18
|
|
|
@@ -41,8 +42,14 @@ class Riffer::Providers::Base
|
|
|
41
42
|
# : (untyped) -> Riffer::TokenUsage?
|
|
42
43
|
def extract_token_usage: (untyped) -> Riffer::TokenUsage?
|
|
43
44
|
|
|
44
|
-
# : (untyped
|
|
45
|
-
def
|
|
45
|
+
# : (untyped) -> String
|
|
46
|
+
def extract_content: (untyped) -> String
|
|
47
|
+
|
|
48
|
+
# : (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
49
|
+
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
50
|
+
|
|
51
|
+
# : (String) -> Hash[Symbol, untyped]?
|
|
52
|
+
def parse_structured_output: (String) -> Hash[Symbol, untyped]?
|
|
46
53
|
|
|
47
54
|
# : ((String | Hash[String, untyped])?) -> Hash[String, untyped]
|
|
48
55
|
def parse_tool_arguments: ((String | Hash[String, untyped])?) -> Hash[String, untyped]
|
|
@@ -39,8 +39,11 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
39
39
|
# : (untyped) -> Riffer::TokenUsage?
|
|
40
40
|
def extract_token_usage: (untyped) -> Riffer::TokenUsage?
|
|
41
41
|
|
|
42
|
-
# : (untyped
|
|
43
|
-
def
|
|
42
|
+
# : (untyped) -> String
|
|
43
|
+
def extract_content: (untyped) -> String
|
|
44
|
+
|
|
45
|
+
# : (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
46
|
+
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
44
47
|
|
|
45
48
|
# : (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
46
49
|
def execute_stream: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
@@ -22,8 +22,11 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
|
|
|
22
22
|
# : (OpenAI::Models::Responses::Response) -> Riffer::TokenUsage?
|
|
23
23
|
def extract_token_usage: (OpenAI::Models::Responses::Response) -> Riffer::TokenUsage?
|
|
24
24
|
|
|
25
|
-
# : (OpenAI::Models::Responses::Response
|
|
26
|
-
def
|
|
25
|
+
# : (OpenAI::Models::Responses::Response) -> String
|
|
26
|
+
def extract_content: (OpenAI::Models::Responses::Response) -> String
|
|
27
|
+
|
|
28
|
+
# : (OpenAI::Models::Responses::Response) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
29
|
+
def extract_tool_calls: (OpenAI::Models::Responses::Response) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
27
30
|
|
|
28
31
|
# : (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|
|
29
32
|
def execute_stream: (Hash[Symbol, untyped], Enumerator::Yielder) -> void
|