riffer 0.51.0 → 0.52.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: c1379919ce4dcd42bd21286bb3447f32967cd94fff23cf5a3ed2e422b40a6cfc
4
- data.tar.gz: 8d7860fa41acf5e89e82bd25f8f57477da356cb6948a4438d9ca0a7c71a6d959
3
+ metadata.gz: cdd49e9dd195fd8a90f82297b355f5a267fe79d15a909bb40219fcc5faedc1ea
4
+ data.tar.gz: c0aeeffd8b3aba99953c1efc2ac186a65d86611bd4c9e690cdd31c8834e8242d
5
5
  SHA512:
6
- metadata.gz: 882930df0b0f87cc9e2dbe3b792969db10b811a1c33d92ea5797d53564421177032c5ad75362247cbd5e012784fea7bf25c90d5de3a3ce54cc472af2c210e268
7
- data.tar.gz: 9b23fe7c44c6d3fbbbe5ef165f2dc07c1a55ad2050dfd7b188cb674b9b3fe6ee45441583e8cc544092e3820c1c2a30f1f9809ce3ddd874d038180f94184990fd
6
+ metadata.gz: fc7d4287a7a94b1891d95d2c048e8ad3d5cd1cac9e7cae3047887316ed52224aa8f72a0c01eaba5d0ba1ebe1b4d78b48de42cc4a00114d5a8119fefe405f0e58
7
+ data.tar.gz: db472038a3c65f8643e792c987a837e1955c0699d3a2fd6297612e7f3bc8a8374f56751541663e9662f6da2dea0381a219d43b32b96eef30d6716d9662fdb540
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.51.0"
2
+ ".": "0.52.1"
3
3
  }
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.52.1](https://github.com/janeapp/riffer/compare/riffer/v0.52.0...riffer/v0.52.1) (2026-10-09)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **structured_output:** return the extracted JSON as response content ([#484](https://github.com/janeapp/riffer/issues/484)) ([cbea668](https://github.com/janeapp/riffer/commit/cbea66888690ec7fc0dccbde6198d2704fb165f0))
14
+
15
+ ## [0.52.0](https://github.com/janeapp/riffer/compare/riffer/v0.51.0...riffer/v0.52.0) (2026-10-08)
16
+
17
+
18
+ ### Features
19
+
20
+ * **structured_output:** add experimental prompted strategy ([#477](https://github.com/janeapp/riffer/issues/477)) ([f517fe1](https://github.com/janeapp/riffer/commit/f517fe19788fe34167515998a7ac2870605b2228))
21
+
8
22
  ## [0.51.0](https://github.com/janeapp/riffer/compare/riffer/v0.50.1...riffer/v0.51.0) (2026-10-07)
9
23
 
10
24
 
data/docs/AGENTS.md CHANGED
@@ -168,7 +168,7 @@ end
168
168
 
169
169
  The LLM response is automatically parsed and validated against the schema. Access the result via `response.structured_output`.
170
170
 
171
- If the response is not valid JSON on its own, Riffer recovers a JSON object wrapped in a code fence (```` ```json ````), surrounded by prose (`Here is the data: {...}`), or wrapped in formatting (`**{...}**`). It parses the span from the first `{` to the last `}`, so a reply whose surrounding text contains other braces, or that holds more than one object, is still rejected; `response.content` keeps the raw text.
171
+ If the response is not valid JSON on its own, Riffer recovers a JSON object wrapped in a code fence (```` ```json ````), surrounded by prose (`Here is the data: {...}`), or wrapped in formatting (`**{...}**`). It parses the span from the first `{` to the last `}`, so a reply whose surrounding text contains other braces, or that holds more than one object, is still rejected. When the recovered object validates, `response.content` holds only the extracted JSON text, without the fence or prose; the assistant message in `response.messages` keeps the raw reply. A reply that fails parsing or validation leaves the raw text in `response.content`.
172
172
 
173
173
  When the response contains no well-formed JSON object or does not satisfy the schema, `response.structured_output` is `nil`, `response.outcome.reason` is `:invalid_structured_output`, and `response.outcome.detail` carries the parse or validation message:
174
174
 
@@ -183,6 +183,29 @@ else
183
183
  end
184
184
  ```
185
185
 
186
+ #### Structured output strategy (experimental)
187
+
188
+ By default Riffer sends the schema through each provider's native structured-output field. Some models reject that field, accept it without enforcing it, or behave differently with it enabled (for example, skipping tool calls). For those, opt the agent into the **experimental** prompted strategy:
189
+
190
+ ```ruby
191
+ class SentimentAgent < Riffer::Agent
192
+ model 'amazon_bedrock/my-model'
193
+ instructions 'Analyze the sentiment of the given text.'
194
+ structured_output strategy: :prompted do
195
+ required :sentiment, String, description: "positive, negative, or neutral"
196
+ end
197
+ end
198
+ ```
199
+
200
+ With `strategy: :prompted`, the provider never receives its native structured-output field. Instead, an instruction carrying the JSON Schema is appended to the system instructions on every request of the run (it is not stored in `session.messages`), and the reply is parsed and validated exactly as above: `response.content` is the reply's JSON text, `response.structured_output` holds the validated object, and a reply that fails parsing or validation ends with `:invalid_structured_output`. Because nothing forces the model to comply, expect more `:invalid_structured_output` outcomes than with the native strategy.
201
+
202
+ | Strategy | Behavior |
203
+ | ------------------- | --------------------------------------------------------------- |
204
+ | `:native` (default) | Schema sent through the provider's structured-output field |
205
+ | `:prompted` | Schema sent in the system prompt; experimental, may change |
206
+
207
+ Riffer does not pick a strategy per model — the choice is always yours. The strategy is copied to subclasses and survives [serialization](SERIALIZATION.md). An unknown value raises `Riffer::ArgumentError`. `strategy:` can also be passed with a `Riffer::Params` instance: `structured_output params, strategy: :prompted`.
208
+
186
209
  #### Nested Objects
187
210
 
188
211
  Use `Hash` with a block to define nested object schemas:
@@ -45,6 +45,7 @@ The session is used **as-is**: the rebuilt agent does not prepend anything to it
45
45
  model_options: { temperature: 0.2 },
46
46
  max_steps: 8, # integer; -1 = unlimited (see below)
47
47
  structured_output: { type: "object", … }, # JSON Schema, or null
48
+ structured_output_strategy: :native, # :native or :prompted ("native"/"prompted" over JSON)
48
49
  tools: [ { name:, description:, parameters_schema:, timeout: }, … ]
49
50
  }
50
51
  ```
@@ -57,6 +58,8 @@ The session is used **as-is**: the rebuilt agent does not prepend anything to it
57
58
 
58
59
  Structured output crosses as **provider-neutral JSON Schema** (`Riffer::Params#to_json_schema`), never a provider-rendered schema. `from_h` rebuilds a validating `Riffer::Params` via `Riffer::Params.from_json_schema`, so the rebuilt agent both constrains the model and can `parse_and_validate` responses — rendering provider-correct bytes at call time, the same way an in-code agent does.
59
60
 
61
+ `structured_output_strategy` carries the [strategy](AGENTS.md#structured-output-strategy-experimental) the agent was configured with, so a `:prompted` agent rebuilds as `:prompted`. A hash without the key decodes as `:native`.
62
+
60
63
  ## Reconstructing tools
61
64
 
62
65
  Tools cross as `{name, description, parameters_schema, timeout}` descriptors — never code. How a descriptor becomes a runnable tool is controlled by the `tool_resolver:` you pass to `from_h`. The two common shapes:
@@ -3,6 +3,7 @@
3
3
 
4
4
  class Riffer::Agent::Config
5
5
  DEFAULT_MAX_STEPS = 16 #: Integer
6
+ STRUCTURED_OUTPUT_STRATEGIES = %i[native prompted].freeze #: Array[Symbol]
6
7
 
7
8
  # @rbs @tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?
8
9
 
@@ -11,6 +12,7 @@ class Riffer::Agent::Config
11
12
  attr_reader :instructions #: (String | Proc)? # @dynamic instructions
12
13
  attr_accessor :model_options #: Hash[Symbol, untyped] # @dynamic model_options, model_options=
13
14
  attr_reader :structured_output #: Riffer::Params? # @dynamic structured_output
15
+ attr_reader :structured_output_strategy #: Symbol # @dynamic structured_output_strategy
14
16
  attr_accessor :max_steps #: Numeric? # @dynamic max_steps, max_steps=
15
17
  attr_accessor :tools_config #: (Array[singleton(Riffer::Tool)] | Proc)? # @dynamic tools_config, tools_config=
16
18
  attr_reader :mcp_configs #: Array[Hash[Symbol, untyped]] # @dynamic mcp_configs
@@ -24,6 +26,7 @@ class Riffer::Agent::Config
24
26
  # ?instructions: (String | Proc)?,
25
27
  # ?model_options: Hash[Symbol, untyped],
26
28
  # ?structured_output: Riffer::Params?,
29
+ # ?structured_output_strategy: Symbol,
27
30
  # ?max_steps: Numeric?,
28
31
  # ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?,
29
32
  # ?mcp_configs: Array[Hash[Symbol, untyped]],
@@ -37,6 +40,7 @@ class Riffer::Agent::Config
37
40
  instructions: nil,
38
41
  model_options: {},
39
42
  structured_output: nil,
43
+ structured_output_strategy: :native,
40
44
  max_steps: DEFAULT_MAX_STEPS,
41
45
  tools_config: nil,
42
46
  mcp_configs: [],
@@ -54,6 +58,7 @@ class Riffer::Agent::Config
54
58
  self.model = model
55
59
  self.instructions = instructions
56
60
  self.structured_output = structured_output
61
+ self.structured_output_strategy = structured_output_strategy
57
62
  self.tool_runtime = tool_runtime if tool_runtime
58
63
  end
59
64
 
@@ -73,6 +78,17 @@ class Riffer::Agent::Config
73
78
  @structured_output = value
74
79
  end
75
80
 
81
+ #--
82
+ #: (Symbol) -> Symbol
83
+ def structured_output_strategy=(value)
84
+ unless STRUCTURED_OUTPUT_STRATEGIES.include?(value)
85
+ raise Riffer::ArgumentError,
86
+ "structured_output_strategy must be one of #{STRUCTURED_OUTPUT_STRATEGIES.inspect}, got #{value.inspect}"
87
+ end
88
+
89
+ @structured_output_strategy = value
90
+ end
91
+
76
92
  #--
77
93
  #: () -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
78
94
  def tool_runtime
@@ -207,7 +207,7 @@ module Riffer::Agent::Run
207
207
  result = agent.structured_output && structured_output_result(agent, message)
208
208
  build_response(
209
209
  agent,
210
- message&.content || "",
210
+ result&.json || message&.content || "",
211
211
  outcome: final_outcome(message, result, interrupted: interrupted, interrupt_reason: interrupt_reason),
212
212
  modifications: all_modifications,
213
213
  reasoning: message&.reasoning || [],
@@ -27,6 +27,7 @@ module Riffer::Agent::Serializer
27
27
  model_options: config.model_options,
28
28
  max_steps: encode_max_steps(config.max_steps),
29
29
  structured_output: config.structured_output&.to_json_schema(strict: false),
30
+ structured_output_strategy: config.structured_output_strategy,
30
31
  tools: agent.tools.map { |tool_class| tool_descriptor(tool_class) },
31
32
  }
32
33
  end
@@ -76,6 +77,7 @@ module Riffer::Agent::Serializer
76
77
  instructions: hash[:instructions],
77
78
  model_options: hash[:model_options] || {},
78
79
  structured_output: decode_structured_output(hash[:structured_output]),
80
+ structured_output_strategy: (hash[:structured_output_strategy] || :native).to_sym,
79
81
  max_steps: decode_max_steps(hash),
80
82
  tools_config: tools,
81
83
  } #: Hash[Symbol, untyped]
@@ -9,19 +9,30 @@ module Riffer::Agent::StructuredOutput::Parser
9
9
  #--
10
10
  #: (String) -> Hash[Symbol, untyped]?
11
11
  def parse(content)
12
- parse_object(content) || outermost_object(content)
12
+ extract(content)&.last
13
+ end
14
+
15
+ #--
16
+ #: (String) -> [String, Hash[Symbol, untyped]]?
17
+ def extract(content)
18
+ parsed = parse_object(content)
19
+ return [content, parsed] if parsed
20
+
21
+ outermost_object(content)
13
22
  end
14
23
 
15
24
  private
16
25
 
17
26
  #--
18
- #: (String) -> Hash[Symbol, untyped]?
27
+ #: (String) -> [String, Hash[Symbol, untyped]]?
19
28
  def outermost_object(content)
20
29
  start = content.index("{")
21
30
  stop = content.rindex("}")
22
31
  return unless start && stop
23
32
 
24
- parse_object(content[start..stop].to_s)
33
+ json = content[start..stop].to_s
34
+ parsed = parse_object(json)
35
+ [json, parsed] if parsed
25
36
  end
26
37
 
27
38
  #--
@@ -5,11 +5,14 @@ class Riffer::Agent::StructuredOutput::Result
5
5
  attr_reader :object #: Hash[Symbol, untyped]? # @dynamic object
6
6
  attr_reader :error #: String? # @dynamic error
7
7
 
8
+ attr_reader :json #: String? # @dynamic json
9
+
8
10
  #--
9
- #: (?object: Hash[Symbol, untyped]?, ?error: String?) -> void
10
- def initialize(object: nil, error: nil)
11
+ #: (?object: Hash[Symbol, untyped]?, ?error: String?, ?json: String?) -> void
12
+ def initialize(object: nil, error: nil, json: nil)
11
13
  @object = object
12
14
  @error = error
15
+ @json = json
13
16
  end
14
17
 
15
18
  #--
@@ -1,13 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
+ require "json"
5
+
4
6
  class Riffer::Agent::StructuredOutput
5
7
  attr_reader :params #: Riffer::Params # @dynamic params
8
+ attr_reader :strategy #: Symbol # @dynamic strategy
6
9
 
7
10
  #--
8
- #: (Riffer::Params) -> void
9
- def initialize(params)
11
+ #: (Riffer::Params, ?strategy: Symbol) -> void
12
+ def initialize(params, strategy: :native)
10
13
  @params = params
14
+ @strategy = strategy
15
+ end
16
+
17
+ #--
18
+ #: () -> bool
19
+ def prompted?
20
+ @strategy == :prompted
11
21
  end
12
22
 
13
23
  #--
@@ -16,14 +26,23 @@ class Riffer::Agent::StructuredOutput
16
26
  @params.to_json_schema(strict: strict)
17
27
  end
18
28
 
29
+ # Worded for the final answer only, so the model can still call tools first.
30
+ #--
31
+ #: () -> String
32
+ def prompt_instructions
33
+ "When you give your final answer, respond with only a JSON object that conforms to " \
34
+ "the following JSON Schema, with no other text before or after it:\n\n" \
35
+ "#{JSON.generate(json_schema)}"
36
+ end
37
+
19
38
  #--
20
39
  #: (String) -> Riffer::Agent::StructuredOutput::Result
21
40
  def parse_and_validate(json_string)
22
- parsed = Riffer::Agent::StructuredOutput::Parser.parse(json_string)
41
+ json, parsed = Riffer::Agent::StructuredOutput::Parser.extract(json_string)
23
42
  return Result.new(error: "JSON parse error: no JSON object found in the response") if parsed.nil?
24
43
 
25
44
  validated = @params.validate(parsed)
26
- Result.new(object: validated)
45
+ Result.new(object: validated, json: json)
27
46
  rescue Riffer::ValidationError => e
28
47
  Result.new(error: "Validation error: #{e.message}")
29
48
  end
data/lib/riffer/agent.rb CHANGED
@@ -53,13 +53,14 @@ class Riffer::Agent
53
53
  end
54
54
 
55
55
  #--
56
- #: (?Riffer::Params?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
57
- def self.structured_output(params = nil, &block)
56
+ #: (?Riffer::Params?, ?strategy: Symbol?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
57
+ def self.structured_output(params = nil, strategy: nil, &block)
58
58
  if block
59
59
  params = Riffer::Params.new
60
60
  params.instance_eval(&block)
61
61
  end
62
62
  config.structured_output = params if params
63
+ config.structured_output_strategy = strategy if strategy
63
64
  config.structured_output
64
65
  end
65
66
 
@@ -328,7 +329,7 @@ class Riffer::Agent
328
329
  #: () -> Riffer::Agent::StructuredOutput?
329
330
  def resolve_structured_output
330
331
  params = @config.structured_output
331
- params ? Riffer::Agent::StructuredOutput.new(params) : nil
332
+ params ? Riffer::Agent::StructuredOutput.new(params, strategy: @config.structured_output_strategy) : nil
332
333
  end
333
334
 
334
335
  #--
@@ -38,8 +38,8 @@ class Riffer::Providers::Base
38
38
  messages = normalize_messages(prompt: prompt, system: system, messages: messages, files: files)
39
39
  validate_normalized_messages!(messages)
40
40
  Riffer::Files::Resolver.new(provider: self).resolve!(messages)
41
- messages = merge_consecutive_messages(messages)
42
- params = build_request_params(messages, model, options)
41
+ messages = merge_consecutive_messages(with_structured_output_prompt(messages, options[:structured_output]))
42
+ params = build_request_params(messages, model, request_options(options))
43
43
 
44
44
  in_chat_span(model, messages, options) do |span|
45
45
  response = execute_generate(params)
@@ -76,8 +76,8 @@ class Riffer::Providers::Base
76
76
  messages = normalize_messages(prompt: prompt, system: system, messages: messages, files: files)
77
77
  validate_normalized_messages!(messages)
78
78
  Riffer::Files::Resolver.new(provider: self).resolve!(messages)
79
- messages = merge_consecutive_messages(messages)
80
- params = build_request_params(messages, model, options)
79
+ messages = merge_consecutive_messages(with_structured_output_prompt(messages, options[:structured_output]))
80
+ params = build_request_params(messages, model, request_options(options))
81
81
 
82
82
  # The enumerator body runs in its own fiber, where the fiber-local OTEL
83
83
  # context is empty — capture here so the chat span parents to the caller's
@@ -371,6 +371,26 @@ class Riffer::Providers::Base
371
371
  JSON.parse(arguments)
372
372
  end
373
373
 
374
+ #--
375
+ #: (Array[Riffer::Messages::Base], Riffer::Agent::StructuredOutput?) -> Array[Riffer::Messages::Base]
376
+ def with_structured_output_prompt(messages, structured_output)
377
+ return messages unless structured_output&.prompted?
378
+
379
+ # After the leading system messages, so merge_consecutive_messages folds it into the system prompt.
380
+ leading_system = messages.take_while { |m| m.is_a?(Riffer::Messages::System) }
381
+ [
382
+ *leading_system,
383
+ Riffer::Messages::System.new(structured_output.prompt_instructions),
384
+ *messages.drop(leading_system.length),
385
+ ]
386
+ end
387
+
388
+ #--
389
+ #: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
390
+ def request_options(options)
391
+ options[:structured_output]&.prompted? ? options.except(:structured_output) : options
392
+ end
393
+
374
394
  #--
375
395
  #: (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
376
396
  def merge_consecutive_messages(messages)
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.51.0" #: String
5
+ VERSION = "0.52.1" #: String
6
6
  end
@@ -3,6 +3,8 @@
3
3
  class Riffer::Agent::Config
4
4
  DEFAULT_MAX_STEPS: Integer
5
5
 
6
+ STRUCTURED_OUTPUT_STRATEGIES: Array[Symbol]
7
+
6
8
  @tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?
7
9
 
8
10
  attr_reader identifier: String?
@@ -15,6 +17,8 @@ class Riffer::Agent::Config
15
17
 
16
18
  attr_reader structured_output: Riffer::Params?
17
19
 
20
+ attr_reader structured_output_strategy: Symbol
21
+
18
22
  attr_accessor max_steps: Numeric?
19
23
 
20
24
  attr_accessor tools_config: (Array[singleton(Riffer::Tool)] | Proc)?
@@ -32,6 +36,7 @@ class Riffer::Agent::Config
32
36
  # ?instructions: (String | Proc)?,
33
37
  # ?model_options: Hash[Symbol, untyped],
34
38
  # ?structured_output: Riffer::Params?,
39
+ # ?structured_output_strategy: Symbol,
35
40
  # ?max_steps: Numeric?,
36
41
  # ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?,
37
42
  # ?mcp_configs: Array[Hash[Symbol, untyped]],
@@ -39,7 +44,7 @@ class Riffer::Agent::Config
39
44
  # ?skills_config: Riffer::Skills::Config?,
40
45
  # ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]
41
46
  # ) -> void
42
- def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
47
+ def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?model_options: Hash[Symbol, untyped], ?structured_output: Riffer::Params?, ?structured_output_strategy: Symbol, ?max_steps: Numeric?, ?tools_config: (Array[singleton(Riffer::Tool)] | Proc)?, ?mcp_configs: Array[Hash[Symbol, untyped]], ?tool_runtime: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)?, ?skills_config: Riffer::Skills::Config?, ?guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]) -> void
43
48
 
