riffer 0.47.0 → 0.47.2
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/.claude/rules/rbs-inline.md +5 -1
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +16 -0
- data/Rakefile +1 -1
- data/Steepfile +1 -15
- data/docs/AGENTS.md +2 -2
- data/docs/STREAM_EVENTS.md +23 -0
- data/docs/providers/CUSTOM_PROVIDERS.md +2 -0
- data/lib/riffer/agent/config.rb +11 -11
- data/lib/riffer/agent/outcome.rb +2 -2
- data/lib/riffer/agent/response.rb +9 -9
- data/lib/riffer/agent/run.rb +0 -1
- data/lib/riffer/agent/session.rb +1 -1
- data/lib/riffer/agent/structured_output/result.rb +2 -2
- data/lib/riffer/agent/structured_output.rb +1 -1
- data/lib/riffer/agent.rb +11 -11
- data/lib/riffer/config.rb +30 -30
- data/lib/riffer/evals/judge.rb +2 -3
- data/lib/riffer/evals/result.rb +6 -6
- data/lib/riffer/evals/run_result.rb +1 -1
- data/lib/riffer/evals/scenario_result.rb +6 -6
- data/lib/riffer/guardrails/modification.rb +3 -3
- data/lib/riffer/guardrails/result.rb +3 -3
- data/lib/riffer/guardrails/runner.rb +4 -4
- data/lib/riffer/guardrails/tripwire.rb +4 -4
- data/lib/riffer/mcp/manifest.rb +5 -5
- data/lib/riffer/mcp/registration.rb +1 -1
- data/lib/riffer/mcp/search_tool.rb +1 -1
- data/lib/riffer/messages/assistant.rb +5 -5
- data/lib/riffer/messages/base.rb +2 -4
- data/lib/riffer/messages/file_part.rb +4 -7
- data/lib/riffer/messages/tool.rb +4 -4
- data/lib/riffer/messages/user.rb +1 -1
- data/lib/riffer/params/param.rb +29 -10
- data/lib/riffer/params.rb +3 -1
- data/lib/riffer/providers/amazon_bedrock.rb +29 -2
- data/lib/riffer/providers/anthropic.rb +7 -0
- data/lib/riffer/providers/base.rb +1 -2
- data/lib/riffer/providers/finish_reason.rb +2 -2
- data/lib/riffer/providers/mock.rb +1 -1
- data/lib/riffer/providers/open_ai.rb +8 -0
- data/lib/riffer/providers/repository.rb +0 -4
- data/lib/riffer/providers/token_usage.rb +5 -5
- data/lib/riffer/skills/adapter.rb +1 -1
- data/lib/riffer/skills/context.rb +4 -7
- data/lib/riffer/skills/filesystem_backend.rb +1 -2
- data/lib/riffer/skills/frontmatter.rb +4 -4
- data/lib/riffer/skills/xml_adapter.rb +1 -1
- data/lib/riffer/stream_events/base.rb +1 -1
- data/lib/riffer/stream_events/finish_reason_done.rb +2 -2
- data/lib/riffer/stream_events/guardrail_modification.rb +1 -1
- data/lib/riffer/stream_events/guardrail_tripwire.rb +1 -1
- data/lib/riffer/stream_events/interrupt.rb +2 -2
- data/lib/riffer/stream_events/reasoning_delta.rb +1 -1
- data/lib/riffer/stream_events/reasoning_done.rb +1 -1
- data/lib/riffer/stream_events/skill_activation.rb +1 -1
- data/lib/riffer/stream_events/text_delta.rb +1 -1
- data/lib/riffer/stream_events/text_done.rb +1 -1
- data/lib/riffer/stream_events/token_usage_done.rb +1 -1
- data/lib/riffer/stream_events/tool_call_delta.rb +3 -3
- data/lib/riffer/stream_events/tool_call_done.rb +4 -4
- data/lib/riffer/stream_events/web_search_done.rb +2 -2
- data/lib/riffer/stream_events/web_search_status.rb +3 -3
- data/lib/riffer/tools/response.rb +4 -4
- data/lib/riffer/tracing/stream_recorder.rb +6 -6
- data/lib/riffer/version.rb +1 -1
- data/lib/riffer.rb +7 -0
- data/rbs_collection.lock.yaml +320 -0
- data/rbs_collection.yaml +12 -0
- data/sig/_private/anthropic.rbs +5 -4
- data/sig/_private/aws-sdk-core/event_error.rbs +15 -0
- data/sig/generated/riffer/params/param.rbs +10 -1
- data/sig/generated/riffer/params.rbs +2 -0
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +10 -1
- data/sig/generated/riffer/providers/repository.rbs +0 -2
- data/sig/generated/riffer.rbs +8 -0
- metadata +4 -3
- data/sig/_private/async.rbs +0 -28
- data/sig/_private/minitest.rbs +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5e4c3861f4218fa075b5abd666be63eee6432db05c9a6fff3ab4285748ed7c5
|
|
4
|
+
data.tar.gz: a945c6c549c4c18b4e228cbcce1a03a48eb2b263a20bb18c9c1c51088c6a3e8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 308010c98f1f44a9a13032b39fea4a2636977d9b6f232ccb9aa040ef6485f0b2d51ec3d66a69be0e4a522759cc86e42fa8b94eae6b2446a5c61ec6a3f6388892
|
|
7
|
+
data.tar.gz: dd7710eb0ed144e1dfaf49d063e5d4845068e590927092dd5b3f946b0cc0f49533262c3dc83e12dbb87fdc8356eb83890177ccf8eacc485028ca4ce7684ce01e
|
data/.claude/rules/rbs-inline.md
CHANGED
|
@@ -78,7 +78,11 @@ end
|
|
|
78
78
|
|
|
79
79
|
### Where stubs and stdlib deps live
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
Dependency signatures come from [`rbs collection`](https://github.com/ruby/gem_rbs_collection): `rbs_collection.yaml` pins the upstream repo to a commit SHA, the gem set is derived from `Gemfile.lock`, and `rbs_collection.lock.yaml` is committed (the installed `.gem_rbs_collection/` is gitignored). Steep auto-detects the config, so the `Steepfile` declares no `library` lines. `yaml` is the one hand-listed entry under `gems:` — it's stdlib, so it never appears in `Gemfile.lock`.
|
|
82
|
+
|
|
83
|
+
`bin/typecheck` runs `rbs collection update` before `steep check`, keeping the lock in sync with `Gemfile.lock`; CI fails on lock drift. So after a Dependabot gem bump, run `bin/typecheck` and commit the refreshed lock. To pull newer upstream stubs, bump `revision` in `rbs_collection.yaml` and run `bin/typecheck` — pinning is what makes `update` deterministic.
|
|
84
|
+
|
|
85
|
+
- `sig/_private/` — signatures that must **not** ship. RBS **skips** `_`-prefixed directories in library mode, so consumers never load them; riffer's own `steep check` does (via the `Steepfile`). Two kinds, by predictable path: external-gem signatures are named by gem at the top level — full stubs for the gems upstream has nothing for (`mcp.rbs`, `zeitwerk.rbs`, `opentelemetry.rbs`, `rspec.rbs`, `aws-sdk-core/*`) plus arity patches for the provider SDKs (`openai.rbs`, `anthropic.rbs`); riffer's own hidden stubs mirror `lib/` under `riffer/` (e.g. `riffer/providers/anthropic.rbs` narrows the private `client` method to the SDK-typed client). A stub must be **deleted** once the collection covers that gem, or RBS raises `DuplicatedMethodDefinitionError`.
|
|
82
86
|
- `sig/manual/` — hand-written riffer-only signatures that are **safe to ship**, for the few things rbs-inline can't generate _at all_ (mirroring `lib/`). In practice that's `extend self` modules (`riffer/agent/run.rbs`, `riffer/helpers/call_or_value.rbs`) and modeling an include applied dynamically (`riffer/tools/toolable.rbs`). SDK-free ivars are **not** hand-written here — declare them inline with `# @rbs` (see "Annotation Conventions"). SDK-typed signatures can't ship, so they go in `_private/riffer/providers/` (the narrowed `client`).
|
|
83
87
|
- `sig/manifest.yaml` — declares the **stdlib** RBS the shipped sigs reference (`uri`, `net-http`) so `rbs -r riffer` resolves them.
|
|
84
88
|
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.47.2](https://github.com/janeapp/riffer/compare/riffer/v0.47.1...riffer/v0.47.2) (2026-09-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **providers:** raise IncompleteStreamError when a stream ends without its terminal event ([#445](https://github.com/janeapp/riffer/issues/445)) ([d142e23](https://github.com/janeapp/riffer/commit/d142e23e80d30ce73e81474eee35c71df1856c40))
|
|
14
|
+
* stop relying on the transitive cgi gem for CGI.escapeHTML ([#443](https://github.com/janeapp/riffer/issues/443)) ([821f3e3](https://github.com/janeapp/riffer/commit/821f3e3e405845b2cc275f86344e526cc2cf183a))
|
|
15
|
+
|
|
16
|
+
## [0.47.1](https://github.com/janeapp/riffer/compare/riffer/v0.47.0...riffer/v0.47.1) (2026-09-11)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **params:** keep optional Hash params nullable in strict JSON Schema ([#433](https://github.com/janeapp/riffer/issues/433)) ([c7308fd](https://github.com/janeapp/riffer/commit/c7308fd15a3293877510e74c41662a68fac942df))
|
|
22
|
+
* **params:** raise for bare Hash and Array params under strict schemas ([#434](https://github.com/janeapp/riffer/issues/434)) ([174ad92](https://github.com/janeapp/riffer/commit/174ad92e7c765dc8833695ded9d2920f1ab27337))
|
|
23
|
+
|
|
8
24
|
## [0.47.0](https://github.com/janeapp/riffer/compare/riffer/v0.46.1...riffer/v0.47.0) (2026-09-11)
|
|
9
25
|
|
|
10
26
|
|
data/Rakefile
CHANGED
data/Steepfile
CHANGED
|
@@ -9,19 +9,5 @@ target :lib do
|
|
|
9
9
|
|
|
10
10
|
check "lib"
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
library "aws-sdk-bedrockruntime"
|
|
14
|
-
library "aws-sdk-core"
|
|
15
|
-
library "base64"
|
|
16
|
-
library "cgi"
|
|
17
|
-
library "digest"
|
|
18
|
-
library "json"
|
|
19
|
-
library "logger"
|
|
20
|
-
library "net-http"
|
|
21
|
-
library "openai"
|
|
22
|
-
library "securerandom"
|
|
23
|
-
library "uri"
|
|
24
|
-
library "yaml"
|
|
25
|
-
|
|
26
|
-
configure_code_diagnostics(D::Ruby.strict)
|
|
12
|
+
configure_code_diagnostics(D::Ruby.all_error)
|
|
27
13
|
end
|
data/docs/AGENTS.md
CHANGED
|
@@ -247,7 +247,7 @@ end
|
|
|
247
247
|
|
|
248
248
|
#### Limitations
|
|
249
249
|
|
|
250
|
-
Using both `of:` and a block raises `Riffer::ArgumentError`. Using `of:` with a non-primitive type (e.g. `of: Hash`) also raises `Riffer::ArgumentError`.
|
|
250
|
+
A `Hash` param requires a block, and an `Array` param requires a block or `of:`. Using both `of:` and a block raises `Riffer::ArgumentError`. Using `of:` with a non-primitive type (e.g. `of: Hash`) also raises `Riffer::ArgumentError`.
|
|
251
251
|
|
|
252
252
|
Structured output is not compatible with streaming — calling `stream` on an agent with structured output configured raises `Riffer::ArgumentError`.
|
|
253
253
|
|
|
@@ -372,7 +372,7 @@ agent.generate("Summarize this ticket.",
|
|
|
372
372
|
agent.stream("...", tags: {team: "growth", environment: "production"})
|
|
373
373
|
```
|
|
374
374
|
|
|
375
|
-
Keys and values may be `String` or `Symbol`; both are stringified, and entries with a `nil` value are dropped.
|
|
375
|
+
Keys and values may be `String` or `Symbol`; both are stringified, and entries with a `nil` value are dropped. An omitted or empty `tags:` is a complete no-op.
|
|
376
376
|
|
|
377
377
|
Tags propagate to **two** places:
|
|
378
378
|
|
data/docs/STREAM_EVENTS.md
CHANGED
|
@@ -272,6 +272,29 @@ event.to_h # => {role: :assistant, finish_reason: :length, raw_fin
|
|
|
272
272
|
|
|
273
273
|
The agent loop stamps this value onto the accumulated assistant message's `finish_reason`.
|
|
274
274
|
|
|
275
|
+
## Incomplete Streams
|
|
276
|
+
|
|
277
|
+
If a provider's stream ends before its terminal event, the enumerator raises `Riffer::IncompleteStreamError` (a `Riffer::Error` subclass) instead of finishing normally, so a truncated or empty response is never returned as a complete message. Events already yielded before the raise were delivered as usual, but nothing from the failed step is added to the session: there is no partial assistant message to resume from, and messages from earlier completed steps (tool calls and their results) are untouched.
|
|
278
|
+
|
|
279
|
+
Supported on Amazon Bedrock, Anthropic, and OpenAI / Azure OpenAI.
|
|
280
|
+
|
|
281
|
+
The user prompt is added to the session before the run starts and stays there after the failure, so retry with `agent.stream` and no prompt. Passing the prompt again would add a second user turn.
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
attempts = 0
|
|
285
|
+
prompt = "Tell me a story"
|
|
286
|
+
begin
|
|
287
|
+
agent.stream(prompt).each do |event|
|
|
288
|
+
print event.content if event.is_a?(Riffer::StreamEvents::TextDelta)
|
|
289
|
+
end
|
|
290
|
+
rescue Riffer::IncompleteStreamError => e
|
|
291
|
+
warn "stream ended early: #{e.message}"
|
|
292
|
+
prompt = nil # already in the session; re-run on the existing history
|
|
293
|
+
retry if (attempts += 1) < 3
|
|
294
|
+
raise
|
|
295
|
+
end
|
|
296
|
+
```
|
|
297
|
+
|
|
275
298
|
## Streaming with Tools
|
|
276
299
|
|
|
277
300
|
When an agent uses tools during streaming, the flow is:
|
|
@@ -274,6 +274,8 @@ For streaming, emit a `FinishReasonDone` event near the end of `execute_stream`:
|
|
|
274
274
|
yielder << Riffer::StreamEvents::FinishReasonDone.new(finish_reason: :stop, raw_finish_reason: "done")
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
+
Also have `execute_stream` raise `Riffer::IncompleteStreamError` when the stream ends without the provider's terminal event, rather than returning normally. Otherwise a connection that drops mid-response looks identical to a finished one, and the agent loop accepts a truncated message as complete.
|
|
278
|
+
|
|
277
279
|
## Trace Provider Name
|
|
278
280
|
|
|
279
281
|
LLM-call and agent-run spans stamp `gen_ai.provider.name` from the `semconv_provider_name` class method. The default is your snake_cased class name; override it when a [GenAI semconv well-known value](https://opentelemetry.io/docs/specs/semconv/gen-ai/) exists for your provider:
|
data/lib/riffer/agent/config.rb
CHANGED
|
@@ -8,37 +8,37 @@ class Riffer::Agent::Config
|
|
|
8
8
|
DEFAULT_MAX_STEPS = 16 #: Integer
|
|
9
9
|
|
|
10
10
|
# The configured agent identifier.
|
|
11
|
-
attr_reader :identifier #: String?
|
|
11
|
+
attr_reader :identifier #: String? # @dynamic identifier
|
|
12
12
|
|
|
13
13
|
# The configured model.
|
|
14
|
-
attr_reader :model #: (String | Proc)?
|
|
14
|
+
attr_reader :model #: (String | Proc)? # @dynamic model
|
|
15
15
|
|
|
16
16
|
# The configured instructions.
|
|
17
|
-
attr_reader :instructions #: (String | Proc)?
|
|
17
|
+
attr_reader :instructions #: (String | Proc)? # @dynamic instructions
|
|
18
18
|
|
|
19
19
|
# Options passed to generate_text/stream_text.
|
|
20
|
-
attr_accessor :model_options #: Hash[Symbol, untyped]
|
|
20
|
+
attr_accessor :model_options #: Hash[Symbol, untyped] # @dynamic model_options, model_options=
|
|
21
21
|
|
|
22
22
|
# The configured structured-output schema.
|
|
23
|
-
attr_reader :structured_output #: Riffer::Params?
|
|
23
|
+
attr_reader :structured_output #: Riffer::Params? # @dynamic structured_output
|
|
24
24
|
|
|
25
25
|
# The maximum number of LLM call steps in the tool-use loop.
|
|
26
|
-
attr_accessor :max_steps #: Numeric?
|
|
26
|
+
attr_accessor :max_steps #: Numeric? # @dynamic max_steps, max_steps=
|
|
27
27
|
|
|
28
28
|
# The configured tools.
|
|
29
|
-
attr_accessor :tools_config #: (Array[singleton(Riffer::Tool)] | Proc)?
|
|
29
|
+
attr_accessor :tools_config #: (Array[singleton(Riffer::Tool)] | Proc)? # @dynamic tools_config, tools_config=
|
|
30
30
|
|
|
31
31
|
# The accumulated +use_mcp+ tag configurations.
|
|
32
|
-
attr_reader :mcp_configs #: Array[Hash[Symbol, untyped]]
|
|
32
|
+
attr_reader :mcp_configs #: Array[Hash[Symbol, untyped]] # @dynamic mcp_configs
|
|
33
33
|
|
|
34
34
|
# The configured tool runtime.
|
|
35
|
-
attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
|
|
35
|
+
attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc) # @dynamic tool_runtime
|
|
36
36
|
|
|
37
37
|
# The configured skills.
|
|
38
|
-
attr_accessor :skills_config #: Riffer::Skills::Config?
|
|
38
|
+
attr_accessor :skills_config #: Riffer::Skills::Config? # @dynamic skills_config, skills_config=
|
|
39
39
|
|
|
40
40
|
# Registered guardrail entries keyed by phase.
|
|
41
|
-
attr_reader :guardrails #: Hash[Symbol, Array[Hash[Symbol, untyped]]]
|
|
41
|
+
attr_reader :guardrails #: Hash[Symbol, Array[Hash[Symbol, untyped]]] # @dynamic guardrails
|
|
42
42
|
|
|
43
43
|
# Builds a new Config. Raises Riffer::ArgumentError if +model+ or
|
|
44
44
|
# +instructions+ is invalid (e.g. an empty string).
|
data/lib/riffer/agent/outcome.rb
CHANGED
|
@@ -25,10 +25,10 @@ class Riffer::Agent::Outcome
|
|
|
25
25
|
PROVIDER_STOP_REASONS).freeze #: Array[Symbol]
|
|
26
26
|
|
|
27
27
|
# Why the run ended.
|
|
28
|
-
attr_reader :reason #: Symbol
|
|
28
|
+
attr_reader :reason #: Symbol # @dynamic reason
|
|
29
29
|
|
|
30
30
|
# Human-readable specifics for +reason+, when there are any.
|
|
31
|
-
attr_reader :detail #: String?
|
|
31
|
+
attr_reader :detail #: String? # @dynamic detail
|
|
32
32
|
|
|
33
33
|
# Raises Riffer::ArgumentError when +reason+ is outside VALUES.
|
|
34
34
|
#--
|
|
@@ -13,33 +13,33 @@
|
|
|
13
13
|
# end
|
|
14
14
|
class Riffer::Agent::Response
|
|
15
15
|
# The response content.
|
|
16
|
-
attr_reader :content #: String
|
|
16
|
+
attr_reader :content #: String # @dynamic content
|
|
17
17
|
|
|
18
18
|
# How the run ended.
|
|
19
|
-
attr_reader :outcome #: Riffer::Agent::Outcome
|
|
19
|
+
attr_reader :outcome #: Riffer::Agent::Outcome # @dynamic outcome
|
|
20
20
|
|
|
21
21
|
# The tripwire if execution was blocked.
|
|
22
|
-
attr_reader :tripwire #: Riffer::Guardrails::Tripwire?
|
|
22
|
+
attr_reader :tripwire #: Riffer::Guardrails::Tripwire? # @dynamic tripwire
|
|
23
23
|
|
|
24
24
|
# The modifications made by guardrails during processing.
|
|
25
|
-
attr_reader :modifications #: Array[Riffer::Guardrails::Modification]
|
|
25
|
+
attr_reader :modifications #: Array[Riffer::Guardrails::Modification] # @dynamic modifications
|
|
26
26
|
|
|
27
27
|
# The parsed structured output, if structured output was configured.
|
|
28
|
-
attr_reader :structured_output #: Hash[Symbol, untyped]?
|
|
28
|
+
attr_reader :structured_output #: Hash[Symbol, untyped]? # @dynamic structured_output
|
|
29
29
|
|
|
30
30
|
# The aggregate token usage across this run's LLM calls, if any was reported.
|
|
31
|
-
attr_reader :token_usage #: Riffer::Providers::TokenUsage?
|
|
31
|
+
attr_reader :token_usage #: Riffer::Providers::TokenUsage? # @dynamic token_usage
|
|
32
32
|
|
|
33
33
|
# The number of LLM calls made during this run (0 when a before-guardrail
|
|
34
34
|
# blocks before any call). Distinct from the session's cumulative step count.
|
|
35
|
-
attr_reader :steps #: Integer
|
|
35
|
+
attr_reader :steps #: Integer # @dynamic steps
|
|
36
36
|
|
|
37
37
|
# The full message history from the agent conversation.
|
|
38
|
-
attr_reader :messages #: Array[Riffer::Messages::Base]
|
|
38
|
+
attr_reader :messages #: Array[Riffer::Messages::Base] # @dynamic messages
|
|
39
39
|
|
|
40
40
|
# Call ids of tool_use blocks riffer filled with placeholder results this
|
|
41
41
|
# turn (when an interrupt left them unanswered and history healing is on).
|
|
42
|
-
attr_reader :healed_tool_call_ids #: Array[String]
|
|
42
|
+
attr_reader :healed_tool_call_ids #: Array[String] # @dynamic healed_tool_call_ids
|
|
43
43
|
|
|
44
44
|
#--
|
|
45
45
|
#: (
|
data/lib/riffer/agent/run.rb
CHANGED
|
@@ -461,7 +461,6 @@ module Riffer::Agent::Run
|
|
|
461
461
|
#: (Hash[(String | Symbol), untyped]?) -> Hash[String, String]
|
|
462
462
|
def normalize_tags(tags)
|
|
463
463
|
return {} if tags.nil?
|
|
464
|
-
raise Riffer::ArgumentError, "tags: must be a Hash, got #{tags.class}" unless tags.is_a?(Hash)
|
|
465
464
|
|
|
466
465
|
result = {} #: Hash[String, String]
|
|
467
466
|
tags.each do |key, value|
|
data/lib/riffer/agent/session.rb
CHANGED
|
@@ -18,7 +18,7 @@ class Riffer::Agent::Session
|
|
|
18
18
|
# @rbs @callbacks: Array[^(Riffer::Messages::Base) -> void]
|
|
19
19
|
|
|
20
20
|
# The message history.
|
|
21
|
-
attr_reader :messages #: Array[Riffer::Messages::Base]
|
|
21
|
+
attr_reader :messages #: Array[Riffer::Messages::Base] # @dynamic messages
|
|
22
22
|
|
|
23
23
|
#--
|
|
24
24
|
#: (?messages: Array[Riffer::Messages::Base]) -> void
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
# Wraps the result of structured output parsing and validation.
|
|
5
5
|
class Riffer::Agent::StructuredOutput::Result
|
|
6
6
|
# The validated object, or +nil+ on failure.
|
|
7
|
-
attr_reader :object #: Hash[Symbol, untyped]?
|
|
7
|
+
attr_reader :object #: Hash[Symbol, untyped]? # @dynamic object
|
|
8
8
|
|
|
9
9
|
# The error message, or +nil+ on success.
|
|
10
|
-
attr_reader :error #: String?
|
|
10
|
+
attr_reader :error #: String? # @dynamic error
|
|
11
11
|
|
|
12
12
|
#--
|
|
13
13
|
#: (?object: Hash[Symbol, untyped]?, ?error: String?) -> void
|
data/lib/riffer/agent.rb
CHANGED
|
@@ -214,44 +214,44 @@ class Riffer::Agent
|
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
# The conversation handle.
|
|
217
|
-
attr_reader :session #: Riffer::Agent::Session
|
|
217
|
+
attr_reader :session #: Riffer::Agent::Session # @dynamic session
|
|
218
218
|
|
|
219
219
|
# The per-instance Riffer::Agent::Config.
|
|
220
|
-
attr_reader :config #: Riffer::Agent::Config
|
|
220
|
+
attr_reader :config #: Riffer::Agent::Config # @dynamic config
|
|
221
221
|
|
|
222
222
|
# The system message built from the configured +instructions+, or +nil+ when
|
|
223
223
|
# none are configured.
|
|
224
|
-
attr_reader :instruction_message #: Riffer::Messages::System?
|
|
224
|
+
attr_reader :instruction_message #: Riffer::Messages::System? # @dynamic instruction_message
|
|
225
225
|
|
|
226
226
|
# The system message describing the configured skills catalog, or +nil+ when
|
|
227
227
|
# skills are unconfigured or the catalog is empty.
|
|
228
|
-
attr_reader :skills_message #: Riffer::Messages::System?
|
|
228
|
+
attr_reader :skills_message #: Riffer::Messages::System? # @dynamic skills_message
|
|
229
229
|
|
|
230
230
|
# The mutable runtime context shared with every +Riffer::Agent::Run+ this
|
|
231
231
|
# agent executes and threaded through all Proc-based settings.
|
|
232
|
-
attr_reader :context #: Riffer::Agent::Context
|
|
232
|
+
attr_reader :context #: Riffer::Agent::Context # @dynamic context
|
|
233
233
|
|
|
234
234
|
# The resolved provider name (the part before "/" in the model string),
|
|
235
235
|
# e.g. +"openai"+.
|
|
236
|
-
attr_reader :provider_name #: String
|
|
236
|
+
attr_reader :provider_name #: String # @dynamic provider_name
|
|
237
237
|
|
|
238
238
|
# The resolved model name (the part after "/" in the model string), used as
|
|
239
239
|
# the model argument on every LLM call.
|
|
240
|
-
attr_reader :model_name #: String
|
|
240
|
+
attr_reader :model_name #: String # @dynamic model_name
|
|
241
241
|
|
|
242
242
|
# The provider client. Public so tests can pre-queue responses on
|
|
243
243
|
# +Riffer::Providers::Mock+ before calling +#generate+.
|
|
244
|
-
attr_reader :provider #: Riffer::Providers::Base
|
|
244
|
+
attr_reader :provider #: Riffer::Providers::Base # @dynamic provider
|
|
245
245
|
|
|
246
246
|
# The +Riffer::Agent::StructuredOutput+ wrapping the configured schema, or
|
|
247
247
|
# +nil+ when not configured.
|
|
248
|
-
attr_reader :structured_output #: Riffer::Agent::StructuredOutput?
|
|
248
|
+
attr_reader :structured_output #: Riffer::Agent::StructuredOutput? # @dynamic structured_output
|
|
249
249
|
|
|
250
250
|
# The tool classes the LLM sees on every call this agent makes.
|
|
251
|
-
attr_reader :tools #: Array[singleton(Riffer::Tool)]
|
|
251
|
+
attr_reader :tools #: Array[singleton(Riffer::Tool)] # @dynamic tools
|
|
252
252
|
|
|
253
253
|
# The tool runtime instance used to execute tool calls.
|
|
254
|
-
attr_reader :tool_runtime #: Riffer::Tools::Runtime
|
|
254
|
+
attr_reader :tool_runtime #: Riffer::Tools::Runtime # @dynamic tool_runtime
|
|
255
255
|
|
|
256
256
|
# Initializes a new agent.
|
|
257
257
|
#
|
data/lib/riffer/config.rb
CHANGED
|
@@ -16,11 +16,11 @@ class Riffer::Config
|
|
|
16
16
|
class Skills
|
|
17
17
|
# The tool class the LLM calls to activate a skill; defaults to
|
|
18
18
|
# <tt>Riffer::Skills::ActivateTool</tt>.
|
|
19
|
-
attr_reader :default_activate_tool #: singleton(Riffer::Tool)
|
|
19
|
+
attr_reader :default_activate_tool #: singleton(Riffer::Tool) # @dynamic default_activate_tool
|
|
20
20
|
|
|
21
21
|
# Default skills backend for agents that declare a +skills+ block without
|
|
22
22
|
# one; defaults to +nil+.
|
|
23
|
-
attr_reader :default_backend #: (Riffer::Skills::Backend | Proc)?
|
|
23
|
+
attr_reader :default_backend #: (Riffer::Skills::Backend | Proc)? # @dynamic default_backend
|
|
24
24
|
|
|
25
25
|
#--
|
|
26
26
|
#: () -> void
|
|
@@ -61,17 +61,17 @@ class Riffer::Config
|
|
|
61
61
|
class Tracing
|
|
62
62
|
# Whether riffer emits OTEL spans; defaults to +true+, a no-op until a
|
|
63
63
|
# host wires an OTEL SDK.
|
|
64
|
-
attr_reader :enabled #: bool
|
|
64
|
+
attr_reader :enabled #: bool # @dynamic enabled
|
|
65
65
|
|
|
66
66
|
# Whether LLM-call spans capture full message content
|
|
67
67
|
# (<tt>gen_ai.input.messages</tt>, <tt>gen_ai.output.messages</tt>,
|
|
68
68
|
# <tt>gen_ai.system_instructions</tt>); defaults to +false+ — message
|
|
69
69
|
# content routinely carries sensitive data.
|
|
70
|
-
attr_reader :capture_messages #: bool
|
|
70
|
+
attr_reader :capture_messages #: bool # @dynamic capture_messages
|
|
71
71
|
|
|
72
72
|
# The backend riffer routes spans through; defaults to +nil+, a no-op.
|
|
73
73
|
# Riffer auto-detects no backend; assigning one is opt-in.
|
|
74
|
-
attr_reader :backend #: untyped
|
|
74
|
+
attr_reader :backend #: untyped # @dynamic backend
|
|
75
75
|
|
|
76
76
|
#--
|
|
77
77
|
#: () -> void
|
|
@@ -118,17 +118,17 @@ class Riffer::Config
|
|
|
118
118
|
# File-attachment-download policy for +Riffer::Messages::FilePart+ URL sources
|
|
119
119
|
class Files
|
|
120
120
|
# Allow file attachments to be downloaded to send to providers.
|
|
121
|
-
attr_reader :allow_downloads #: bool
|
|
121
|
+
attr_reader :allow_downloads #: bool # @dynamic allow_downloads
|
|
122
122
|
# Maximum file size to download before failing.
|
|
123
|
-
attr_reader :max_bytes #: Integer
|
|
123
|
+
attr_reader :max_bytes #: Integer # @dynamic max_bytes
|
|
124
124
|
# Maximum amount of time to spend downloading a file before failing.
|
|
125
|
-
attr_reader :timeout #: Integer
|
|
125
|
+
attr_reader :timeout #: Integer # @dynamic timeout
|
|
126
126
|
# Maximum number of files to include in an individual message.
|
|
127
|
-
attr_reader :max_per_message #: Integer?
|
|
127
|
+
attr_reader :max_per_message #: Integer? # @dynamic max_per_message
|
|
128
128
|
# Execution pattern for downloading files.
|
|
129
|
-
attr_reader :runner #: Riffer::Runner
|
|
129
|
+
attr_reader :runner #: Riffer::Runner # @dynamic runner
|
|
130
130
|
# The object used to fetch a URL source's bytes
|
|
131
|
-
attr_reader :downloader #: untyped
|
|
131
|
+
attr_reader :downloader #: untyped # @dynamic downloader
|
|
132
132
|
|
|
133
133
|
#--
|
|
134
134
|
#: () -> void
|
|
@@ -215,16 +215,16 @@ class Riffer::Config
|
|
|
215
215
|
# and +cache_write+ fall back to the +input+ rate when unset.
|
|
216
216
|
class Rates
|
|
217
217
|
# Input rate per million tokens.
|
|
218
|
-
attr_reader :input #: Float
|
|
218
|
+
attr_reader :input #: Float # @dynamic input
|
|
219
219
|
|
|
220
220
|
# Output rate per million tokens.
|
|
221
|
-
attr_reader :output #: Float
|
|
221
|
+
attr_reader :output #: Float # @dynamic output
|
|
222
222
|
|
|
223
223
|
# Cache-read rate per million tokens.
|
|
224
|
-
attr_reader :cache_read #: Float?
|
|
224
|
+
attr_reader :cache_read #: Float? # @dynamic cache_read
|
|
225
225
|
|
|
226
226
|
# Cache-write rate per million tokens.
|
|
227
|
-
attr_reader :cache_write #: Float?
|
|
227
|
+
attr_reader :cache_write #: Float? # @dynamic cache_write
|
|
228
228
|
|
|
229
229
|
#--
|
|
230
230
|
#: (input: Float, output: Float, ?cache_read: Float?, ?cache_write: Float?) -> void
|
|
@@ -329,34 +329,34 @@ class Riffer::Config
|
|
|
329
329
|
VALID_MESSAGE_ID_STRATEGIES = %i[none uuid uuidv7].freeze
|
|
330
330
|
|
|
331
331
|
# Amazon Bedrock configuration.
|
|
332
|
-
attr_reader :amazon_bedrock #: Riffer::Config::AmazonBedrock
|
|
332
|
+
attr_reader :amazon_bedrock #: Riffer::Config::AmazonBedrock # @dynamic amazon_bedrock
|
|
333
333
|
|
|
334
334
|
# Anthropic configuration.
|
|
335
|
-
attr_reader :anthropic #: Riffer::Config::Anthropic
|
|
335
|
+
attr_reader :anthropic #: Riffer::Config::Anthropic # @dynamic anthropic
|
|
336
336
|
|
|
337
337
|
# Azure OpenAI configuration.
|
|
338
|
-
attr_reader :azure_openai #: Riffer::Config::AzureOpenAI
|
|
338
|
+
attr_reader :azure_openai #: Riffer::Config::AzureOpenAI # @dynamic azure_openai
|
|
339
339
|
|
|
340
340
|
# Google Gemini configuration.
|
|
341
|
-
attr_reader :gemini #: Riffer::Config::Gemini
|
|
341
|
+
attr_reader :gemini #: Riffer::Config::Gemini # @dynamic gemini
|
|
342
342
|
|
|
343
343
|
# OpenAI configuration.
|
|
344
|
-
attr_reader :openai #: Riffer::Config::OpenAI
|
|
344
|
+
attr_reader :openai #: Riffer::Config::OpenAI # @dynamic openai
|
|
345
345
|
|
|
346
346
|
# OpenRouter configuration.
|
|
347
|
-
attr_reader :openrouter #: Riffer::Config::OpenRouter
|
|
347
|
+
attr_reader :openrouter #: Riffer::Config::OpenRouter # @dynamic openrouter
|
|
348
348
|
|
|
349
349
|
# Evals configuration.
|
|
350
|
-
attr_reader :evals #: Riffer::Config::Evals
|
|
350
|
+
attr_reader :evals #: Riffer::Config::Evals # @dynamic evals
|
|
351
351
|
|
|
352
352
|
# MCP configuration. +credentials+ is an optional Proc returning per-run
|
|
353
353
|
# +tools/call+ headers (or +nil+ to deny); +discovery_runner+ runs tool
|
|
354
354
|
# discovery.
|
|
355
|
-
attr_reader :mcp #: Riffer::Config::Mcp
|
|
355
|
+
attr_reader :mcp #: Riffer::Config::Mcp # @dynamic mcp
|
|
356
356
|
|
|
357
357
|
# Global tool runtime configuration (experimental); defaults to
|
|
358
358
|
# <tt>Riffer::Tools::Runtime::Inline.new</tt>.
|
|
359
|
-
attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)
|
|
359
|
+
attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc) # @dynamic tool_runtime
|
|
360
360
|
|
|
361
361
|
# Sets the global tool runtime. Raises Riffer::ArgumentError on an invalid
|
|
362
362
|
# value.
|
|
@@ -374,20 +374,20 @@ class Riffer::Config
|
|
|
374
374
|
end
|
|
375
375
|
|
|
376
376
|
# Skills-related global configuration.
|
|
377
|
-
attr_reader :skills #: Riffer::Config::Skills
|
|
377
|
+
attr_reader :skills #: Riffer::Config::Skills # @dynamic skills
|
|
378
378
|
|
|
379
379
|
# Tracing-related global configuration.
|
|
380
|
-
attr_reader :tracing #: Riffer::Config::Tracing
|
|
380
|
+
attr_reader :tracing #: Riffer::Config::Tracing # @dynamic tracing
|
|
381
381
|
|
|
382
|
-
attr_reader :files #: Riffer::Config::Files
|
|
382
|
+
attr_reader :files #: Riffer::Config::Files # @dynamic files
|
|
383
383
|
|
|
384
384
|
# Consumer-configured per-model token pricing.
|
|
385
|
-
attr_reader :pricing #: Riffer::Config::Pricing
|
|
385
|
+
attr_reader :pricing #: Riffer::Config::Pricing # @dynamic pricing
|
|
386
386
|
|
|
387
387
|
# Strategy for auto-generating message ids: +:none+ (default), +:uuid+, or
|
|
388
388
|
# +:uuidv7+. When not +:none+, messages get an +id+ at construction, and
|
|
389
389
|
# seeded messages passed to +Riffer::Agent#generate+ must carry their own.
|
|
390
|
-
attr_reader :message_id_strategy #: Symbol
|
|
390
|
+
attr_reader :message_id_strategy #: Symbol # @dynamic message_id_strategy
|
|
391
391
|
|
|
392
392
|
# Sets the message id strategy. Raises Riffer::ArgumentError unless the value
|
|
393
393
|
# is +:none+, +:uuid+, or +:uuidv7+.
|
|
@@ -404,7 +404,7 @@ class Riffer::Config
|
|
|
404
404
|
# Experimental: when +true+, riffer maintains the +tool_use+ ↔ +tool_result+
|
|
405
405
|
# invariant itself — stripping orphaned exchanges and filling interrupted
|
|
406
406
|
# ones. Defaults to +false+; the surface may change without notice.
|
|
407
|
-
attr_reader :experimental_history_healing #: bool
|
|
407
|
+
attr_reader :experimental_history_healing #: bool # @dynamic experimental_history_healing
|
|
408
408
|
|
|
409
409
|
# Sets the +experimental_history_healing+ flag, coercing boolean-ish values so
|
|
410
410
|
# an env-var +"false"+ (truthy in Ruby) doesn't silently enable healing.
|
data/lib/riffer/evals/judge.rb
CHANGED
|
@@ -28,7 +28,7 @@ class Riffer::Evals::Judge
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# The model string (provider/model format).
|
|
31
|
-
attr_reader :model #: String
|
|
31
|
+
attr_reader :model #: String # @dynamic model
|
|
32
32
|
|
|
33
33
|
# Raises Riffer::ArgumentError unless +model+ is "provider/model" format.
|
|
34
34
|
#--
|
|
@@ -109,8 +109,7 @@ class Riffer::Evals::Judge
|
|
|
109
109
|
#--
|
|
110
110
|
#: (Riffer::Messages::Assistant) -> Hash[Symbol, untyped]
|
|
111
111
|
def parse_tool_response(response)
|
|
112
|
-
tool_call = response.tool_calls.
|
|
113
|
-
raise Riffer::Error, "Invalid judge response: no tool call found" unless tool_call
|
|
112
|
+
tool_call = response.tool_calls.fetch(0) { raise Riffer::Error, "Invalid judge response: no tool call found" }
|
|
114
113
|
|
|
115
114
|
parsed = JSON.parse(tool_call[:arguments], symbolize_names: true)
|
|
116
115
|
score = parsed[:score]
|
data/lib/riffer/evals/result.rb
CHANGED
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
# Represents the result of a single evaluation.
|
|
5
5
|
class Riffer::Evals::Result
|
|
6
6
|
# The evaluator class that produced this result.
|
|
7
|
-
attr_reader :evaluator #: singleton(Riffer::Evals::Evaluator)
|
|
7
|
+
attr_reader :evaluator #: singleton(Riffer::Evals::Evaluator) # @dynamic evaluator
|
|
8
8
|
|
|
9
9
|
# The evaluation score (0.0 to 1.0).
|
|
10
|
-
attr_reader :score #: Float
|
|
10
|
+
attr_reader :score #: Float # @dynamic score
|
|
11
11
|
|
|
12
12
|
# Human-readable explanation of the score.
|
|
13
|
-
attr_reader :reason #: String?
|
|
13
|
+
attr_reader :reason #: String? # @dynamic reason
|
|
14
14
|
|
|
15
15
|
# Additional metadata from the evaluation.
|
|
16
|
-
attr_reader :metadata #: Hash[Symbol, untyped]
|
|
16
|
+
attr_reader :metadata #: Hash[Symbol, untyped] # @dynamic metadata
|
|
17
17
|
|
|
18
18
|
# Whether higher scores are better for this evaluator.
|
|
19
|
-
attr_reader :higher_is_better #: bool
|
|
19
|
+
attr_reader :higher_is_better #: bool # @dynamic higher_is_better
|
|
20
20
|
|
|
21
21
|
# Token usage for the judge call that produced this result, when the
|
|
22
22
|
# evaluator used an LLM. Nil for rule-based evaluators.
|
|
23
|
-
attr_reader :token_usage #: Riffer::Providers::TokenUsage?
|
|
23
|
+
attr_reader :token_usage #: Riffer::Providers::TokenUsage? # @dynamic token_usage
|
|
24
24
|
|
|
25
25
|
# Raises Riffer::ArgumentError if +score+ is not between 0.0 and 1.0.
|
|
26
26
|
#--
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Represents the complete result of an evaluation run across multiple scenarios.
|
|
5
5
|
class Riffer::Evals::RunResult
|
|
6
6
|
# Per-scenario evaluation results.
|
|
7
|
-
attr_reader :scenario_results #: Array[Riffer::Evals::ScenarioResult]
|
|
7
|
+
attr_reader :scenario_results #: Array[Riffer::Evals::ScenarioResult] # @dynamic scenario_results
|
|
8
8
|
|
|
9
9
|
#--
|
|
10
10
|
#: (scenario_results: Array[Riffer::Evals::ScenarioResult]) -> void
|
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
# Represents the result of evaluating a single scenario.
|
|
5
5
|
class Riffer::Evals::ScenarioResult
|
|
6
6
|
# The input that was evaluated.
|
|
7
|
-
attr_reader :input #: String
|
|
7
|
+
attr_reader :input #: String # @dynamic input
|
|
8
8
|
|
|
9
9
|
# The agent output for this scenario.
|
|
10
|
-
attr_reader :output #: String
|
|
10
|
+
attr_reader :output #: String # @dynamic output
|
|
11
11
|
|
|
12
12
|
# The ground truth used during evaluation.
|
|
13
|
-
attr_reader :ground_truth #: String?
|
|
13
|
+
attr_reader :ground_truth #: String? # @dynamic ground_truth
|
|
14
14
|
|
|
15
15
|
# Individual evaluation results.
|
|
16
|
-
attr_reader :results #: Array[Riffer::Evals::Result]
|
|
16
|
+
attr_reader :results #: Array[Riffer::Evals::Result] # @dynamic results
|
|
17
17
|
|
|
18
18
|
# The full message history from the agent conversation.
|
|
19
|
-
attr_reader :messages #: Array[Riffer::Messages::Base]
|
|
19
|
+
attr_reader :messages #: Array[Riffer::Messages::Base] # @dynamic messages
|
|
20
20
|
|
|
21
21
|
# Token usage the agent under test spent generating this scenario's output.
|
|
22
|
-
attr_reader :token_usage #: Riffer::Providers::TokenUsage?
|
|
22
|
+
attr_reader :token_usage #: Riffer::Providers::TokenUsage? # @dynamic token_usage
|
|
23
23
|
|
|
24
24
|
#--
|
|
25
25
|
#: (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
# Records a guardrail transformation event.
|
|
5
5
|
class Riffer::Guardrails::Modification
|
|
6
6
|
# The guardrail class that transformed data.
|
|
7
|
-
attr_reader :guardrail #: singleton(Riffer::Guardrail)
|
|
7
|
+
attr_reader :guardrail #: singleton(Riffer::Guardrail) # @dynamic guardrail
|
|
8
8
|
|
|
9
9
|
# The phase when the transformation occurred (:before or :after).
|
|
10
|
-
attr_reader :phase #: Symbol
|
|
10
|
+
attr_reader :phase #: Symbol # @dynamic phase
|
|
11
11
|
|
|
12
12
|
# The indices of messages that were changed.
|
|
13
|
-
attr_reader :message_indices #: Array[Integer]
|
|
13
|
+
attr_reader :message_indices #: Array[Integer] # @dynamic message_indices
|
|
14
14
|
|
|
15
15
|
#--
|
|
16
16
|
#: (guardrail: singleton(Riffer::Guardrail), phase: Symbol, message_indices: Array[Integer]) -> void
|
|
@@ -7,13 +7,13 @@ class Riffer::Guardrails::Result
|
|
|
7
7
|
TYPES = %i[pass transform block].freeze #: Array[Symbol]
|
|
8
8
|
|
|
9
9
|
# The result type (:pass, :transform, or :block).
|
|
10
|
-
attr_reader :type #: Symbol
|
|
10
|
+
attr_reader :type #: Symbol # @dynamic type
|
|
11
11
|
|
|
12
12
|
# The data (for pass/transform) or reason (for block).
|
|
13
|
-
attr_reader :data #: untyped
|
|
13
|
+
attr_reader :data #: untyped # @dynamic data
|
|
14
14
|
|
|
15
15
|
# Optional metadata for block results.
|
|
16
|
-
attr_reader :metadata #: Hash[Symbol, untyped]?
|
|
16
|
+
attr_reader :metadata #: Hash[Symbol, untyped]? # @dynamic metadata
|
|
17
17
|
|
|
18
18
|
class << self
|
|
19
19
|
# Creates a pass result that continues with unchanged data.
|