riffer 0.50.1 → 0.51.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/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +12 -0
- data/docs/AGENTS.md +3 -1
- data/docs/AGENT_LIFECYCLE.md +1 -1
- data/docs/AGENT_LOOP.md +2 -2
- data/lib/riffer/agent/run.rb +1 -1
- data/lib/riffer/agent/session.rb +7 -0
- data/lib/riffer/agent/structured_output/parser.rb +35 -0
- data/lib/riffer/agent/structured_output.rb +3 -5
- data/lib/riffer/providers/base.rb +1 -3
- data/lib/riffer/version.rb +1 -1
- data/rbs_collection.lock.yaml +2 -2
- data/sig/generated/riffer/agent/session.rbs +4 -0
- data/sig/generated/riffer/agent/structured_output/parser.rbs +17 -0
- data/sig/manual/riffer/agent/structured_output/parser.rbs +5 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1379919ce4dcd42bd21286bb3447f32967cd94fff23cf5a3ed2e422b40a6cfc
|
|
4
|
+
data.tar.gz: 8d7860fa41acf5e89e82bd25f8f57477da356cb6948a4438d9ca0a7c71a6d959
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 882930df0b0f87cc9e2dbe3b792969db10b811a1c33d92ea5797d53564421177032c5ad75362247cbd5e012784fea7bf25c90d5de3a3ce54cc472af2c210e268
|
|
7
|
+
data.tar.gz: 9b23fe7c44c6d3fbbbe5ef165f2dc07c1a55ad2050dfd7b188cb674b9b3fe6ee45441583e8cc544092e3820c1c2a30f1f9809ce3ddd874d038180f94184990fd
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ 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.51.0](https://github.com/janeapp/riffer/compare/riffer/v0.50.1...riffer/v0.51.0) (2026-10-07)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **structured_output:** recover objects wrapped in code fences or prose ([#473](https://github.com/janeapp/riffer/issues/473)) ([59a6d40](https://github.com/janeapp/riffer/commit/59a6d40b08b9f18fcb4ebac24598925f94a9acea))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **agent:** apply the max_steps cap per run, not per conversation ([#478](https://github.com/janeapp/riffer/issues/478)) ([4948e6b](https://github.com/janeapp/riffer/commit/4948e6b9769df084bed8c8a29862067560f800bc))
|
|
19
|
+
|
|
8
20
|
## [0.50.1](https://github.com/janeapp/riffer/compare/riffer/v0.50.0...riffer/v0.50.1) (2026-10-02)
|
|
9
21
|
|
|
10
22
|
|
data/docs/AGENTS.md
CHANGED
|
@@ -168,7 +168,9 @@ 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
|
-
|
|
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.
|
|
172
|
+
|
|
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:
|
|
172
174
|
|
|
173
175
|
```ruby
|
|
174
176
|
response = SentimentAgent.generate('Analyze: "I love this!"')
|
data/docs/AGENT_LIFECYCLE.md
CHANGED
|
@@ -351,7 +351,7 @@ end
|
|
|
351
351
|
|
|
352
352
|
### response.structured_output
|
|
353
353
|
|
|
354
|
-
When structured output is configured, the LLM response is parsed as JSON and validated against the schema. The validated result is available as `response.structured_output`:
|
|
354
|
+
When structured output is configured, the LLM response is parsed as JSON and validated against the schema. A JSON object wrapped in a code fence, prose, or formatting is recovered (see [Agents — structured_output](AGENTS.md#structured_output)). The validated result is available as `response.structured_output`:
|
|
355
355
|
|
|
356
356
|
```ruby
|
|
357
357
|
response = SentimentAgent.generate('Analyze: "I love this!"')
|
data/docs/AGENT_LOOP.md
CHANGED
|
@@ -72,12 +72,12 @@ response = agent.generate('Approved, continue') # continues where it left off
|
|
|
72
72
|
|
|
73
73
|
### Max Steps Limit
|
|
74
74
|
|
|
75
|
-
The `max_steps` class method caps the number of LLM call steps in the tool-use loop. When the step count reaches the limit, the loop interrupts automatically with reason `:max_steps`.
|
|
75
|
+
The `max_steps` class method caps the number of LLM call steps in the tool-use loop of a single run. Steps are counted from the last user message in the session, so each new user message starts a fresh budget — prior turns' assistant messages in the history do not count against it. When the step count reaches the limit, the loop interrupts automatically with reason `:max_steps`.
|
|
76
76
|
|
|
77
77
|
- **When to use:** Safety net to prevent runaway tool-use loops — useful when agents have access to many tools or operate autonomously.
|
|
78
78
|
- **Response:** `response.outcome.reason` is `:max_steps`.
|
|
79
79
|
- **Streaming:** Yields an `Interrupt` event with `reason: :max_steps`.
|
|
80
|
-
- **Resumable:** Yes. Call `generate('Continue')` or `stream('Continue')` on the same agent instance to resume. For cross-process resume, pass persisted messages as an array to a new agent. Pending tool calls are automatically executed before the LLM loop resumes.
|
|
80
|
+
- **Resumable:** Yes. Call `generate('Continue')` or `stream('Continue')` on the same agent instance to resume with a fresh budget, or call `generate` / `stream` with no prompt to resume against the remaining budget. For cross-process resume, pass persisted messages as an array to a new agent; the same rule applies. Pending tool calls are automatically executed before the LLM loop resumes.
|
|
81
81
|
|
|
82
82
|
```ruby
|
|
83
83
|
class MyAgent < Riffer::Agent
|
data/lib/riffer/agent/run.rb
CHANGED
data/lib/riffer/agent/session.rb
CHANGED
|
@@ -143,6 +143,13 @@ class Riffer::Agent::Session
|
|
|
143
143
|
@messages.count { |m| m.is_a?(Riffer::Messages::Assistant) }
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
+
#--
|
|
147
|
+
#: () -> Integer
|
|
148
|
+
def steps_since_last_user_message
|
|
149
|
+
since_last_user = @messages.reverse_each.take_while { |m| !m.is_a?(Riffer::Messages::User) }
|
|
150
|
+
since_last_user.count { |m| m.is_a?(Riffer::Messages::Assistant) }
|
|
151
|
+
end
|
|
152
|
+
|
|
146
153
|
#--
|
|
147
154
|
#: () -> Riffer::Messages::Assistant?
|
|
148
155
|
def final_assistant_message
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Riffer::Agent::StructuredOutput::Parser
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
#--
|
|
10
|
+
#: (String) -> Hash[Symbol, untyped]?
|
|
11
|
+
def parse(content)
|
|
12
|
+
parse_object(content) || outermost_object(content)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
#--
|
|
18
|
+
#: (String) -> Hash[Symbol, untyped]?
|
|
19
|
+
def outermost_object(content)
|
|
20
|
+
start = content.index("{")
|
|
21
|
+
stop = content.rindex("}")
|
|
22
|
+
return unless start && stop
|
|
23
|
+
|
|
24
|
+
parse_object(content[start..stop].to_s)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#--
|
|
28
|
+
#: (String) -> Hash[Symbol, untyped]?
|
|
29
|
+
def parse_object(json)
|
|
30
|
+
parsed = JSON.parse(json, symbolize_names: true)
|
|
31
|
+
parsed if parsed.is_a?(Hash)
|
|
32
|
+
rescue JSON::ParserError
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
require "json"
|
|
5
|
-
|
|
6
4
|
class Riffer::Agent::StructuredOutput
|
|
7
5
|
attr_reader :params #: Riffer::Params # @dynamic params
|
|
8
6
|
|
|
@@ -21,11 +19,11 @@ class Riffer::Agent::StructuredOutput
|
|
|
21
19
|
#--
|
|
22
20
|
#: (String) -> Riffer::Agent::StructuredOutput::Result
|
|
23
21
|
def parse_and_validate(json_string)
|
|
24
|
-
parsed =
|
|
22
|
+
parsed = Riffer::Agent::StructuredOutput::Parser.parse(json_string)
|
|
23
|
+
return Result.new(error: "JSON parse error: no JSON object found in the response") if parsed.nil?
|
|
24
|
+
|
|
25
25
|
validated = @params.validate(parsed)
|
|
26
26
|
Result.new(object: validated)
|
|
27
|
-
rescue JSON::ParserError => e
|
|
28
|
-
Result.new(error: "JSON parse error: #{e.message}")
|
|
29
27
|
rescue Riffer::ValidationError => e
|
|
30
28
|
Result.new(error: "Validation error: #{e.message}")
|
|
31
29
|
end
|
|
@@ -360,9 +360,7 @@ class Riffer::Providers::Base
|
|
|
360
360
|
#--
|
|
361
361
|
#: (String) -> Hash[Symbol, untyped]?
|
|
362
362
|
def parse_structured_output(content)
|
|
363
|
-
|
|
364
|
-
rescue JSON::ParserError
|
|
365
|
-
nil
|
|
363
|
+
Riffer::Agent::StructuredOutput::Parser.parse(content)
|
|
366
364
|
end
|
|
367
365
|
|
|
368
366
|
#--
|
data/lib/riffer/version.rb
CHANGED
data/rbs_collection.lock.yaml
CHANGED
|
@@ -10,7 +10,7 @@ gems:
|
|
|
10
10
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
11
11
|
repo_dir: gems
|
|
12
12
|
- name: anthropic
|
|
13
|
-
version: 1.
|
|
13
|
+
version: 1.76.0
|
|
14
14
|
source:
|
|
15
15
|
type: rubygems
|
|
16
16
|
- name: ast
|
|
@@ -170,7 +170,7 @@ gems:
|
|
|
170
170
|
source:
|
|
171
171
|
type: stdlib
|
|
172
172
|
- name: openai
|
|
173
|
-
version: 0.
|
|
173
|
+
version: 0.97.0
|
|
174
174
|
source:
|
|
175
175
|
type: rubygems
|
|
176
176
|
- name: openssl
|
|
@@ -57,6 +57,10 @@ class Riffer::Agent::Session
|
|
|
57
57
|
# : () -> Integer
|
|
58
58
|
def steps: () -> Integer
|
|
59
59
|
|
|
60
|
+
# --
|
|
61
|
+
# : () -> Integer
|
|
62
|
+
def steps_since_last_user_message: () -> Integer
|
|
63
|
+
|
|
60
64
|
# --
|
|
61
65
|
# : () -> Riffer::Messages::Assistant?
|
|
62
66
|
def final_assistant_message: () -> Riffer::Messages::Assistant?
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/structured_output/parser.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Riffer::Agent::StructuredOutput::Parser
|
|
4
|
+
# --
|
|
5
|
+
# : (String) -> Hash[Symbol, untyped]?
|
|
6
|
+
def parse: (String) -> Hash[Symbol, untyped]?
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# --
|
|
11
|
+
# : (String) -> Hash[Symbol, untyped]?
|
|
12
|
+
def outermost_object: (String) -> Hash[Symbol, untyped]?
|
|
13
|
+
|
|
14
|
+
# --
|
|
15
|
+
# : (String) -> Hash[Symbol, untyped]?
|
|
16
|
+
def parse_object: (String) -> Hash[Symbol, untyped]?
|
|
17
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# `Riffer::Agent::StructuredOutput::Parser` uses `extend self`; rbs-inline doesn't emit that,
|
|
2
|
+
# so re-extend here to expose its instance methods as singleton methods.
|
|
3
|
+
module Riffer::Agent::StructuredOutput::Parser
|
|
4
|
+
extend ::Riffer::Agent::StructuredOutput::Parser
|
|
5
|
+
end
|
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.51.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jake Bottrall
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- lib/riffer/agent/session.rb
|
|
118
118
|
- lib/riffer/agent/session/repair.rb
|
|
119
119
|
- lib/riffer/agent/structured_output.rb
|
|
120
|
+
- lib/riffer/agent/structured_output/parser.rb
|
|
120
121
|
- lib/riffer/agent/structured_output/result.rb
|
|
121
122
|
- lib/riffer/config.rb
|
|
122
123
|
- lib/riffer/config/amazon_bedrock.rb
|
|
@@ -265,6 +266,7 @@ files:
|
|
|
265
266
|
- sig/generated/riffer/agent/session.rbs
|
|
266
267
|
- sig/generated/riffer/agent/session/repair.rbs
|
|
267
268
|
- sig/generated/riffer/agent/structured_output.rbs
|
|
269
|
+
- sig/generated/riffer/agent/structured_output/parser.rbs
|
|
268
270
|
- sig/generated/riffer/agent/structured_output/result.rbs
|
|
269
271
|
- sig/generated/riffer/config.rbs
|
|
270
272
|
- sig/generated/riffer/config/amazon_bedrock.rbs
|
|
@@ -390,6 +392,7 @@ files:
|
|
|
390
392
|
- sig/manual/riffer/agent/run.rbs
|
|
391
393
|
- sig/manual/riffer/agent/serializer.rbs
|
|
392
394
|
- sig/manual/riffer/agent/session/repair.rbs
|
|
395
|
+
- sig/manual/riffer/agent/structured_output/parser.rbs
|
|
393
396
|
- sig/manual/riffer/evals/evaluator_runner.rbs
|
|
394
397
|
- sig/manual/riffer/helpers/boolean.rbs
|
|
395
398
|
- sig/manual/riffer/helpers/call_or_value.rbs
|