44
49
  # --
45
50
  # : (untyped) -> String?
@@ -49,6 +54,10 @@ class Riffer::Agent::Config
49
54
  # : (Riffer::Params?) -> Riffer::Params?
50
55
  def structured_output=: (Riffer::Params?) -> Riffer::Params?
51
56
 
57
+ # --
58
+ # : (Symbol) -> Symbol
59
+ def structured_output_strategy=: (Symbol) -> Symbol
60
+
52
61
  # --
53
62
  # : () -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
54
63
  def tool_runtime: () -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
@@ -5,11 +5,15 @@ module Riffer::Agent::StructuredOutput::Parser
5
5
  # : (String) -> Hash[Symbol, untyped]?
6
6
  def parse: (String) -> Hash[Symbol, untyped]?
7
7
 
8
+ # --
9
+ # : (String) -> [String, Hash[Symbol, untyped]]?
10
+ def extract: (String) -> [ String, Hash[Symbol, untyped] ]?
11
+
8
12
  private
9
13
 
10
14
  # --
11
- # : (String) -> Hash[Symbol, untyped]?
12
- def outermost_object: (String) -> Hash[Symbol, untyped]?
15
+ # : (String) -> [String, Hash[Symbol, untyped]]?
16
+ def outermost_object: (String) -> [ String, Hash[Symbol, untyped] ]?
13
17
 
