riffer 0.45.0 → 0.46.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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +25 -0
- data/docs/AGENTS.md +13 -0
- data/docs/AGENT_LIFECYCLE.md +47 -14
- data/docs/AGENT_LOOP.md +8 -9
- data/docs/CONFIGURATION.md +24 -17
- data/docs/GUARDRAILS.md +4 -4
- data/docs/MESSAGES.md +4 -3
- data/docs/STREAM_EVENTS.md +1 -1
- data/docs/TOOLS.md +2 -0
- data/docs/TOOL_ADVANCED.md +2 -0
- data/docs/TRACING.md +3 -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 +55 -17
- data/lib/riffer/agent/session.rb +2 -0
- data/lib/riffer/agent.rb +5 -4
- data/lib/riffer/messages/assistant.rb +24 -2
- data/lib/riffer/messages/base.rb +11 -19
- data/lib/riffer/params/param.rb +2 -0
- data/lib/riffer/params.rb +30 -6
- data/lib/riffer/providers/anthropic.rb +5 -0
- data/lib/riffer/providers/base.rb +1 -0
- 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 +20 -9
- data/sig/generated/riffer/agent.rbs +7 -6
- data/sig/generated/riffer/messages/assistant.rbs +14 -2
- data/sig/generated/riffer/params.rbs +16 -2
- data/sig/generated/riffer/runner/fibers.rbs +2 -0
- metadata +3 -1
|
@@ -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
|
|
@@ -14,8 +14,8 @@ module Riffer::Agent::Run
|
|
|
14
14
|
# for prompt/files semantics.
|
|
15
15
|
#
|
|
16
16
|
# --
|
|
17
|
-
# : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
18
|
-
def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
17
|
+
# : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
18
|
+
def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
@@ -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
|
|
@@ -113,8 +113,8 @@ class Riffer::Agent
|
|
|
113
113
|
|
|
114
114
|
# Streams a response using a new agent instance.
|
|
115
115
|
# --
|
|
116
|
-
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
117
|
-
def self.stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
116
|
+
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
117
|
+
def self.stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?context: Hash[Symbol, untyped]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
118
118
|
|
|
119
119
|
# Reconstructs a runnable agent from a wire hash produced by +#to_h+.
|
|
120
120
|
# --
|
|
@@ -208,14 +208,15 @@ class Riffer::Agent
|
|
|
208
208
|
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Riffer::Agent::Response
|
|
209
209
|
def generate: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Riffer::Agent::Response
|
|
210
210
|
|
|
211
|
-
# Streams a response from the agent
|
|
212
|
-
# +Riffer::StreamEvents
|
|
211
|
+
# Streams a response from the agent as an +Enumerator+ of
|
|
212
|
+
# +Riffer::StreamEvents+ whose block-form +each+ returns the final
|
|
213
|
+
# Riffer::Agent::Response. See +#generate+ for prompt/files/tags semantics.
|
|
213
214
|
#
|
|
214
215
|
# Raises Riffer::ArgumentError if structured output is configured.
|
|
215
216
|
#
|
|
216
217
|
# --
|
|
217
|
-
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
218
|
-
def stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base,
|
|
218
|
+
# : (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[(String | Symbol), untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
219
|
+
def stream: (?String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, ?tags: Hash[String | Symbol, untyped]) -> Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
219
220
|
|
|
220
221
|
# Interrupts the agent loop from an +on_message+ callback. Equivalent to
|
|
221
222
|
# <tt>throw :riffer_interrupt, reason</tt>.
|
|
@@ -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
|
|
@@ -40,6 +40,11 @@ class Riffer::Params
|
|
|
40
40
|
|
|
41
41
|
# Validates arguments against parameter definitions.
|
|
42
42
|
#
|
|
43
|
+
# A Float param accepts an Integer (JSON Schema <tt>"number"</tt> covers
|
|
44
|
+
# integers) and its value is coerced with +to_f+, so callers always get a
|
|
45
|
+
# Float. The same holds for the items of an <tt>of: Float</tt> array. No other
|
|
46
|
+
# type is coerced.
|
|
47
|
+
#
|
|
43
48
|
# Raises Riffer::ValidationError if validation fails.
|
|
44
49
|
#
|
|
45
50
|
# --
|
|
@@ -71,7 +76,16 @@ class Riffer::Params
|
|
|
71
76
|
# : (Riffer::Params::Param, Array[untyped], Array[String]) -> Array[untyped]
|
|
72
77
|
def validate_nested_array_of_objects: (Riffer::Params::Param, Array[untyped], Array[String]) -> Array[untyped]
|
|
73
78
|
|
|
79
|
+
# Returns the array with its valid items coerced by +coerce_value+.
|
|
80
|
+
# --
|
|
81
|
+
# : (Riffer::Params::Param, Array[untyped], Array[String]) -> Array[untyped]
|
|
82
|
+
def validate_typed_array: (Riffer::Params::Param, Array[untyped], Array[String]) -> Array[untyped]
|
|
83
|
+
|
|
84
|
+
# Coerces an already-validated value to the Ruby type its param declares.
|
|
85
|
+
# Only Float coerces today, because JSON Schema "number" accepts integers and
|
|
86
|
+
# callers should not get a type that depends on whether the model wrote a
|
|
87
|
+
# decimal point. Add a branch here rather than inline at a call site.
|
|
74
88
|
# --
|
|
75
|
-
# : (
|
|
76
|
-
def
|
|
89
|
+
# : (Module, untyped) -> untyped
|
|
90
|
+
def coerce_value: (Module, untyped) -> untyped
|
|
77
91
|
end
|
|
@@ -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.1
|
|
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
|