riffer 0.28.0 → 0.29.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/.agents/architecture.md +18 -11
- data/.agents/code-style.md +1 -1
- data/.agents/rbs-inline.md +2 -2
- data/.agents/testing.md +9 -5
- data/.release-please-manifest.json +1 -1
- data/AGENTS.md +17 -10
- data/CHANGELOG.md +19 -0
- data/README.md +17 -18
- data/Steepfile +7 -1
- data/docs/03_AGENTS.md +34 -3
- data/docs/04_AGENT_LIFECYCLE.md +87 -86
- data/docs/05_AGENT_LOOP.md +2 -2
- data/docs/06_TOOLS.md +9 -4
- data/docs/07_TOOL_ADVANCED.md +17 -17
- data/docs/08_MESSAGES.md +25 -32
- data/docs/09_STREAM_EVENTS.md +1 -1
- data/docs/10_CONFIGURATION.md +7 -18
- data/docs/providers/01_PROVIDERS.md +6 -0
- data/docs/providers/06_MOCK_PROVIDER.md +2 -1
- data/docs/providers/07_CUSTOM_PROVIDERS.md +4 -4
- data/docs/providers/08_GEMINI.md +2 -2
- data/docs/providers/09_OPENROUTER.md +242 -0
- data/lib/riffer/agent/config.rb +173 -0
- data/lib/riffer/agent/context.rb +125 -0
- data/lib/riffer/agent/run.rb +308 -0
- data/lib/riffer/agent/session/repair.rb +112 -0
- data/lib/riffer/agent/session.rb +268 -0
- data/lib/riffer/{structured_output → agent/structured_output}/result.rb +1 -1
- data/lib/riffer/{structured_output.rb → agent/structured_output.rb} +4 -4
- data/lib/riffer/agent.rb +234 -923
- data/lib/riffer/config.rb +14 -7
- data/lib/riffer/evals/evaluator.rb +13 -3
- data/lib/riffer/evals/judge.rb +2 -2
- data/lib/riffer/evals/run_result.rb +2 -1
- data/lib/riffer/evals/scenario_result.rb +2 -1
- data/lib/riffer/guardrails/runner.rb +3 -2
- data/lib/riffer/helpers/call_or_value.rb +16 -0
- data/lib/riffer/helpers.rb +0 -1
- data/lib/riffer/mcp/authenticated_tool.rb +4 -0
- data/lib/riffer/mcp/client.rb +1 -1
- data/lib/riffer/mcp/registration.rb +2 -3
- data/lib/riffer/mcp/registry.rb +3 -1
- data/lib/riffer/mcp/tool_factory.rb +5 -0
- data/lib/riffer/messages/assistant.rb +9 -3
- data/lib/riffer/messages/base.rb +22 -0
- data/lib/riffer/messages/converter.rb +6 -6
- data/lib/riffer/{file_part.rb → messages/file_part.rb} +5 -5
- data/lib/riffer/messages/tool.rb +1 -1
- data/lib/riffer/messages/user.rb +4 -4
- data/lib/riffer/{boolean.rb → params/boolean.rb} +3 -3
- data/lib/riffer/{param.rb → params/param.rb} +6 -6
- data/lib/riffer/params.rb +27 -21
- data/lib/riffer/providers/amazon_bedrock.rb +19 -20
- data/lib/riffer/providers/anthropic.rb +27 -28
- data/lib/riffer/providers/base.rb +10 -9
- data/lib/riffer/providers/gemini.rb +15 -12
- data/lib/riffer/providers/mock.rb +41 -13
- data/lib/riffer/providers/open_ai.rb +24 -22
- data/lib/riffer/providers/open_router.rb +318 -0
- data/lib/riffer/providers/repository.rb +1 -0
- data/lib/riffer/{token_usage.rb → providers/token_usage.rb} +4 -4
- data/lib/riffer/providers.rb +1 -0
- data/lib/riffer/runner/fibers.rb +4 -3
- data/lib/riffer/runner/sequential.rb +1 -1
- data/lib/riffer/runner/threaded.rb +1 -1
- data/lib/riffer/runner.rb +1 -1
- data/lib/riffer/skills/activate_tool.rb +4 -3
- data/lib/riffer/skills/config.rb +1 -1
- data/lib/riffer/skills/context.rb +3 -3
- data/lib/riffer/skills/filesystem_backend.rb +7 -5
- data/lib/riffer/skills/markdown_adapter.rb +1 -1
- data/lib/riffer/skills/xml_adapter.rb +1 -1
- data/lib/riffer/stream_events/interrupt.rb +1 -1
- data/lib/riffer/stream_events/token_usage_done.rb +2 -2
- data/lib/riffer/stream_events/web_search_status.rb +1 -1
- data/lib/riffer/tool.rb +3 -3
- data/lib/riffer/{tool_runtime → tools/runtime}/fibers.rb +2 -2
- data/lib/riffer/{tool_runtime → tools/runtime}/inline.rb +1 -1
- data/lib/riffer/{tool_runtime → tools/runtime}/threaded.rb +2 -2
- data/lib/riffer/{tool_runtime.rb → tools/runtime.rb} +9 -9
- data/lib/riffer/{toolable.rb → tools/toolable.rb} +12 -9
- data/lib/riffer/version.rb +1 -1
- data/lib/riffer.rb +2 -1
- data/sig/generated/riffer/agent/config.rbs +119 -0
- data/sig/generated/riffer/agent/context.rbs +91 -0
- data/sig/generated/riffer/agent/run.rbs +144 -0
- data/sig/generated/riffer/agent/session/repair.rbs +51 -0
- data/sig/generated/riffer/agent/session.rbs +145 -0
- data/sig/generated/riffer/{structured_output → agent/structured_output}/result.rbs +2 -2
- data/sig/generated/riffer/{structured_output.rbs → agent/structured_output.rbs} +6 -6
- data/sig/generated/riffer/agent.rbs +143 -342
- data/sig/generated/riffer/config.rbs +17 -5
- data/sig/generated/riffer/evals/judge.rbs +2 -2
- data/sig/generated/riffer/helpers/call_or_value.rbs +9 -0
- data/sig/generated/riffer/helpers.rbs +0 -1
- data/sig/generated/riffer/messages/assistant.rbs +7 -3
- data/sig/generated/riffer/messages/base.rbs +18 -0
- data/sig/generated/riffer/messages/converter.rbs +4 -4
- data/sig/generated/riffer/{file_part.rbs → messages/file_part.rbs} +5 -5
- data/sig/generated/riffer/messages/user.rbs +4 -4
- data/sig/generated/riffer/params/boolean.rbs +10 -0
- data/sig/generated/riffer/{param.rbs → params/param.rbs} +3 -3
- data/sig/generated/riffer/params.rbs +15 -15
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +22 -22
- data/sig/generated/riffer/providers/anthropic.rbs +4 -4
- data/sig/generated/riffer/providers/base.rbs +10 -10
- data/sig/generated/riffer/providers/gemini.rbs +4 -4
- data/sig/generated/riffer/providers/mock.rbs +25 -5
- data/sig/generated/riffer/providers/open_ai.rbs +4 -4
- data/sig/generated/riffer/providers/open_router.rbs +85 -0
- data/sig/generated/riffer/{token_usage.rbs → providers/token_usage.rbs} +5 -5
- data/sig/generated/riffer/providers.rbs +1 -0
- data/sig/generated/riffer/runner/fibers.rbs +2 -2
- data/sig/generated/riffer/runner/sequential.rbs +2 -2
- data/sig/generated/riffer/runner/threaded.rbs +2 -2
- data/sig/generated/riffer/runner.rbs +2 -2
- data/sig/generated/riffer/skills/activate_tool.rbs +4 -3
- data/sig/generated/riffer/skills/config.rbs +1 -1
- data/sig/generated/riffer/skills/context.rbs +2 -2
- data/sig/generated/riffer/stream_events/token_usage_done.rbs +3 -3
- data/sig/generated/riffer/tool.rbs +5 -5
- data/sig/generated/riffer/{tool_runtime → tools/runtime}/fibers.rbs +3 -3
- data/sig/generated/riffer/{tool_runtime → tools/runtime}/inline.rbs +2 -2
- data/sig/generated/riffer/{tool_runtime → tools/runtime}/threaded.rbs +3 -3
- data/sig/generated/riffer/{tool_runtime.rbs → tools/runtime.rbs} +12 -12
- data/sig/generated/riffer/{toolable.rbs → tools/toolable.rbs} +6 -6
- data/sig/stubs/agent_ivars.rbs +7 -0
- data/sig/stubs/async.rbs +24 -0
- data/sig/stubs/aws-sdk-core/seahorse_request_context.rbs +7 -0
- data/sig/stubs/aws-sdk-core/static_token_provider.rbs +5 -0
- data/sig/stubs/extend_self.rbs +11 -0
- data/sig/stubs/lib_ivars.rbs +101 -0
- data/sig/stubs/mcp_sdk.rbs +22 -0
- data/sig/stubs/provider_ivars.rbs +36 -0
- data/sig/stubs/provider_sdk_methods.rbs +50 -0
- data/sig/stubs/zeitwerk.rbs +12 -0
- metadata +54 -33
- data/lib/riffer/core.rb +0 -28
- data/lib/riffer/helpers/validations.rb +0 -18
- data/sig/generated/riffer/boolean.rbs +0 -10
- data/sig/generated/riffer/core.rbs +0 -19
- data/sig/generated/riffer/helpers/validations.rbs +0 -12
data/lib/riffer/tool.rb
CHANGED
|
@@ -24,7 +24,7 @@ require "timeout"
|
|
|
24
24
|
# end
|
|
25
25
|
#
|
|
26
26
|
class Riffer::Tool
|
|
27
|
-
extend Riffer::Toolable
|
|
27
|
+
extend Riffer::Tools::Toolable
|
|
28
28
|
|
|
29
29
|
kind :tool
|
|
30
30
|
|
|
@@ -33,7 +33,7 @@ class Riffer::Tool
|
|
|
33
33
|
# Raises NotImplementedError if not implemented by subclass.
|
|
34
34
|
#
|
|
35
35
|
#--
|
|
36
|
-
#: (context:
|
|
36
|
+
#: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
37
37
|
def call(context:, **kwargs)
|
|
38
38
|
raise NotImplementedError, "#{self.class} must implement #call"
|
|
39
39
|
end
|
|
@@ -69,7 +69,7 @@ class Riffer::Tool
|
|
|
69
69
|
# Raises Riffer::Error if the tool does not return a Response object.
|
|
70
70
|
#
|
|
71
71
|
#--
|
|
72
|
-
#: (context:
|
|
72
|
+
#: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
73
73
|
def call_with_validation(context:, **kwargs)
|
|
74
74
|
params_builder = self.class.params
|
|
75
75
|
validated_args = params_builder ? params_builder.validate(kwargs) : kwargs
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
# Executes tool calls concurrently using fibers via the +async+ gem.
|
|
5
5
|
#
|
|
6
6
|
# class MyAgent < Riffer::Agent
|
|
7
|
-
# tool_runtime Riffer::
|
|
7
|
+
# tool_runtime Riffer::Tools::Runtime::Fibers
|
|
8
8
|
# end
|
|
9
9
|
#
|
|
10
|
-
class Riffer::
|
|
10
|
+
class Riffer::Tools::Runtime::Fibers < Riffer::Tools::Runtime
|
|
11
11
|
# [max_concurrency] maximum number of tool calls to execute simultaneously.
|
|
12
12
|
# When +nil+, all tool calls run as fibers without limit.
|
|
13
13
|
#
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
# Executes tool calls concurrently using threads.
|
|
5
5
|
#
|
|
6
6
|
# class MyAgent < Riffer::Agent
|
|
7
|
-
# tool_runtime Riffer::
|
|
7
|
+
# tool_runtime Riffer::Tools::Runtime::Threaded
|
|
8
8
|
# end
|
|
9
9
|
#
|
|
10
|
-
class Riffer::
|
|
10
|
+
class Riffer::Tools::Runtime::Threaded < Riffer::Tools::Runtime
|
|
11
11
|
DEFAULT_MAX_CONCURRENCY = 5 #: Integer
|
|
12
12
|
|
|
13
13
|
# [max_concurrency] maximum number of tool calls to execute simultaneously.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
|
-
# Riffer::
|
|
6
|
+
# Riffer::Tools::Runtime handles tool call execution for an agent.
|
|
7
7
|
#
|
|
8
8
|
# Composes with a Riffer::Runner for concurrency control and provides
|
|
9
9
|
# +execute+ as the sole public entry point.
|
|
@@ -11,19 +11,19 @@ require "json"
|
|
|
11
11
|
# Subclass and override +dispatch_tool_call+ to customize how individual
|
|
12
12
|
# tool calls are dispatched (e.g., HTTP, gRPC).
|
|
13
13
|
#
|
|
14
|
-
# runtime = Riffer::
|
|
14
|
+
# runtime = Riffer::Tools::Runtime::Inline.new
|
|
15
15
|
# results = runtime.execute(tool_calls, tools: tools, context: context)
|
|
16
16
|
#
|
|
17
|
-
class Riffer::
|
|
17
|
+
class Riffer::Tools::Runtime
|
|
18
18
|
# [runner] the concurrency runner to use for batch execution.
|
|
19
19
|
#
|
|
20
|
-
# Subclasses must provide a runner; instantiating
|
|
20
|
+
# Subclasses must provide a runner; instantiating Riffer::Tools::Runtime directly
|
|
21
21
|
# raises +NotImplementedError+.
|
|
22
22
|
#
|
|
23
23
|
#--
|
|
24
24
|
#: (runner: Riffer::Runner) -> void
|
|
25
25
|
def initialize(runner:)
|
|
26
|
-
raise NotImplementedError, "#{self.class} is abstract — use a subclass like Riffer::
|
|
26
|
+
raise NotImplementedError, "#{self.class} is abstract — use a subclass like Riffer::Tools::Runtime::Inline" if instance_of?(Riffer::Tools::Runtime)
|
|
27
27
|
@runner = runner
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -38,7 +38,7 @@ class Riffer::ToolRuntime
|
|
|
38
38
|
# instrumentation that needs the accompanying assistant text).
|
|
39
39
|
#
|
|
40
40
|
#--
|
|
41
|
-
#: (Array[Riffer::Messages::Assistant::ToolCall], tools: Array[singleton(Riffer::Tool)], context:
|
|
41
|
+
#: (Array[Riffer::Messages::Assistant::ToolCall], tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Array[[Riffer::Messages::Assistant::ToolCall, Riffer::Tools::Response]]
|
|
42
42
|
def execute(tool_calls, tools:, context:, assistant_message: nil)
|
|
43
43
|
@runner.map(tool_calls, context: context) do |tool_call|
|
|
44
44
|
result = around_tool_call(tool_call, context: context, assistant_message: assistant_message) do
|
|
@@ -53,7 +53,7 @@ class Riffer::ToolRuntime
|
|
|
53
53
|
#
|
|
54
54
|
# The default implementation simply yields.
|
|
55
55
|
#
|
|
56
|
-
# class InstrumentedRuntime < Riffer::
|
|
56
|
+
# class InstrumentedRuntime < Riffer::Tools::Runtime::Inline
|
|
57
57
|
# private
|
|
58
58
|
#
|
|
59
59
|
# def around_tool_call(tool_call, context:, assistant_message: nil)
|
|
@@ -65,7 +65,7 @@ class Riffer::ToolRuntime
|
|
|
65
65
|
# end
|
|
66
66
|
#
|
|
67
67
|
#--
|
|
68
|
-
#: (Riffer::Messages::Assistant::ToolCall, context:
|
|
68
|
+
#: (Riffer::Messages::Assistant::ToolCall, context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) { () -> Riffer::Tools::Response } -> Riffer::Tools::Response
|
|
69
69
|
def around_tool_call(tool_call, context:, assistant_message: nil)
|
|
70
70
|
yield
|
|
71
71
|
end
|
|
@@ -82,7 +82,7 @@ class Riffer::ToolRuntime
|
|
|
82
82
|
# call, when known.
|
|
83
83
|
#
|
|
84
84
|
#--
|
|
85
|
-
#: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context:
|
|
85
|
+
#: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
|
|
86
86
|
def dispatch_tool_call(tool_call, tools:, context:, assistant_message: nil)
|
|
87
87
|
tool_class = tools.find { |tc| tc.name == tool_call.name }
|
|
88
88
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Riffer::Toolable provides the shared class-level DSL for anything that can
|
|
4
|
+
# Riffer::Tools::Toolable provides the shared class-level DSL for anything that can
|
|
5
5
|
# present as a tool to an LLM — tools today, and subagents/workflows in the
|
|
6
6
|
# future.
|
|
7
7
|
#
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# are NOT part of Toolable — those belong on Riffer::Tool.
|
|
14
14
|
#
|
|
15
15
|
# class MyTool
|
|
16
|
-
# extend Riffer::Toolable
|
|
16
|
+
# extend Riffer::Tools::Toolable
|
|
17
17
|
#
|
|
18
18
|
# description "Does something useful"
|
|
19
19
|
#
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
# end
|
|
23
23
|
# end
|
|
24
24
|
#
|
|
25
|
-
module Riffer::Toolable
|
|
25
|
+
module Riffer::Tools::Toolable
|
|
26
26
|
DEFAULT_TIMEOUT = 10 #: Integer
|
|
27
27
|
|
|
28
28
|
# Tracks all classes that extend Toolable.
|
|
@@ -31,8 +31,8 @@ module Riffer::Toolable
|
|
|
31
31
|
#: (Module) -> void
|
|
32
32
|
def self.extended(base)
|
|
33
33
|
base.extend Riffer::Helpers::ClassNameConverter
|
|
34
|
-
@extenders ||= []
|
|
35
|
-
|
|
34
|
+
extenders = (@extenders ||= []) #: Array[Module]
|
|
35
|
+
extenders << base
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Returns all classes that have extended Toolable.
|
|
@@ -82,11 +82,12 @@ module Riffer::Toolable
|
|
|
82
82
|
# Defines parameters using the Params DSL.
|
|
83
83
|
#
|
|
84
84
|
#--
|
|
85
|
-
#: () ?{ () -> void } -> Riffer::Params?
|
|
85
|
+
#: () ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
|
|
86
86
|
def params(&block)
|
|
87
87
|
return @params_builder if block.nil?
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
builder = Riffer::Params.new
|
|
89
|
+
builder.instance_eval(&block)
|
|
90
|
+
@params_builder = builder
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
# Returns the JSON Schema for the tool's parameters.
|
|
@@ -135,6 +136,8 @@ module Riffer::Toolable
|
|
|
135
136
|
private
|
|
136
137
|
|
|
137
138
|
def empty_schema # :nodoc:
|
|
138
|
-
|
|
139
|
+
properties = {} #: Hash[Symbol, untyped]
|
|
140
|
+
required = [] #: Array[untyped]
|
|
141
|
+
{type: "object", properties: properties, required: required, additionalProperties: false}
|
|
139
142
|
end
|
|
140
143
|
end
|
data/lib/riffer/version.rb
CHANGED
data/lib/riffer.rb
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/config.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Typed configuration object holding every class-level DSL setting on a
|
|
4
|
+
# Riffer::Agent subclass.
|
|
5
|
+
#
|
|
6
|
+
# Each subclass of Riffer::Agent owns one Config, accessible via the class
|
|
7
|
+
# method <tt>config</tt>. The class-level DSL (+model+, +instructions+, +uses_tools+,
|
|
8
|
+
# etc.) reads and mutates this Config in place. Append-style DSL methods
|
|
9
|
+
# (+use_mcp+, +guardrail+) are handled by the +add_mcp+ and +add_guardrail+
|
|
10
|
+
# helpers below.
|
|
11
|
+
#
|
|
12
|
+
# Config stores Procs unresolved. Per-instance resolution happens elsewhere
|
|
13
|
+
# (instructions, model, tools, tool runtime, skills).
|
|
14
|
+
class Riffer::Agent::Config
|
|
15
|
+
DEFAULT_MAX_STEPS: Integer
|
|
16
|
+
|
|
17
|
+
attr_reader identifier: String?
|
|
18
|
+
|
|
19
|
+
attr_reader model: (String | Proc)?
|
|
20
|
+
|
|
21
|
+
attr_reader instructions: (String | Proc)?
|
|
22
|
+
|
|
23
|
+
attr_accessor provider_options: Hash[Symbol, untyped]
|
|
24
|
+
|
|
25
|
+
attr_accessor model_options: Hash[Symbol, untyped]
|
|
26
|
+
|
|
27
|
+
attr_reader structured_output: Riffer::Params?
|
|
28
|
+
|
|
29
|
+
attr_accessor max_steps: Numeric
|
|
30
|
+
|
|
31
|
+
attr_accessor tools_config: (Array[singleton(Riffer::Tool)] | Proc)?
|
|
32
|
+
|
|
33
|
+
attr_reader mcp_configs: Array[Hash[Symbol, untyped]]
|
|
34
|
+
|
|
35
|
+
attr_reader tool_runtime: singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc
|
|
36
|
+
|
|
37
|
+
attr_accessor skills_config: Riffer::Skills::Config?
|
|
38
|
+
|
|
39
|
+
attr_reader guardrails: Hash[Symbol, Array[Hash[Symbol, untyped]]]
|
|
40
|
+
|
|
41
|
+
# Builds a new Config. All fields are optional; unset fields take the
|
|
42
|
+
# documented defaults.
|
|
43
|
+
#
|
|
44
|
+
# Raises Riffer::ArgumentError if +model+ or +instructions+ is provided
|
|
45
|
+
# as a non-String, non-Proc value (or as an empty String).
|
|
46
|
+
#
|
|
47
|
+
# --
|
|
48
|
+
# : (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?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
|
|
49
|
+
def initialize: (?identifier: String?, ?model: (String | Proc)?, ?instructions: (String | Proc)?, ?provider_options: Hash[Symbol, untyped], ?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
|
|
50
|
+
|
|
51
|
+
# Sets +identifier+. Accepts +nil+ or any value, coerced to String.
|
|
52
|
+
#
|
|
53
|
+
# --
|
|
54
|
+
# : (untyped) -> String?
|
|
55
|
+
def identifier=: (untyped) -> String?
|
|
56
|
+
|
|
57
|
+
# Sets +structured_output+. Accepts a Riffer::Params instance or +nil+.
|
|
58
|
+
#
|
|
59
|
+
# Raises Riffer::ArgumentError on any other type.
|
|
60
|
+
#
|
|
61
|
+
# --
|
|
62
|
+
# : (Riffer::Params?) -> Riffer::Params?
|
|
63
|
+
def structured_output=: (Riffer::Params?) -> Riffer::Params?
|
|
64
|
+
|
|
65
|
+
# Sets +tool_runtime+. Accepts a Riffer::Tools::Runtime subclass, a
|
|
66
|
+
# Riffer::Tools::Runtime instance, or a Proc.
|
|
67
|
+
#
|
|
68
|
+
# Raises Riffer::ArgumentError on any other type.
|
|
69
|
+
#
|
|
70
|
+
# --
|
|
71
|
+
# : ((singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
|
|
72
|
+
def tool_runtime=: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc) -> (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
|
|
73
|
+
|
|
74
|
+
# Sets +model+. Accepts a String ("provider/model"), a Proc, or +nil+.
|
|
75
|
+
#
|
|
76
|
+
# Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
|
|
77
|
+
#
|
|
78
|
+
# --
|
|
79
|
+
# : ((String | Proc)?) -> (String | Proc)?
|
|
80
|
+
def model=: ((String | Proc)?) -> (String | Proc)?
|
|
81
|
+
|
|
82
|
+
# Sets +instructions+. Accepts a String, a Proc, or +nil+.
|
|
83
|
+
#
|
|
84
|
+
# Raises Riffer::ArgumentError on non-String, non-Proc, or empty-String values.
|
|
85
|
+
#
|
|
86
|
+
# --
|
|
87
|
+
# : ((String | Proc)?) -> (String | Proc)?
|
|
88
|
+
def instructions=: ((String | Proc)?) -> (String | Proc)?
|
|
89
|
+
|
|
90
|
+
# Appends an MCP tag entry to +mcp_configs+.
|
|
91
|
+
#
|
|
92
|
+
# --
|
|
93
|
+
# : (String | Symbol) -> Array[Hash[Symbol, untyped]]
|
|
94
|
+
def add_mcp: (String | Symbol) -> Array[Hash[Symbol, untyped]]
|
|
95
|
+
|
|
96
|
+
# Appends a guardrail entry to +guardrails+ for the given phase.
|
|
97
|
+
#
|
|
98
|
+
# [phase] +:before+, +:after+, or +:around+. +:around+ appends to both
|
|
99
|
+
# +:before+ and +:after+.
|
|
100
|
+
# [klass] the Riffer::Guardrail subclass to register.
|
|
101
|
+
# [options] options forwarded to the guardrail at runtime.
|
|
102
|
+
#
|
|
103
|
+
# Raises Riffer::ArgumentError on an invalid phase or non-Guardrail class.
|
|
104
|
+
#
|
|
105
|
+
# --
|
|
106
|
+
# : (Symbol, klass: singleton(Riffer::Guardrail), ?options: Hash[Symbol, untyped]) -> void
|
|
107
|
+
def add_guardrail: (Symbol, klass: singleton(Riffer::Guardrail), ?options: Hash[Symbol, untyped]) -> void
|
|
108
|
+
|
|
109
|
+
# Returns the guardrail entries for the given phase, or +[]+ if none.
|
|
110
|
+
#
|
|
111
|
+
# --
|
|
112
|
+
# : (Symbol) -> Array[Hash[Symbol, untyped]]
|
|
113
|
+
def guardrails_for: (Symbol) -> Array[Hash[Symbol, untyped]]
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# : (untyped, String) -> void
|
|
118
|
+
def validate_string_or_proc!: (untyped, String) -> void
|
|
119
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/context.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Typed value object wrapping the runtime context Hash held by a
|
|
4
|
+
# Riffer::Agent. Exposes first-class accessors for the framework-managed
|
|
5
|
+
# entries — +skills+ and +token_usage+ — and preserves +#[]+ / +#dig+
|
|
6
|
+
# reads so tools (which receive +context:+ as a keyword) keep working
|
|
7
|
+
# with both built-in and caller-provided keys.
|
|
8
|
+
#
|
|
9
|
+
# Reserved keys (+:skills+, +:token_usage+) cannot be set by the caller
|
|
10
|
+
# at construction; they are owned by Riffer and written through the typed
|
|
11
|
+
# setters. Type invariants are enforced on write — +skills+ must be a
|
|
12
|
+
# +Riffer::Skills::Context+ (or nil); +token_usage+ must be a
|
|
13
|
+
# +Riffer::Providers::TokenUsage+ (or nil).
|
|
14
|
+
#
|
|
15
|
+
# context = Riffer::Agent::Context.new(user_id: 42)
|
|
16
|
+
# context[:user_id] # => 42
|
|
17
|
+
# context.skills # => nil
|
|
18
|
+
# context.token_usage # => nil
|
|
19
|
+
class Riffer::Agent::Context
|
|
20
|
+
# Keys reserved for framework use. Passing any of these to the
|
|
21
|
+
# constructor raises +Riffer::ArgumentError+.
|
|
22
|
+
RESERVED_KEYS: Array[Symbol]
|
|
23
|
+
|
|
24
|
+
# Builds a new context.
|
|
25
|
+
#
|
|
26
|
+
# [data] caller-provided Hash passed as <tt>Agent.new(context:)</tt>.
|
|
27
|
+
# Duped before storage so caller mutations do not affect the
|
|
28
|
+
# agent. Must not contain any +RESERVED_KEYS+.
|
|
29
|
+
#
|
|
30
|
+
# Raises Riffer::ArgumentError when +data+ contains a reserved key.
|
|
31
|
+
#
|
|
32
|
+
# --
|
|
33
|
+
# : (?Hash[Symbol, untyped]) -> void
|
|
34
|
+
def initialize: (?Hash[Symbol, untyped]) -> void
|
|
35
|
+
|
|
36
|
+
# The agent's resolved +Riffer::Skills::Context+, or +nil+ when skills
|
|
37
|
+
# are not configured.
|
|
38
|
+
#
|
|
39
|
+
# --
|
|
40
|
+
# : () -> Riffer::Skills::Context?
|
|
41
|
+
def skills: () -> Riffer::Skills::Context?
|
|
42
|
+
|
|
43
|
+
# Sets the resolved skills context. Called once by +Riffer::Agent+
|
|
44
|
+
# during construction.
|
|
45
|
+
#
|
|
46
|
+
# Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
|
|
47
|
+
# +Riffer::Skills::Context+.
|
|
48
|
+
#
|
|
49
|
+
# --
|
|
50
|
+
# : (Riffer::Skills::Context?) -> Riffer::Skills::Context?
|
|
51
|
+
def skills=: (Riffer::Skills::Context?) -> Riffer::Skills::Context?
|
|
52
|
+
|
|
53
|
+
# The cumulative +Riffer::Providers::TokenUsage+ across every Run on this agent,
|
|
54
|
+
# or +nil+ before the first response is recorded.
|
|
55
|
+
#
|
|
56
|
+
# --
|
|
57
|
+
# : () -> Riffer::Providers::TokenUsage?
|
|
58
|
+
def token_usage: () -> Riffer::Providers::TokenUsage?
|
|
59
|
+
|
|
60
|
+
# Sets the cumulative token usage. Called by +Riffer::Agent::Run+ after
|
|
61
|
+
# each LLM response.
|
|
62
|
+
#
|
|
63
|
+
# Raises Riffer::ArgumentError if +value+ is neither +nil+ nor a
|
|
64
|
+
# +Riffer::Providers::TokenUsage+.
|
|
65
|
+
#
|
|
66
|
+
# --
|
|
67
|
+
# : (Riffer::Providers::TokenUsage?) -> Riffer::Providers::TokenUsage?
|
|
68
|
+
def token_usage=: (Riffer::Providers::TokenUsage?) -> Riffer::Providers::TokenUsage?
|
|
69
|
+
|
|
70
|
+
# Hash-style read. Preserved so downstream tool runtimes pulling
|
|
71
|
+
# caller-provided keys via <tt>context[:agent]</tt> or
|
|
72
|
+
# <tt>context[:tenant]</tt> keep working unchanged.
|
|
73
|
+
#
|
|
74
|
+
# --
|
|
75
|
+
# : (Symbol) -> untyped
|
|
76
|
+
def []: (Symbol) -> untyped
|
|
77
|
+
|
|
78
|
+
# Hash-style dig. Preserved for tools using
|
|
79
|
+
# <tt>context&.dig(:user_id)</tt>.
|
|
80
|
+
#
|
|
81
|
+
# --
|
|
82
|
+
# : (*Symbol) -> untyped
|
|
83
|
+
def dig: (*Symbol) -> untyped
|
|
84
|
+
|
|
85
|
+
# Returns a copy of the underlying Hash. Mutating the result does not
|
|
86
|
+
# affect this context.
|
|
87
|
+
#
|
|
88
|
+
# --
|
|
89
|
+
# : () -> Hash[Symbol, untyped]
|
|
90
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
91
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/run.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Riffer::Agent::Run is the generation loop. A pure module of functions over an
|
|
4
|
+
# +agent+ — Agent owns every per-call value (provider, model, tools, tool
|
|
5
|
+
# runtime, structured output, session, context); Run just orchestrates.
|
|
6
|
+
#
|
|
7
|
+
# Tools and user code see the agent's +context+ (a +Riffer::Agent::Context+)
|
|
8
|
+
# unchanged through the loop, so downstream tool runtimes can read
|
|
9
|
+
# caller-provided keys via <tt>context[:agent]</tt> /
|
|
10
|
+
# <tt>context.dig(:key)</tt>, or the framework built-ins via
|
|
11
|
+
# +context.skills+. Cumulative token usage is updated into
|
|
12
|
+
# +agent.context.token_usage+ as the loop progresses.
|
|
13
|
+
#
|
|
14
|
+
# Riffer::Agent::Run.generate(agent: my_agent, prompt: "Hello")
|
|
15
|
+
# Riffer::Agent::Run.stream(agent: my_agent, prompt: "Hello")
|
|
16
|
+
module Riffer::Agent::Run
|
|
17
|
+
include Riffer::Messages::Converter
|
|
18
|
+
|
|
19
|
+
# Runs the generate loop for the given agent. See Riffer::Agent#generate
|
|
20
|
+
# for prompt/files semantics.
|
|
21
|
+
#
|
|
22
|
+
# --
|
|
23
|
+
# : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
|
|
24
|
+
def generate: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Riffer::Agent::Response
|
|
25
|
+
|
|
26
|
+
# Runs the streaming loop for the given agent. See Riffer::Agent#stream
|
|
27
|
+
# for prompt/files semantics.
|
|
28
|
+
#
|
|
29
|
+
# --
|
|
30
|
+
# : (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
|
|
31
|
+
def stream: (agent: Riffer::Agent, ?prompt: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> Enumerator[Riffer::StreamEvents::Base, void]
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# The generation loop. When +stream_yielder+ is provided, per-step events are
|
|
36
|
+
# pushed to it (and +stream+ discards the return value). When +stream_yielder+
|
|
37
|
+
# is +nil+, no events are emitted and +generate+ returns the Response
|
|
38
|
+
# directly. The two modes share every step of the loop — the only
|
|
39
|
+
# divergences are the LLM call shape (atomic vs. accumulated stream)
|
|
40
|
+
# and whether per-step events are emitted.
|
|
41
|
+
#
|
|
42
|
+
# --
|
|
43
|
+
# : (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
|
|
44
|
+
def run_loop: (Riffer::Agent, ?stream_yielder: Enumerator::Yielder?) -> Riffer::Agent::Response
|
|
45
|
+
|
|
46
|
+
# Consumes one provider stream, forwarding every event to +stream_yielder+
|
|
47
|
+
# and folding it into an +Assistant+ message.
|
|
48
|
+
#
|
|
49
|
+
# --
|
|
50
|
+
# : (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
|
|
51
|
+
def accumulate_streamed_response: (Riffer::Agent, Enumerator::Yielder) -> Riffer::Messages::Assistant
|
|
52
|
+
|
|
53
|
+
# Appends +new_modifications+ to +all_modifications+ and emits a
|
|
54
|
+
# +GuardrailModification+ event for each one when streaming.
|
|
55
|
+
#
|
|
56
|
+
# --
|
|
57
|
+
# : (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
|
|
58
|
+
def record_modifications!: (Enumerator::Yielder?, Array[Riffer::Guardrails::Modification], Array[Riffer::Guardrails::Modification]) -> void
|
|
59
|
+
|
|
60
|
+
# Emits a +GuardrailTripwire+ event when streaming and returns the
|
|
61
|
+
# short-circuit +Response+ for a tripped guardrail.
|
|
62
|
+
#
|
|
63
|
+
# --
|
|
64
|
+
# : (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
|
|
65
|
+
def tripwire_response: (Riffer::Agent, Enumerator::Yielder?, Riffer::Guardrails::Tripwire, Array[Riffer::Guardrails::Modification]) -> Riffer::Agent::Response
|
|
66
|
+
|
|
67
|
+
# Builds the final +Response+ from the session's last assistant
|
|
68
|
+
# message, validating structured output when configured. +extra+
|
|
69
|
+
# carries the interrupt-only fields (+interrupted:+, +interrupt_reason:+,
|
|
70
|
+
# +healed_tool_call_ids:+) on the interrupt exit path.
|
|
71
|
+
#
|
|
72
|
+
# --
|
|
73
|
+
# : (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
|
|
74
|
+
def final_response: (Riffer::Agent, Array[Riffer::Guardrails::Modification], **untyped) -> Riffer::Agent::Response
|
|
75
|
+
|
|
76
|
+
# --
|
|
77
|
+
# : (Riffer::Agent) -> Riffer::Messages::Assistant
|
|
78
|
+
def call_llm: (Riffer::Agent) -> Riffer::Messages::Assistant
|
|
79
|
+
|
|
80
|
+
# --
|
|
81
|
+
# : (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
|
|
82
|
+
def call_llm_stream: (Riffer::Agent) -> Enumerator[Riffer::StreamEvents::Base, void]
|
|
83
|
+
|
|
84
|
+
# --
|
|
85
|
+
# : (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
|
|
86
|
+
def execute_tool_calls: (Riffer::Agent, Riffer::Messages::Assistant, ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall]) -> void
|
|
87
|
+
|
|
88
|
+
# Executes tool calls left unfinished by a prior interrupt.
|
|
89
|
+
#
|
|
90
|
+
# Detects gaps between the last assistant message's requested tool calls
|
|
91
|
+
# and the tool result messages that follow it, executing any that are
|
|
92
|
+
# missing. Safe to call unconditionally.
|
|
93
|
+
#
|
|
94
|
+
# --
|
|
95
|
+
# : (Riffer::Agent) -> void
|
|
96
|
+
def execute_pending_tool_calls: (Riffer::Agent) -> void
|
|
97
|
+
|
|
98
|
+
# Runs the +:before+ guardrail phase. Records any modifications into
|
|
99
|
+
# +all_modifications+ (and emits them when streaming). When a tripwire
|
|
100
|
+
# fires, yields the short-circuit +Response+ — the caller's block is
|
|
101
|
+
# expected to +return+ it from +run_loop+.
|
|
102
|
+
#
|
|
103
|
+
# --
|
|
104
|
+
# : (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
|
|
105
|
+
def run_before_guardrails: (Riffer::Agent, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> void
|
|
106
|
+
|
|
107
|
+
# Runs the +:after+ guardrail phase against the assistant +response+.
|
|
108
|
+
# Records any modifications into +all_modifications+ (and emits them
|
|
109
|
+
# when streaming). When a tripwire fires, yields the short-circuit
|
|
110
|
+
# +Response+ — the caller's block is expected to +return+ it from
|
|
111
|
+
# +run_loop+. Otherwise returns the post-guardrails assistant message.
|
|
112
|
+
#
|
|
113
|
+
# --
|
|
114
|
+
# : (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
|
|
115
|
+
def run_after_guardrails: (Riffer::Agent, Riffer::Messages::Assistant, Enumerator::Yielder?, Array[Riffer::Guardrails::Modification]) { (Riffer::Agent::Response) -> void } -> untyped
|
|
116
|
+
|
|
117
|
+
# --
|
|
118
|
+
# : (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
|
|
119
|
+
def validate_structured_output: (Riffer::Agent, Riffer::Messages::Assistant?) -> Hash[Symbol, untyped]?
|
|
120
|
+
|
|
121
|
+
# --
|
|
122
|
+
# : (Riffer::Agent) -> Hash[Symbol, untyped]
|
|
123
|
+
def merged_model_options: (Riffer::Agent) -> Hash[Symbol, untyped]
|
|
124
|
+
|
|
125
|
+
# --
|
|
126
|
+
# : (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
|
|
127
|
+
def build_response: (Riffer::Agent, String, ?tripwire: Riffer::Guardrails::Tripwire?, ?modifications: Array[Riffer::Guardrails::Modification], ?interrupted: bool, ?interrupt_reason: (String | Symbol)?, ?structured_output: Hash[Symbol, untyped]?, ?healed_tool_call_ids: Array[String]) -> Riffer::Agent::Response
|
|
128
|
+
|
|
129
|
+
# Appends a +User+ message to the session. No-ops when +prompt+ is nil
|
|
130
|
+
# and +files+ is empty (the caller had nothing to add). Raises when
|
|
131
|
+
# +files+ are supplied without a +prompt+ — the provider needs text to
|
|
132
|
+
# anchor the attachments.
|
|
133
|
+
#
|
|
134
|
+
# --
|
|
135
|
+
# : (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
|
|
136
|
+
def append_user_message: (Riffer::Agent, String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?) -> void
|
|
137
|
+
|
|
138
|
+
# Accumulates token usage into +agent.context.token_usage+. Updates the
|
|
139
|
+
# context so cumulative usage persists across every run on the agent.
|
|
140
|
+
#
|
|
141
|
+
# --
|
|
142
|
+
# : (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
|
|
143
|
+
def track_token_usage: (Riffer::Agent, Riffer::Providers::TokenUsage?) -> void
|
|
144
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/session/repair.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Riffer::Agent::Session::Repair holds the pure transformations that keep the
|
|
4
|
+
# +tool_use+ ↔ +tool_result+ invariant on a message array. No state, no
|
|
5
|
+
# instance — module-level functions only. Each entry point is gated by
|
|
6
|
+
# +Riffer.config.experimental_history_healing+: when the flag is off the
|
|
7
|
+
# function returns its input unchanged.
|
|
8
|
+
#
|
|
9
|
+
# Two seams:
|
|
10
|
+
#
|
|
11
|
+
# - +fill_orphans+ — fills orphan +tool_use+ blocks with placeholder
|
|
12
|
+
# results. Used on interrupt (caller-issued or +max_steps+).
|
|
13
|
+
# - +prune_orphans+ — drops orphan +tool_use+ blocks and parentless Tool
|
|
14
|
+
# messages from a caller-provided seed so it is well-formed before the
|
|
15
|
+
# next inference call. Used at construction time when
|
|
16
|
+
# +Riffer::Agent.new(session:)+ receives a session.
|
|
17
|
+
module Riffer::Agent::Session::Repair
|
|
18
|
+
# Placeholder used to fill orphan +tool_use+ blocks. Emitted as the
|
|
19
|
+
# +Riffer::Tools::Response+ body for each filled call_id.
|
|
20
|
+
ORPHAN_PLACEHOLDER: ^(Riffer::Messages::Assistant::ToolCall) -> Riffer::Tools::Response
|
|
21
|
+
|
|
22
|
+
# Fills any orphaned +tool_use+ in +messages+ with the
|
|
23
|
+
# +ORPHAN_PLACEHOLDER+ response. Each placeholder Tool message is
|
|
24
|
+
# inserted immediately after its parent assistant message. Returns
|
|
25
|
+
# +[new_messages, filled_call_ids]+; +filled_call_ids+ is empty when
|
|
26
|
+
# there are no orphans.
|
|
27
|
+
#
|
|
28
|
+
# No-op when +Riffer.config.experimental_history_healing+ is off:
|
|
29
|
+
# returns +[messages, []]+ with the same array reference.
|
|
30
|
+
#
|
|
31
|
+
# --
|
|
32
|
+
# : (Array[Riffer::Messages::Base]) -> [Array[Riffer::Messages::Base], Array[String]]
|
|
33
|
+
def self.fill_orphans: (Array[Riffer::Messages::Base]) -> [ Array[Riffer::Messages::Base], Array[String] ]
|
|
34
|
+
|
|
35
|
+
# Prunes a seeded message array so the +tool_use+ ↔ +tool_result+
|
|
36
|
+
# invariant holds. Drops orphaned tool exchanges (assistant +tool_call+
|
|
37
|
+
# with no matching Tool result) and parentless Tool messages. Returns a
|
|
38
|
+
# new array; the input is not mutated.
|
|
39
|
+
#
|
|
40
|
+
# Pending tool_calls on the resume boundary — the last assistant whose
|
|
41
|
+
# tail is purely Tool results (or empty) — are preserved. They get
|
|
42
|
+
# swept up by +execute_pending_tool_calls+ at the start of the next
|
|
43
|
+
# generate/stream call.
|
|
44
|
+
#
|
|
45
|
+
# No-op when +Riffer.config.experimental_history_healing+ is off:
|
|
46
|
+
# returns +messages+ unchanged.
|
|
47
|
+
#
|
|
48
|
+
# --
|
|
49
|
+
# : (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
|
|
50
|
+
def self.prune_orphans: (Array[Riffer::Messages::Base]) -> Array[Riffer::Messages::Base]
|
|
51
|
+
end
|