14
18
  # --
15
19
  # : (String) -> Hash[Symbol, untyped]?
@@ -5,9 +5,11 @@ class Riffer::Agent::StructuredOutput::Result
5
5
 
6
6
  attr_reader error: String?
7
7
 
8
+ attr_reader json: String?
9
+
8
10
  # --
9
- # : (?object: Hash[Symbol, untyped]?, ?error: String?) -> void
10
- def initialize: (?object: Hash[Symbol, untyped]?, ?error: String?) -> void
11
+ # : (?object: Hash[Symbol, untyped]?, ?error: String?, ?json: String?) -> void
12
+ def initialize: (?object: Hash[Symbol, untyped]?, ?error: String?, ?json: String?) -> void
11
13
 
12
14
  # --
13
15
  # : () -> bool
@@ -3,14 +3,25 @@
3
3
  class Riffer::Agent::StructuredOutput
4
4
  attr_reader params: Riffer::Params
5
5
 
6
+ attr_reader strategy: Symbol
7
+
8
+ # --
9
+ # : (Riffer::Params, ?strategy: Symbol) -> void
10
+ def initialize: (Riffer::Params, ?strategy: Symbol) -> void
11
+
6
12
  # --
7
- # : (Riffer::Params) -> void
8
- def initialize: (Riffer::Params) -> void
13
+ # : () -> bool
14
+ def prompted?: () -> bool
9
15
 
