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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae2818fb04f43e8b5bd3a4a2ecfc2302af15ac073985e08f3fd36c461bafdf8c
4
- data.tar.gz: e5ec190ea0c927eb38604849798b44fc96a03ba80510376d1d0e2eb2f5138fd0
3
+ metadata.gz: 89f3c7419e5a1cde4cf2a7ade73a224dc30ac5ae900041938eb165a31e568e81
4
+ data.tar.gz: a07f353544bbdd3904092b34d2c4c03e1dadaad6374876f2ef3dc3767f3373d2
5
5
  SHA512:
6
- metadata.gz: c841653977d2277537ae2930a874d32748f874a4995fa058fd2abdff798d70f99cecdc4e75058179194f7b78502e209c4c97d916b4f568cab5935b9f4d470e84
7
- data.tar.gz: 383682e8b929bc8f164c9bce1ae0b6bbec9a6ea7a30a5282bde79becfe886b22e7177f45ac5421ec97aad7fdfd2adf0150d649d0791c14f931c59ab737b36940
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 five hook methods that each provider implements:
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
- ├─ extract_token_usage
20
- └─ extract_assistant_message
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
- # Parse the SDK response into an Assistant message.
82
+ # Extract text content from the SDK response.
82
83
  #
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)
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
  ```
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.14.0"
2
+ ".": "0.15.0"
3
3
  }
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
- content = extract_final_response
324
- structured_output = parse_structured_content(content)
325
- return build_response(content, modifications: all_modifications, structured_output: 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
- content = extract_final_response
332
- structured_output = parse_structured_content(content)
333
- build_response(content, modifications: all_modifications, interrupted: true, interrupt_reason: reason, structured_output: 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
- #: () -> String
617
+ #: () -> Riffer::Messages::Assistant?
618
618
  def extract_final_response
619
- last_assistant_message = @messages.reverse.find { |msg| msg.is_a?(Riffer::Messages::Assistant) }
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
- #: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?) -> void
23
- def initialize(content, tool_calls: [], token_usage: nil)
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
- Riffer::Messages::Assistant.new(content, tool_calls: tool_calls)
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
88
- def extract_assistant_message(response, token_usage = nil)
89
- output = response.output
90
- raise Riffer::Error, "No output returned from Bedrock API" if output.nil? || output.message.nil?
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?(:text) && block.text
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
- if text_content.empty? && tool_calls.empty?
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
85
- def extract_assistant_message(response, token_usage = nil)
84
+ #: (Anthropic::Models::Message) -> String
85
+ def extract_content(response)
86
86
  content_blocks = response.content
87
- raise Riffer::Error, "No content returned from Anthropic API" if content_blocks.nil? || content_blocks.empty?
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
- block_type = block.type.to_s
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
- if text_content.empty? && tool_calls.empty?
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
- # - +extract_assistant_message+ — parse the SDK response into an +Assistant+ message
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
- extract_assistant_message(response, extract_token_usage(response))
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
70
- def extract_assistant_message(response, token_usage = nil)
71
- raise NotImplementedError, "Subclasses must implement #extract_assistant_message"
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
71
- def extract_assistant_message(response, token_usage = nil)
72
- if response.is_a?(Hash)
73
- Riffer::Messages::Assistant.new(
74
- response[:content],
75
- tool_calls: response[:tool_calls] || [],
76
- token_usage: token_usage
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
81
- def extract_assistant_message(response, token_usage = nil)
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
- output_items.each do |item|
88
- case item.type
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
- when :function_call
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
- if text_content.empty? && tool_calls.empty?
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
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.14.0" #: String
5
+ VERSION = "0.15.0" #: String
6
6
  end
@@ -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
- # : () -> String
235
- def extract_final_response: () -> String
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
- # : (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?) -> void
32
- def initialize: (String, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::TokenUsage?) -> void
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
26
- def extract_assistant_message: (Aws::BedrockRuntime::Types::ConverseResponse, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
28
- def extract_assistant_message: (Anthropic::Models::Message, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
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
- # - +extract_assistant_message+ — parse the SDK response into an +Assistant+ message
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
45
- def extract_assistant_message: (untyped, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
43
- def extract_assistant_message: (untyped, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
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, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
26
- def extract_assistant_message: (OpenAI::Models::Responses::Response, ?Riffer::TokenUsage?) -> Riffer::Messages::Assistant
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
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.14.0
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall