riffer 0.27.2 → 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 +31 -0
- data/README.md +17 -18
- data/Steepfile +7 -1
- data/docs/03_AGENTS.md +34 -3
- data/docs/04_AGENT_LIFECYCLE.md +134 -86
- data/docs/05_AGENT_LOOP.md +2 -2
- data/docs/06_TOOLS.md +9 -4
- data/docs/07_TOOL_ADVANCED.md +23 -19
- data/docs/08_MESSAGES.md +28 -31
- data/docs/09_STREAM_EVENTS.md +1 -1
- data/docs/10_CONFIGURATION.md +25 -15
- 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/response.rb +11 -2
- 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 +246 -684
- data/lib/riffer/config.rb +56 -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 +10 -3
- 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} +21 -15
- 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/response.rbs +10 -2
- 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 +154 -225
- data/sig/generated/riffer/config.rbs +50 -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/interrupt.rbs +7 -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} +19 -13
- 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
|
@@ -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
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Generated from lib/riffer/agent/session.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Riffer::Agent::Session owns the conversation handle for an agent: the message
|
|
4
|
+
# array, the +on_message+ callback list, and the +tool_use+ ↔ +tool_result+
|
|
5
|
+
# invariant that keeps tool calls and their results consistent.
|
|
6
|
+
#
|
|
7
|
+
# Access via +agent.session+. Sessions are constructed by +Riffer::Agent+
|
|
8
|
+
# and live for the lifetime of the agent.
|
|
9
|
+
#
|
|
10
|
+
# agent.session.add(msg) # append + fire callbacks
|
|
11
|
+
# agent.session.set([msg1, msg2]) # bulk replace (silent)
|
|
12
|
+
# agent.session.unset # clear (silent)
|
|
13
|
+
# agent.session.remove(id: "a_1")
|
|
14
|
+
# agent.session.update(id: "a_1", content: "...")
|
|
15
|
+
# agent.session.find { |m| m.id == "a_1" }
|
|
16
|
+
class Riffer::Agent::Session
|
|
17
|
+
include Enumerable[Riffer::Messages::Base]
|
|
18
|
+
|
|
19
|
+
# The message history.
|
|
20
|
+
attr_reader messages: Array[Riffer::Messages::Base]
|
|
21
|
+
|
|
22
|
+
# --
|
|
23
|
+
# : (?messages: Array[Riffer::Messages::Base]) -> void
|
|
24
|
+
def initialize: (?messages: Array[Riffer::Messages::Base]) -> void
|
|
25
|
+
|
|
26
|
+
# Registers a callback invoked once per message appended via +#add+.
|
|
27
|
+
#
|
|
28
|
+
# Callbacks do NOT fire for +#set+, +#unset+, +#remove+, or +#update+.
|
|
29
|
+
# Returns +self+ to allow chaining.
|
|
30
|
+
#
|
|
31
|
+
# Raises Riffer::ArgumentError if no block is given.
|
|
32
|
+
#
|
|
33
|
+
# --
|
|
34
|
+
# : () { (Riffer::Messages::Base) -> void } -> self
|
|
35
|
+
def on_message: () { (Riffer::Messages::Base) -> void } -> self
|
|
36
|
+
|
|
37
|
+
# Appends +message+ and fires every registered callback once with it.
|
|
38
|
+
#
|
|
39
|
+
# Pass +silent: true+ to skip +on_message+ callbacks — used for
|
|
40
|
+
# non-inference inputs like user messages, which subscribers don't
|
|
41
|
+
# expect to observe through the callback channel. Inference-produced
|
|
42
|
+
# messages (Assistant, Tool) always go through +add+ without +silent+.
|
|
43
|
+
#
|
|
44
|
+
# --
|
|
45
|
+
# : (Riffer::Messages::Base, ?silent: bool) -> Riffer::Messages::Base
|
|
46
|
+
def add: (Riffer::Messages::Base, ?silent: bool) -> Riffer::Messages::Base
|
|
47
|
+
|
|
48
|
+
# Replaces the message history wholesale. Does NOT fire +on_message+
|
|
49
|
+
# callbacks; registered callbacks persist across the swap.
|
|
50
|
+
#
|
|
51
|
+
# Used for seeding, guardrail rewrites, and history healing — cases
|
|
52
|
+
# where firing callbacks would double-emit messages that subscribers
|
|
53
|
+
# have already observed (or never produced).
|
|
54
|
+
#
|
|
55
|
+
# --
|
|
56
|
+
# : (Array[Riffer::Messages::Base]) -> self
|
|
57
|
+
def set: (Array[Riffer::Messages::Base]) -> self
|
|
58
|
+
|
|
59
|
+
# Clears the session. Does NOT fire +on_message+ callbacks; registered
|
|
60
|
+
# callbacks persist.
|
|
61
|
+
#
|
|
62
|
+
# --
|
|
63
|
+
# : () -> self
|
|
64
|
+
def unset: () -> self
|
|
65
|
+
|
|
66
|
+
# Removes a message by id. When the target is an assistant message that
|
|
67
|
+
# carries +tool_calls+, every +Riffer::Messages::Tool+ result whose
|
|
68
|
+
# +tool_call_id+ matches one of those calls is removed atomically — keeping
|
|
69
|
+
# the +tool_use+ ↔ +tool_result+ invariant intact.
|
|
70
|
+
#
|
|
71
|
+
# Raises Riffer::ArgumentError when called on a +Riffer::Messages::Tool+
|
|
72
|
+
# message — that would orphan the parent's +tool_use+. Use
|
|
73
|
+
# +#update+ to rewrite a tool result instead.
|
|
74
|
+
#
|
|
75
|
+
# Returns the removed message, or +nil+ when no message has the given id
|
|
76
|
+
# (idempotent).
|
|
77
|
+
#
|
|
78
|
+
# --
|
|
79
|
+
# : (id: String) -> Riffer::Messages::Base?
|
|
80
|
+
def remove: (id: String) -> Riffer::Messages::Base?
|
|
81
|
+
|
|
82
|
+
# Partial in-place update. Looks up a message by either +id:+ or
|
|
83
|
+
# +tool_call_id:+ (exactly one required), constructs a replacement of the
|
|
84
|
+
# same concrete type with +attrs+ overlaid on the existing fields, and
|
|
85
|
+
# swaps it in place.
|
|
86
|
+
#
|
|
87
|
+
# When the target is an assistant message and the update drops one or more
|
|
88
|
+
# entries from +tool_calls+, every +Riffer::Messages::Tool+ result whose
|
|
89
|
+
# +tool_call_id+ matches a dropped call is removed atomically — keeping the
|
|
90
|
+
# +tool_use+ ↔ +tool_result+ invariant intact.
|
|
91
|
+
#
|
|
92
|
+
# Raises Riffer::ArgumentError when neither or both lookup keys are
|
|
93
|
+
# provided, or when no message matches.
|
|
94
|
+
#
|
|
95
|
+
# --
|
|
96
|
+
# : (?id: String?, ?tool_call_id: String?, **untyped) -> Riffer::Messages::Base
|
|
97
|
+
def update: (?id: String?, ?tool_call_id: String?, **untyped) -> Riffer::Messages::Base
|
|
98
|
+
|
|
99
|
+
# Returns the call_ids of every +tool_call+ on any assistant message that
|
|
100
|
+
# has no matching +Riffer::Messages::Tool+ result anywhere in history.
|
|
101
|
+
#
|
|
102
|
+
# Zero-cost validation hook for callers that want to check the
|
|
103
|
+
# +tool_use+ ↔ +tool_result+ invariant before mutating or persisting.
|
|
104
|
+
#
|
|
105
|
+
# --
|
|
106
|
+
# : () -> Array[String]
|
|
107
|
+
def orphaned_tool_call_ids: () -> Array[String]
|
|
108
|
+
|
|
109
|
+
# Returns +[assistant, pending_tool_calls]+ for the last assistant message.
|
|
110
|
+
# When there is no assistant message or no pending calls, the second
|
|
111
|
+
# element is an empty array.
|
|
112
|
+
#
|
|
113
|
+
# --
|
|
114
|
+
# : () -> [Riffer::Messages::Assistant?, Array[Riffer::Messages::Assistant::ToolCall]]
|
|
115
|
+
def pending_tool_calls: () -> [ Riffer::Messages::Assistant?, Array[Riffer::Messages::Assistant::ToolCall] ]
|
|
116
|
+
|
|
117
|
+
# --
|
|
118
|
+
# : () -> Enumerator[Riffer::Messages::Base, self]
|
|
119
|
+
# : () { (Riffer::Messages::Base) -> void } -> untyped
|
|
120
|
+
def each: () -> Enumerator[Riffer::Messages::Base, self]
|
|
121
|
+
| () { (Riffer::Messages::Base) -> void } -> untyped
|
|
122
|
+
|
|
123
|
+
# The number of LLM steps completed in this session, derived from the
|
|
124
|
+
# count of assistant messages. Used by the agent loop to enforce
|
|
125
|
+
# +max_steps+ on resume.
|
|
126
|
+
#
|
|
127
|
+
# --
|
|
128
|
+
# : () -> Integer
|
|
129
|
+
def steps: () -> Integer
|
|
130
|
+
|
|
131
|
+
# The most recent +Riffer::Messages::Assistant+ in the session, or +nil+
|
|
132
|
+
# when none exists.
|
|
133
|
+
#
|
|
134
|
+
# --
|
|
135
|
+
# : () -> Riffer::Messages::Assistant?
|
|
136
|
+
def final_assistant_message: () -> Riffer::Messages::Assistant?
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
# : (Riffer::Messages::Base, Riffer::Messages::Base) -> void
|
|
141
|
+
def cascade_dropped_tool_calls: (Riffer::Messages::Base, Riffer::Messages::Base) -> void
|
|
142
|
+
|
|
143
|
+
# : (Riffer::Messages::Base, Hash[Symbol, untyped]) -> Riffer::Messages::Base
|
|
144
|
+
def rebuild_message: (Riffer::Messages::Base, Hash[Symbol, untyped]) -> Riffer::Messages::Base
|
|
145
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Generated from lib/riffer/structured_output/result.rb with RBS::Inline
|
|
1
|
+
# Generated from lib/riffer/agent/structured_output/result.rb with RBS::Inline
|
|
2
2
|
|
|
3
3
|
# Wraps the result of structured output parsing and validation.
|
|
4
4
|
#
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# else
|
|
12
12
|
# result.error #=> "JSON parse error: ..."
|
|
13
13
|
# end
|
|
14
|
-
class Riffer::StructuredOutput::Result
|
|
14
|
+
class Riffer::Agent::StructuredOutput::Result
|
|
15
15
|
attr_reader object: Hash[Symbol, untyped]?
|
|
16
16
|
|
|
17
17
|
attr_reader error: String?
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Generated from lib/riffer/structured_output.rb with RBS::Inline
|
|
1
|
+
# Generated from lib/riffer/agent/structured_output.rb with RBS::Inline
|
|
2
2
|
|
|
3
|
-
# Riffer::StructuredOutput provides parse/validate for structured JSON
|
|
3
|
+
# Riffer::Agent::StructuredOutput provides parse/validate for structured JSON
|
|
4
4
|
# responses from LLM providers.
|
|
5
5
|
#
|
|
6
6
|
# params = Riffer::Params.new
|
|
7
7
|
# params.required(:sentiment, String)
|
|
8
|
-
# so = Riffer::StructuredOutput.new(params)
|
|
8
|
+
# so = Riffer::Agent::StructuredOutput.new(params)
|
|
9
9
|
# result = so.parse_and_validate('{"sentiment":"positive","score":0.9}')
|
|
10
10
|
# result.object #=> {sentiment: "positive", score: 0.9}
|
|
11
|
-
class Riffer::StructuredOutput
|
|
11
|
+
class Riffer::Agent::StructuredOutput
|
|
12
12
|
attr_reader params: Riffer::Params
|
|
13
13
|
|
|
14
14
|
# --
|
|
@@ -26,6 +26,6 @@ class Riffer::StructuredOutput
|
|
|
26
26
|
# Returns a Result with the validated object on success, or an error message on failure.
|
|
27
27
|
#
|
|
28
28
|
# --
|
|
29
|
-
# : (String) -> Riffer::StructuredOutput::Result
|
|
30
|
-
def parse_and_validate: (String) -> Riffer::StructuredOutput::Result
|
|
29
|
+
# : (String) -> Riffer::Agent::StructuredOutput::Result
|
|
30
|
+
def parse_and_validate: (String) -> Riffer::Agent::StructuredOutput::Result
|
|
31
31
|
end
|