10
16
  # --
11
17
  # : (?strict: bool) -> Hash[Symbol, untyped]
12
18
  def json_schema: (?strict: bool) -> Hash[Symbol, untyped]
13
19
 
20
+ # Worded for the final answer only, so the model can still call tools first.
21
+ # --
22
+ # : () -> String
23
+ def prompt_instructions: () -> String
24
+
14
25
  # --
15
26
  # : (String) -> Riffer::Agent::StructuredOutput::Result
16
27
  def parse_and_validate: (String) -> Riffer::Agent::StructuredOutput::Result
@@ -32,8 +32,8 @@ class Riffer::Agent
32
32
  def self.model_options: (?Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]
33
33
 
34
34
  # --
35
- # : (?Riffer::Params?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
36
- def self.structured_output: (?Riffer::Params?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
35
+ # : (?Riffer::Params?, ?strategy: Symbol?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
36
+ def self.structured_output: (?Riffer::Params?, ?strategy: Symbol?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
37
37
 
38
38
  # --
39
39
  # : (*Numeric?) -> Numeric?
@@ -146,6 +146,14 @@ class Riffer::Providers::Base
146
146
  # : (String) -> Hash[String, untyped]
147
147
  def parse_tool_arguments: (String) -> Hash[String, untyped]
148
148
 
149
+ # --
150
+ # : (Array[Riffer::Messages::Base], Riffer::Agent::StructuredOutput?) -> Array[Riffer::Messages::Base]
151
+ def with_structured_output_prompt: (Array[Riffer::Messages::Base], Riffer::Agent::StructuredOutput?) -> Array[Riffer::Messages::Base]
152
+
153
+ # --
154
+ # : (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
155
+ def request_options: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
156
+
149
157
  # --
150
158
  # : (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
151
159
  def merge_consecutive_messages: (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
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.51.0
4
+ version: 0.52.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall