riffer 0.44.0 → 0.46.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 +24 -0
- data/docs/AGENTS.md +13 -0
- data/docs/AGENT_LIFECYCLE.md +39 -14
- data/docs/AGENT_LOOP.md +8 -9
- data/docs/CONFIGURATION.md +24 -17
- data/docs/GUARDRAILS.md +4 -4
- data/docs/MESSAGES.md +14 -11
- data/docs/STREAM_EVENTS.md +1 -1
- data/docs/TOOL_ADVANCED.md +2 -0
- data/docs/TRACING.md +4 -2
- data/docs/providers/CUSTOM_PROVIDERS.md +1 -1
- data/docs/providers/MOCK_PROVIDER.md +18 -11
- data/lib/riffer/agent/outcome.rb +52 -0
- data/lib/riffer/agent/response.rb +12 -33
- data/lib/riffer/agent/run.rb +54 -16
- data/lib/riffer/agent/session.rb +2 -0
- data/lib/riffer/messages/assistant.rb +24 -2
- data/lib/riffer/messages/base.rb +11 -19
- data/lib/riffer/providers/amazon_bedrock.rb +3 -0
- data/lib/riffer/providers/anthropic.rb +9 -0
- data/lib/riffer/providers/base.rb +1 -0
- data/lib/riffer/providers/finish_reason.rb +1 -1
- data/lib/riffer/providers/gemini.rb +10 -1
- data/lib/riffer/providers/open_ai.rb +30 -23
- data/lib/riffer/providers/open_router.rb +21 -7
- data/lib/riffer/runner/fibers.rb +12 -8
- data/lib/riffer/version.rb +1 -1
- data/sig/_private/async.rbs +4 -0
- data/sig/generated/riffer/agent/outcome.rbs +41 -0
- data/sig/generated/riffer/agent/response.rbs +11 -26
- data/sig/generated/riffer/agent/run.rbs +18 -7
- data/sig/generated/riffer/messages/assistant.rbs +14 -2
- data/sig/generated/riffer/providers/open_ai.rbs +8 -3
- data/sig/generated/riffer/providers/open_router.rbs +10 -2
- data/sig/generated/riffer/runner/fibers.rbs +2 -0
- metadata +3 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/outcome.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# How a run ended — the single place to read whether the agent completed
|
|
4
|
+
# normally and, if not, why. +detail+ carries the specifics when there are any:
|
|
5
|
+
# the tripwire reason, the interrupt reason, the provider's raw finish value,
|
|
6
|
+
# or the structured output parse/validation error.
|
|
7
|
+
#
|
|
8
|
+
# response = agent.generate("Analyze this")
|
|
9
|
+
# case response.outcome.reason
|
|
10
|
+
# when :completed then puts response.structured_output
|
|
11
|
+
# when :invalid_structured_output then warn response.outcome.detail
|
|
12
|
+
# end
|
|
13
|
+
class Riffer::Agent::Outcome
|
|
14
|
+
# Finish reasons that end a turn normally; every other finish reason means the
|
|
15
|
+
# provider cut the turn short and surfaces as the run's outcome verbatim.
|
|
16
|
+
NORMAL_FINISH_REASONS: Array[Symbol]
|
|
17
|
+
|
|
18
|
+
# Derived from the provider vocabulary so a new finish reason becomes an
|
|
19
|
+
# outcome without a second list to update.
|
|
20
|
+
PROVIDER_STOP_REASONS: Array[Symbol]
|
|
21
|
+
|
|
22
|
+
# The vocabulary every run ends in.
|
|
23
|
+
VALUES: Array[Symbol]
|
|
24
|
+
|
|
25
|
+
# Why the run ended.
|
|
26
|
+
attr_reader reason: Symbol
|
|
27
|
+
|
|
28
|
+
# Human-readable specifics for +reason+, when there are any.
|
|
29
|
+
attr_reader detail: String?
|
|
30
|
+
|
|
31
|
+
# Raises Riffer::ArgumentError when +reason+ is outside VALUES.
|
|
32
|
+
# --
|
|
33
|
+
# : (reason: Symbol, ?detail: String?) -> void
|
|
34
|
+
def initialize: (reason: Symbol, ?detail: String?) -> void
|
|
35
|
+
|
|
36
|
+
# Returns true when the run completed normally.
|
|
37
|
+
#
|
|
38
|
+
# --
|
|
39
|
+
# : () -> bool
|
|
40
|
+
def success?: () -> bool
|
|
41
|
+
end
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
# Generated from lib/riffer/agent/response.rb with RBS::Inline
|
|
2
2
|
|
|
3
|
-
# Wraps an agent generation response.
|
|
4
|
-
# +content+ is empty and +tripwire+ carries the
|
|
3
|
+
# Wraps an agent generation response. +outcome+ says how the run ended; when a
|
|
4
|
+
# guardrail blocks execution, +content+ is empty and +tripwire+ carries the
|
|
5
|
+
# block details.
|
|
5
6
|
#
|
|
6
7
|
# response = agent.generate("Hello")
|
|
7
|
-
# if response.
|
|
8
|
-
# puts "Blocked: #{response.tripwire.reason}"
|
|
9
|
-
# else
|
|
8
|
+
# if response.outcome.success?
|
|
10
9
|
# puts response.content
|
|
10
|
+
# else
|
|
11
|
+
# puts "#{response.outcome.reason}: #{response.outcome.detail}"
|
|
11
12
|
# end
|
|
12
13
|
class Riffer::Agent::Response
|
|
13
|
-
@interrupted: bool
|
|
14
|
-
|
|
15
14
|
# The response content.
|
|
16
15
|
attr_reader content: String
|
|
17
16
|
|
|
17
|
+
# How the run ended.
|
|
18
|
+
attr_reader outcome: Riffer::Agent::Outcome
|
|
19
|
+
|
|
18
20
|
# The tripwire if execution was blocked.
|
|
19
21
|
attr_reader tripwire: Riffer::Guardrails::Tripwire?
|
|
20
22
|
|
|
21
23
|
# The modifications made by guardrails during processing.
|
|
22
24
|
attr_reader modifications: Array[Riffer::Guardrails::Modification]
|
|
23
25
|
|
|
24
|
-
# The reason provided with the interrupt, if any.
|
|
25
|
-
attr_reader interrupt_reason: (String | Symbol)?
|
|
26
|
-
|
|
27
26
|
# The parsed structured output, if structured output was configured.
|
|
28
27
|
attr_reader structured_output: Hash[Symbol, untyped]?
|
|
29
28
|
|
|
@@ -44,34 +43,20 @@ class Riffer::Agent::Response
|
|
|
44
43
|
# --
|
|
45
44
|
# : (
|
|
46
45
|
# String,
|
|
46
|
+
# outcome: Riffer::Agent::Outcome,
|
|
47
47
|
# ?tripwire: Riffer::Guardrails::Tripwire?,
|
|
48
48
|
# ?modifications: Array[Riffer::Guardrails::Modification],
|
|
49
|
-
# ?interrupted: bool,
|
|
50
|
-
# ?interrupt_reason: (String | Symbol)?,
|
|
51
49
|
# ?structured_output: Hash[Symbol, untyped]?,
|
|
52
50
|
# ?messages: Array[Riffer::Messages::Base],
|
|
53
51
|
# ?healed_tool_call_ids: Array[String],
|
|
54
52
|
# ?token_usage: Riffer::Providers::TokenUsage?,
|
|
55
53
|
# ?steps: Integer
|
|
56
54
|
# ) -> void
|
|
57
|
-
def initialize: (String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?
|
|
58
|
-
|
|
59
|
-
# Returns true if the response was blocked by a guardrail.
|
|
60
|
-
#
|
|
61
|
-
# --
|
|
62
|
-
# : () -> bool
|
|
63
|
-
def blocked?: () -> bool
|
|
55
|
+
def initialize: (String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?structured_output: Hash[Symbol, untyped]?, ?messages: Array[Riffer::Messages::Base], ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> void
|
|
64
56
|
|
|
65
57
|
# Returns true if any guardrail modified data during processing.
|
|
66
58
|
#
|
|
67
59
|
# --
|
|
68
60
|
# : () -> bool
|
|
69
61
|
def modified?: () -> bool
|
|
70
|
-
|
|
71
|
-
# Returns true if the agent loop was interrupted by a callback
|
|
72
|
-
# via <tt>throw :riffer_interrupt</tt>.
|
|
73
|
-
#
|
|
74
|
-
# --
|
|
75
|
-
# : () -> bool
|
|
76
|
-
def interrupted?: () -> bool
|
|
77
62
|
end
|
|
@@ -45,8 +45,16 @@ module Riffer::Agent::Run
|
|
|
45
45
|
def tripwire_response: (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> Riffer::Agent::Response
|
|
46
46
|
|
|
47
47
|
# --
|
|
48
|
-
# : (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
|
|
49
|
-
def final_response: (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
|
|
48
|
+
# : (Riffer::Agent, Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, **untyped) -> Riffer::Agent::Response
|
|
49
|
+
def final_response: (Riffer::Agent, Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, **untyped) -> Riffer::Agent::Response
|
|
50
|
+
|
|
51
|
+
# Checked in the order things happened. The loop being stopped (max_steps or
|
|
52
|
+
# an interrupt) beats the provider's finish reason, which beats riffer's own
|
|
53
|
+
# validation of the content. A truncated response that also fails the schema
|
|
54
|
+
# therefore reports :length, not :invalid_structured_output.
|
|
55
|
+
# --
|
|
56
|
+
# : (Riffer::Messages::Assistant?, Riffer::Agent::StructuredOutput::Result?, interrupted: bool, interrupt_reason: (String | Symbol)?) -> Riffer::Agent::Outcome
|
|
57
|
+
def final_outcome: (Riffer::Messages::Assistant?, Riffer::Agent::StructuredOutput::Result?, interrupted: bool, interrupt_reason: (String | Symbol)?) -> Riffer::Agent::Outcome
|
|
50
58
|
|
|
51
59
|
# --
|
|
52
60
|
# : (Riffer::Agent, ?Hash[String, String]) -> Riffer::Messages::Assistant
|
|
@@ -77,8 +85,8 @@ module Riffer::Agent::Run
|
|
|
77
85
|
def run_after_guardrails: (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], ?Hash[String, String]) { (Riffer::Guardrails::Tripwire) -> void } -> untyped
|
|
78
86
|
|
|
79
87
|
# --
|
|
80
|
-
# : (Riffer::Agent, Riffer::Messages::Assistant?) ->
|
|
81
|
-
def
|
|
88
|
+
# : (Riffer::Agent, Riffer::Messages::Assistant?) -> Riffer::Agent::StructuredOutput::Result?
|
|
89
|
+
def structured_output_result: (Riffer::Agent, Riffer::Messages::Assistant?) -> Riffer::Agent::StructuredOutput::Result?
|
|
82
90
|
|
|
83
91
|
# --
|
|
84
92
|
# : (Riffer::Agent) -> Array[singleton(Riffer::Tool)]
|
|
@@ -96,16 +104,15 @@ module Riffer::Agent::Run
|
|
|
96
104
|
# : (
|
|
97
105
|
# Riffer::Agent,
|
|
98
106
|
# String,
|
|
107
|
+
# outcome: Riffer::Agent::Outcome,
|
|
99
108
|
# ?tripwire: Riffer::Guardrails::Tripwire?,
|
|
100
109
|
# ?modifications: Array[Riffer::Guardrails::Modification],
|
|
101
|
-
# ?interrupted: bool,
|
|
102
|
-
# ?interrupt_reason: (String | Symbol)?,
|
|
103
110
|
# ?structured_output: Hash[Symbol, untyped]?,
|
|
104
111
|
# ?healed_tool_call_ids: Array[String],
|
|
105
112
|
# ?token_usage: Riffer::Providers::TokenUsage?,
|
|
106
113
|
# ?steps: Integer
|
|
107
114
|
# ) -> Riffer::Agent::Response
|
|
108
|
-
def build_response: (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?
|
|
115
|
+
def build_response: (Riffer::Agent, String, outcome: Riffer::Agent::Outcome, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String], ?token_usage: Riffer::Providers::TokenUsage?, ?steps: Integer) -> Riffer::Agent::Response
|
|
109
116
|
|
|
110
117
|
# Raises when +files+ are supplied without a +prompt+ — the provider needs
|
|
111
118
|
# text to anchor the attachments.
|
|
@@ -136,4 +143,8 @@ module Riffer::Agent::Run
|
|
|
136
143
|
# --
|
|
137
144
|
# : (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Riffer::Agent::Response) -> void
|
|
138
145
|
def record_run_outcome: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Riffer::Agent::Response) -> void
|
|
146
|
+
|
|
147
|
+
# --
|
|
148
|
+
# : (Riffer::Agent::Outcome) -> String?
|
|
149
|
+
def interrupt_reason_attribute: (Riffer::Agent::Outcome) -> String?
|
|
139
150
|
end
|
|
@@ -27,11 +27,23 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
27
27
|
# <tt>Riffer::Providers::FinishReason::VALUES</tt>).
|
|
28
28
|
attr_reader finish_reason: Symbol?
|
|
29
29
|
|
|
30
|
+
# The provider's raw finish-reason value behind +finish_reason+, when one
|
|
31
|
+
# exists on the wire.
|
|
32
|
+
attr_reader finish_reason_raw: String?
|
|
33
|
+
|
|
30
34
|
# Raises Riffer::ArgumentError when +finish_reason+ is outside the
|
|
31
35
|
# normalized vocabulary.
|
|
32
36
|
# --
|
|
33
|
-
# : (
|
|
34
|
-
|
|
37
|
+
# : (
|
|
38
|
+
# String,
|
|
39
|
+
# ?id: String?,
|
|
40
|
+
# ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall],
|
|
41
|
+
# ?token_usage: Riffer::Providers::TokenUsage?,
|
|
42
|
+
# ?structured_output: Hash[Symbol, untyped]?,
|
|
43
|
+
# ?finish_reason: Symbol?,
|
|
44
|
+
# ?finish_reason_raw: String?
|
|
45
|
+
# ) -> void
|
|
46
|
+
def initialize: (String, ?id: String?, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall], ?token_usage: Riffer::Providers::TokenUsage?, ?structured_output: Hash[Symbol, untyped]?, ?finish_reason: Symbol?, ?finish_reason_raw: String?) -> void
|
|
35
47
|
|
|
36
48
|
# --
|
|
37
49
|
# : () -> Symbol
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
class Riffer::Providers::OpenAI < Riffer::Providers::Base
|
|
5
5
|
WEB_SEARCH_TOOL_TYPE: String
|
|
6
6
|
|
|
7
|
+
# The Responses API has no finish_reason field. The response +status+ is
|
|
8
|
+
# the primary signal; an +incomplete+ status is only meaningful together
|
|
9
|
+
# with <tt>incomplete_details.reason</tt>, so that branch nests one level.
|
|
10
|
+
FINISH_REASONS: Hash[String, Symbol | Hash[String, Symbol]]
|
|
11
|
+
|
|
7
12
|
# The GenAI semconv well-known provider name.
|
|
8
13
|
# --
|
|
9
14
|
# : () -> String
|
|
@@ -50,14 +55,14 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
|
|
|
50
55
|
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
51
56
|
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
52
57
|
|
|
53
|
-
# The Responses API reports no finish_reason field, so one is derived.
|
|
54
58
|
# --
|
|
55
59
|
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
56
60
|
def build_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
57
61
|
|
|
62
|
+
# The nested field that names the cause behind an ambiguous status.
|
|
58
63
|
# --
|
|
59
|
-
# : (untyped) ->
|
|
60
|
-
def
|
|
64
|
+
# : (untyped, String) -> String?
|
|
65
|
+
def finish_detail: (untyped, String) -> String?
|
|
61
66
|
|
|
62
67
|
# --
|
|
63
68
|
# : (untyped) -> String
|
|
@@ -57,9 +57,17 @@ class Riffer::Providers::OpenRouter < Riffer::Providers::Base
|
|
|
57
57
|
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
58
58
|
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
59
59
|
|
|
60
|
+
# +native+ is the upstream model's own finish reason, which OpenRouter
|
|
61
|
+
# reports alongside its normalized one; it wins as +raw+ when present.
|
|
60
62
|
# --
|
|
61
|
-
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
62
|
-
def build_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
63
|
+
# : (untyped, ?native: untyped) -> Riffer::Providers::FinishReason?
|
|
64
|
+
def build_finish_reason: (untyped, ?native: untyped) -> Riffer::Providers::FinishReason?
|
|
65
|
+
|
|
66
|
+
# +native_finish_reason+ is outside the OpenAI schema, so it is only
|
|
67
|
+
# reachable through the SDK model's raw data hash.
|
|
68
|
+
# --
|
|
69
|
+
# : (untyped) -> untyped
|
|
70
|
+
def native_finish_reason: (untyped) -> untyped
|
|
63
71
|
|
|
64
72
|
# --
|
|
65
73
|
# : (untyped) -> String
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# +max_concurrency+ caps simultaneous fibers via an <tt>Async::Semaphore</tt>.
|
|
5
5
|
# If multiple fibers raise, only the first exception is re-raised after all
|
|
6
6
|
# finish.
|
|
7
|
+
# Joins the current reactor task when one is already running, and otherwise
|
|
8
|
+
# starts its own.
|
|
7
9
|
class Riffer::Runner::Fibers < Riffer::Runner
|
|
8
10
|
@max_concurrency: Integer?
|
|
9
11
|
|
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.46.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jake Bottrall
|
|
@@ -110,6 +110,7 @@ files:
|
|
|
110
110
|
- lib/riffer/agent.rb
|
|
111
111
|
- lib/riffer/agent/config.rb
|
|
112
112
|
- lib/riffer/agent/context.rb
|
|
113
|
+
- lib/riffer/agent/outcome.rb
|
|
113
114
|
- lib/riffer/agent/response.rb
|
|
114
115
|
- lib/riffer/agent/run.rb
|
|
115
116
|
- lib/riffer/agent/serializer.rb
|
|
@@ -239,6 +240,7 @@ files:
|
|
|
239
240
|
- sig/generated/riffer/agent.rbs
|
|
240
241
|
- sig/generated/riffer/agent/config.rbs
|
|
241
242
|
- sig/generated/riffer/agent/context.rbs
|
|
243
|
+
- sig/generated/riffer/agent/outcome.rbs
|
|
242
244
|
- sig/generated/riffer/agent/response.rbs
|
|
243
245
|
- sig/generated/riffer/agent/run.rbs
|
|
244
246
|
- sig/generated/riffer/agent/serializer.rbs
|