riffer 0.48.0 → 0.50.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/.claude/rules/comments.md +2 -4
- data/.claude/rules/rbs-inline.md +2 -12
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +32 -0
- data/docs/AGENTS.md +3 -5
- data/docs/AGENT_LIFECYCLE.md +13 -16
- data/docs/CONFIGURATION.md +22 -33
- data/docs/STREAM_EVENTS.md +3 -3
- data/docs/TOOL_ADVANCED.md +1 -3
- data/docs/TRACING.md +1 -1
- data/docs/providers/AMAZON_BEDROCK.md +31 -0
- data/docs/providers/ANTHROPIC.md +10 -0
- data/docs/providers/AZURE_OPENAI.md +2 -0
- data/docs/providers/OPENAI.md +22 -0
- data/docs/providers/OPENROUTER.md +18 -1
- data/docs-site/build.rb +0 -6
- data/docs-site/check.rb +1 -5
- data/lib/riffer/agent/config.rb +4 -45
- data/lib/riffer/agent/context.rb +2 -26
- data/lib/riffer/agent/outcome.rb +0 -20
- data/lib/riffer/agent/response.rb +0 -38
- data/lib/riffer/agent/run.rb +11 -43
- data/lib/riffer/agent/serializer.rb +10 -39
- data/lib/riffer/agent/session/repair.rb +3 -15
- data/lib/riffer/agent/session.rb +25 -38
- data/lib/riffer/agent/structured_output/result.rb +0 -8
- data/lib/riffer/agent/structured_output.rb +0 -7
- data/lib/riffer/agent.rb +10 -130
- data/lib/riffer/config/amazon_bedrock.rb +30 -0
- data/lib/riffer/config/anthropic.rb +21 -0
- data/lib/riffer/config/azure_open_ai.rb +30 -0
- data/lib/riffer/config/evals.rb +18 -0
- data/lib/riffer/config/files.rb +61 -0
- data/lib/riffer/config/gemini.rb +21 -0
- data/lib/riffer/config/mcp.rb +31 -0
- data/lib/riffer/config/open_ai.rb +30 -0
- data/lib/riffer/config/open_router.rb +21 -0
- data/lib/riffer/config/pricing/rates.rb +36 -0
- data/lib/riffer/config/pricing.rb +64 -0
- data/lib/riffer/config/skills.rb +38 -0
- data/lib/riffer/config/tracing.rb +43 -0
- data/lib/riffer/config.rb +3 -362
- data/lib/riffer/evals/evaluator.rb +1 -29
- data/lib/riffer/evals/evaluator_runner.rb +0 -15
- data/lib/riffer/evals/judge.rb +0 -8
- data/lib/riffer/evals/result.rb +1 -11
- data/lib/riffer/evals/run_result.rb +0 -12
- data/lib/riffer/evals/scenario_result.rb +0 -19
- data/lib/riffer/files/downloader.rb +2 -3
- data/lib/riffer/files/resolver.rb +2 -7
- data/lib/riffer/guardrail.rb +2 -22
- data/lib/riffer/guardrails/modification.rb +0 -8
- data/lib/riffer/guardrails/result.rb +0 -17
- data/lib/riffer/guardrails/runner.rb +2 -11
- data/lib/riffer/guardrails/tripwire.rb +0 -8
- data/lib/riffer/guardrails.rb +0 -2
- data/lib/riffer/helpers/boolean.rb +0 -4
- data/lib/riffer/helpers/call_or_value.rb +0 -3
- data/lib/riffer/helpers/deep_dup.rb +3 -8
- data/lib/riffer/helpers/dependencies.rb +0 -4
- data/lib/riffer/helpers/identifier.rb +3 -12
- data/lib/riffer/helpers/validate.rb +42 -0
- data/lib/riffer/mcp/authenticated_tool.rb +4 -12
- data/lib/riffer/mcp/client.rb +0 -7
- data/lib/riffer/mcp/manifest.rb +3 -7
- data/lib/riffer/mcp/registration.rb +0 -10
- data/lib/riffer/mcp/registry.rb +0 -9
- data/lib/riffer/mcp/search_tool.rb +0 -4
- data/lib/riffer/mcp/tool.rb +1 -4
- data/lib/riffer/mcp/tool_factory.rb +3 -6
- data/lib/riffer/mcp.rb +2 -23
- data/lib/riffer/messages/assistant/reasoning_part.rb +4 -21
- data/lib/riffer/messages/assistant/tool_call.rb +1 -9
- data/lib/riffer/messages/assistant.rb +1 -21
- data/lib/riffer/messages/base.rb +0 -12
- data/lib/riffer/messages/system.rb +0 -3
- data/lib/riffer/messages/tool.rb +0 -15
- data/lib/riffer/messages/user/file_part.rb +2 -30
- data/lib/riffer/messages/user.rb +0 -4
- data/lib/riffer/params/boolean.rb +1 -5
- data/lib/riffer/params/param.rb +3 -31
- data/lib/riffer/params.rb +8 -41
- data/lib/riffer/providers/amazon_bedrock.rb +98 -46
- data/lib/riffer/providers/anthropic.rb +65 -65
- data/lib/riffer/providers/azure_open_ai.rb +10 -8
- data/lib/riffer/providers/base.rb +7 -28
- data/lib/riffer/providers/finish_reason.rb +0 -6
- data/lib/riffer/providers/gemini/client.rb +4 -17
- data/lib/riffer/providers/gemini.rb +4 -10
- data/lib/riffer/providers/mock.rb +1 -26
- data/lib/riffer/providers/open_ai.rb +94 -46
- data/lib/riffer/providers/open_router.rb +105 -30
- data/lib/riffer/providers/repository.rb +2 -14
- data/lib/riffer/providers/token_usage.rb +5 -14
- data/lib/riffer/registrable.rb +11 -45
- data/lib/riffer/runner/fibers.rb +1 -6
- data/lib/riffer/runner/sequential.rb +0 -1
- data/lib/riffer/runner/threaded.rb +0 -3
- data/lib/riffer/runner.rb +0 -3
- data/lib/riffer/skills/activate_tool.rb +0 -3
- data/lib/riffer/skills/adapter.rb +0 -8
- data/lib/riffer/skills/backend.rb +2 -7
- data/lib/riffer/skills/config.rb +4 -20
- data/lib/riffer/skills/context.rb +0 -29
- data/lib/riffer/skills/filesystem_backend.rb +0 -7
- data/lib/riffer/skills/frontmatter.rb +2 -16
- data/lib/riffer/skills/markdown_adapter.rb +3 -6
- data/lib/riffer/skills/xml_adapter.rb +0 -3
- data/lib/riffer/stream_events/base.rb +0 -3
- data/lib/riffer/stream_events/finish_reason_done.rb +1 -6
- data/lib/riffer/stream_events/guardrail_modification.rb +0 -10
- data/lib/riffer/stream_events/guardrail_tripwire.rb +0 -10
- data/lib/riffer/stream_events/interrupt.rb +2 -12
- data/lib/riffer/stream_events/reasoning_delta.rb +0 -3
- data/lib/riffer/stream_events/reasoning_done.rb +1 -5
- data/lib/riffer/stream_events/skill_activation.rb +0 -3
- data/lib/riffer/stream_events/text_delta.rb +0 -2
- data/lib/riffer/stream_events/text_done.rb +0 -2
- data/lib/riffer/stream_events/token_usage_done.rb +0 -2
- data/lib/riffer/stream_events/tool_call_delta.rb +1 -5
- data/lib/riffer/stream_events/tool_call_done.rb +0 -5
- data/lib/riffer/stream_events/web_search_done.rb +0 -3
- data/lib/riffer/stream_events/web_search_status.rb +1 -5
- data/lib/riffer/testing/minitest.rb +4 -5
- data/lib/riffer/testing.rb +5 -38
- data/lib/riffer/tool.rb +2 -28
- data/lib/riffer/tools/response.rb +3 -32
- data/lib/riffer/tools/runtime/fibers.rb +0 -6
- data/lib/riffer/tools/runtime/inline.rb +0 -1
- data/lib/riffer/tools/runtime/threaded.rb +0 -6
- data/lib/riffer/tools/runtime.rb +5 -26
- data/lib/riffer/tools/toolable.rb +0 -37
- data/lib/riffer/tracing/capture.rb +3 -5
- data/lib/riffer/tracing/no_op.rb +0 -7
- data/lib/riffer/tracing/otel.rb +7 -16
- data/lib/riffer/tracing/stream_recorder.rb +0 -7
- data/lib/riffer/tracing.rb +4 -27
- data/lib/riffer/version.rb +1 -1
- data/lib/riffer.rb +2 -30
- data/sig/generated/riffer/agent/config.rbs +12 -48
- data/sig/generated/riffer/agent/context.rbs +2 -26
- data/sig/generated/riffer/agent/outcome.rbs +0 -20
- data/sig/generated/riffer/agent/response.rbs +1 -29
- data/sig/generated/riffer/agent/run.rbs +1 -27
- data/sig/generated/riffer/agent/serializer.rbs +2 -27
- data/sig/generated/riffer/agent/session/repair.rbs +2 -10
- data/sig/generated/riffer/agent/session.rbs +10 -36
- data/sig/generated/riffer/agent/structured_output/result.rbs +0 -7
- data/sig/generated/riffer/agent/structured_output.rbs +0 -7
- data/sig/generated/riffer/agent.rbs +5 -121
- data/sig/generated/riffer/config/amazon_bedrock.rbs +21 -0
- data/sig/generated/riffer/config/anthropic.rbs +15 -0
- data/sig/generated/riffer/config/azure_open_ai.rbs +21 -0
- data/sig/generated/riffer/config/evals.rbs +13 -0
- data/sig/generated/riffer/config/files.rbs +43 -0
- data/sig/generated/riffer/config/gemini.rbs +15 -0
- data/sig/generated/riffer/config/mcp.rbs +19 -0
- data/sig/generated/riffer/config/open_ai.rbs +21 -0
- data/sig/generated/riffer/config/open_router.rbs +15 -0
- data/sig/generated/riffer/config/pricing/rates.rbs +19 -0
- data/sig/generated/riffer/config/pricing.rbs +31 -0
- data/sig/generated/riffer/config/skills.rbs +19 -0
- data/sig/generated/riffer/config/tracing.rbs +25 -0
- data/sig/generated/riffer/config.rbs +2 -307
- data/sig/generated/riffer/evals/evaluator.rbs +0 -28
- data/sig/generated/riffer/evals/evaluator_runner.rbs +0 -14
- data/sig/generated/riffer/evals/judge.rbs +0 -7
- data/sig/generated/riffer/evals/result.rbs +1 -11
- data/sig/generated/riffer/evals/run_result.rbs +0 -12
- data/sig/generated/riffer/evals/scenario_result.rbs +0 -14
- data/sig/generated/riffer/files/resolver.rbs +0 -7
- data/sig/generated/riffer/guardrail.rbs +2 -22
- data/sig/generated/riffer/guardrails/modification.rbs +0 -6
- data/sig/generated/riffer/guardrails/result.rbs +0 -15
- data/sig/generated/riffer/guardrails/runner.rbs +0 -11
- data/sig/generated/riffer/guardrails/tripwire.rbs +0 -8
- data/sig/generated/riffer/guardrails.rbs +0 -2
- data/sig/generated/riffer/helpers/boolean.rbs +0 -4
- data/sig/generated/riffer/helpers/call_or_value.rbs +0 -3
- data/sig/generated/riffer/helpers/deep_dup.rbs +0 -8
- data/sig/generated/riffer/helpers/dependencies.rbs +0 -4
- data/sig/generated/riffer/helpers/identifier.rbs +0 -12
- data/sig/generated/riffer/helpers/validate.rbs +21 -0
- data/sig/generated/riffer/mcp/authenticated_tool.rbs +0 -5
- data/sig/generated/riffer/mcp/client.rbs +0 -7
- data/sig/generated/riffer/mcp/manifest.rbs +3 -7
- data/sig/generated/riffer/mcp/registration.rbs +0 -10
- data/sig/generated/riffer/mcp/registry.rbs +0 -9
- data/sig/generated/riffer/mcp/search_tool.rbs +0 -4
- data/sig/generated/riffer/mcp/tool.rbs +0 -4
- data/sig/generated/riffer/mcp/tool_factory.rbs +0 -6
- data/sig/generated/riffer/mcp.rbs +2 -22
- data/sig/generated/riffer/messages/assistant/reasoning_part.rbs +4 -17
- data/sig/generated/riffer/messages/assistant/tool_call.rbs +1 -9
- data/sig/generated/riffer/messages/assistant.rbs +10 -30
- data/sig/generated/riffer/messages/base.rbs +0 -12
- data/sig/generated/riffer/messages/system.rbs +0 -3
- data/sig/generated/riffer/messages/tool.rbs +0 -12
- data/sig/generated/riffer/messages/user/file_part.rbs +0 -30
- data/sig/generated/riffer/messages/user.rbs +0 -4
- data/sig/generated/riffer/params/boolean.rbs +1 -4
- data/sig/generated/riffer/params/param.rbs +0 -31
- data/sig/generated/riffer/params.rbs +4 -40
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +26 -26
- data/sig/generated/riffer/providers/anthropic.rbs +23 -22
- data/sig/generated/riffer/providers/azure_open_ai.rbs +5 -8
- data/sig/generated/riffer/providers/base.rbs +0 -28
- data/sig/generated/riffer/providers/finish_reason.rbs +0 -6
- data/sig/generated/riffer/providers/gemini/client.rbs +4 -17
- data/sig/generated/riffer/providers/gemini.rbs +0 -6
- data/sig/generated/riffer/providers/mock.rbs +0 -26
- data/sig/generated/riffer/providers/open_ai.rbs +36 -19
- data/sig/generated/riffer/providers/open_router.rbs +33 -14
- data/sig/generated/riffer/providers/repository.rbs +2 -14
- data/sig/generated/riffer/providers/token_usage.rbs +5 -14
- data/sig/generated/riffer/registrable.rbs +0 -45
- data/sig/generated/riffer/runner/fibers.rbs +0 -6
- data/sig/generated/riffer/runner/sequential.rbs +0 -1
- data/sig/generated/riffer/runner/threaded.rbs +0 -3
- data/sig/generated/riffer/runner.rbs +0 -3
- data/sig/generated/riffer/skills/activate_tool.rbs +0 -3
- data/sig/generated/riffer/skills/adapter.rbs +0 -8
- data/sig/generated/riffer/skills/backend.rbs +2 -7
- data/sig/generated/riffer/skills/config.rbs +0 -20
- data/sig/generated/riffer/skills/context.rbs +0 -29
- data/sig/generated/riffer/skills/filesystem_backend.rbs +0 -7
- data/sig/generated/riffer/skills/frontmatter.rbs +2 -16
- data/sig/generated/riffer/skills/markdown_adapter.rbs +0 -6
- data/sig/generated/riffer/skills/xml_adapter.rbs +0 -3
- data/sig/generated/riffer/stream_events/base.rbs +0 -3
- data/sig/generated/riffer/stream_events/finish_reason_done.rbs +1 -6
- data/sig/generated/riffer/stream_events/guardrail_modification.rbs +0 -10
- data/sig/generated/riffer/stream_events/guardrail_tripwire.rbs +0 -10
- data/sig/generated/riffer/stream_events/interrupt.rbs +2 -10
- data/sig/generated/riffer/stream_events/reasoning_delta.rbs +0 -3
- data/sig/generated/riffer/stream_events/reasoning_done.rbs +1 -5
- data/sig/generated/riffer/stream_events/skill_activation.rbs +0 -3
- data/sig/generated/riffer/stream_events/text_delta.rbs +0 -2
- data/sig/generated/riffer/stream_events/text_done.rbs +0 -2
- data/sig/generated/riffer/stream_events/token_usage_done.rbs +0 -2
- data/sig/generated/riffer/stream_events/tool_call_delta.rbs +1 -5
- data/sig/generated/riffer/stream_events/tool_call_done.rbs +0 -5
- data/sig/generated/riffer/stream_events/web_search_done.rbs +0 -3
- data/sig/generated/riffer/stream_events/web_search_status.rbs +1 -5
- data/sig/generated/riffer/testing.rbs +0 -38
- data/sig/generated/riffer/tool.rbs +0 -27
- data/sig/generated/riffer/tools/response.rbs +3 -29
- data/sig/generated/riffer/tools/runtime/fibers.rbs +0 -5
- data/sig/generated/riffer/tools/runtime/inline.rbs +0 -1
- data/sig/generated/riffer/tools/runtime/threaded.rbs +0 -5
- data/sig/generated/riffer/tools/runtime.rbs +2 -24
- data/sig/generated/riffer/tools/toolable.rbs +0 -37
- data/sig/generated/riffer/tracing/capture.rbs +2 -5
- data/sig/generated/riffer/tracing/no_op.rbs +0 -7
- data/sig/generated/riffer/tracing/otel.rbs +7 -16
- data/sig/generated/riffer/tracing/stream_recorder.rbs +0 -2
- data/sig/generated/riffer/tracing.rbs +4 -23
- data/sig/generated/riffer.rbs +2 -29
- data/sig/manual/riffer/helpers/validate.rbs +5 -0
- metadata +30 -1
data/lib/riffer/config.rb
CHANGED
|
@@ -1,365 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Configuration for the Riffer framework.
|
|
5
4
|
class Riffer::Config
|
|
6
|
-
AmazonBedrock = Struct.new(:api_token, :region, :client)
|
|
7
|
-
Anthropic = Struct.new(:api_key, :client)
|
|
8
|
-
AzureOpenAI = Struct.new(:api_key, :endpoint, :client)
|
|
9
|
-
Gemini = Struct.new(:api_key, :client)
|
|
10
|
-
OpenAI = Struct.new(:api_key, :base_url, :client)
|
|
11
|
-
OpenRouter = Struct.new(:api_key, :client)
|
|
12
|
-
Evals = Struct.new(:judge_model)
|
|
13
|
-
Mcp = Struct.new(:credentials, :discovery_runner)
|
|
14
|
-
|
|
15
|
-
# Skills-related global configuration.
|
|
16
|
-
class Skills
|
|
17
|
-
# The tool class the LLM calls to activate a skill; defaults to
|
|
18
|
-
# <tt>Riffer::Skills::ActivateTool</tt>.
|
|
19
|
-
attr_reader :default_activate_tool #: singleton(Riffer::Tool) # @dynamic default_activate_tool
|
|
20
|
-
|
|
21
|
-
# Default skills backend for agents that declare a +skills+ block without
|
|
22
|
-
# one; defaults to +nil+.
|
|
23
|
-
attr_reader :default_backend #: (Riffer::Skills::Backend | Proc)? # @dynamic default_backend
|
|
24
|
-
|
|
25
|
-
#--
|
|
26
|
-
#: () -> void
|
|
27
|
-
def initialize
|
|
28
|
-
@default_activate_tool = Riffer::Skills::ActivateTool
|
|
29
|
-
@default_backend = nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Sets the default skill activation tool class. Raises Riffer::ArgumentError
|
|
33
|
-
# on an invalid value.
|
|
34
|
-
#--
|
|
35
|
-
#: (singleton(Riffer::Tool)) -> void
|
|
36
|
-
def default_activate_tool=(value)
|
|
37
|
-
unless value.is_a?(Class) && value < Riffer::Tool
|
|
38
|
-
raise Riffer::ArgumentError,
|
|
39
|
-
"default_activate_tool must be a Riffer::Tool subclass"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
@default_activate_tool = value
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Sets the default skills backend. Raises Riffer::ArgumentError on an
|
|
46
|
-
# invalid value.
|
|
47
|
-
#--
|
|
48
|
-
#: ((Riffer::Skills::Backend | Proc)?) -> void
|
|
49
|
-
def default_backend=(value)
|
|
50
|
-
valid = value.nil? || value.is_a?(Riffer::Skills::Backend) || value.is_a?(Proc)
|
|
51
|
-
unless valid
|
|
52
|
-
raise Riffer::ArgumentError,
|
|
53
|
-
"default_backend must be a Riffer::Skills::Backend instance, Proc, or nil"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
@default_backend = value
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Tracing-related global configuration.
|
|
61
|
-
class Tracing
|
|
62
|
-
# Whether riffer emits OTEL spans; defaults to +true+, a no-op until a
|
|
63
|
-
# host wires an OTEL SDK.
|
|
64
|
-
attr_reader :enabled #: bool # @dynamic enabled
|
|
65
|
-
|
|
66
|
-
# Whether LLM-call spans capture full message content
|
|
67
|
-
# (<tt>gen_ai.input.messages</tt>, <tt>gen_ai.output.messages</tt>,
|
|
68
|
-
# <tt>gen_ai.system_instructions</tt>); defaults to +false+ — message
|
|
69
|
-
# content routinely carries sensitive data.
|
|
70
|
-
attr_reader :capture_messages #: bool # @dynamic capture_messages
|
|
71
|
-
|
|
72
|
-
# The backend riffer routes spans through; defaults to +nil+, a no-op.
|
|
73
|
-
# Riffer auto-detects no backend; assigning one is opt-in.
|
|
74
|
-
attr_reader :backend #: untyped # @dynamic backend
|
|
75
|
-
|
|
76
|
-
#--
|
|
77
|
-
#: () -> void
|
|
78
|
-
def initialize
|
|
79
|
-
@enabled = true
|
|
80
|
-
@capture_messages = false
|
|
81
|
-
@backend = nil
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Sets the enabled flag, coercing boolean-ish values so an env-var
|
|
85
|
-
# +"false"+ (truthy in Ruby) doesn't silently keep tracing on. Raises
|
|
86
|
-
# Riffer::ArgumentError on an unrecognized value.
|
|
87
|
-
#--
|
|
88
|
-
#: (untyped) -> void
|
|
89
|
-
def enabled=(value)
|
|
90
|
-
@enabled = Riffer::Helpers::Boolean.coerce(value, attribute: "enabled")
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Sets the capture_messages flag, coercing boolean-ish values so an
|
|
94
|
-
# env-var +"false"+ (truthy in Ruby) doesn't silently enable content
|
|
95
|
-
# capture. Raises Riffer::ArgumentError on an unrecognized value.
|
|
96
|
-
#--
|
|
97
|
-
#: (untyped) -> void
|
|
98
|
-
def capture_messages=(value)
|
|
99
|
-
@capture_messages = Riffer::Helpers::Boolean.coerce(value, attribute: "capture_messages")
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Sets the tracing backend riffer routes spans through. Raises
|
|
103
|
-
# Riffer::ArgumentError unless the value is +nil+ or responds to the full
|
|
104
|
-
# delegated contract: +in_span+, +current_context+, and +with_context+.
|
|
105
|
-
#--
|
|
106
|
-
#: (untyped) -> void
|
|
107
|
-
def backend=(value)
|
|
108
|
-
contract = %i[in_span current_context with_context]
|
|
109
|
-
unless value.nil? || contract.all? { |method| value.respond_to?(method) }
|
|
110
|
-
raise Riffer::ArgumentError, "tracing backend must respond to #in_span, #current_context, and #with_context"
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
@backend = value
|
|
114
|
-
Riffer::Tracing.reset!
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# File-attachment-download policy for +Riffer::Messages::User::FilePart+ URL sources
|
|
119
|
-
class Files
|
|
120
|
-
# Allow file attachments to be downloaded to send to providers.
|
|
121
|
-
attr_reader :allow_downloads #: bool # @dynamic allow_downloads
|
|
122
|
-
# Maximum file size to download before failing.
|
|
123
|
-
attr_reader :max_bytes #: Integer # @dynamic max_bytes
|
|
124
|
-
# Maximum amount of time to spend downloading a file before failing.
|
|
125
|
-
attr_reader :timeout #: Integer # @dynamic timeout
|
|
126
|
-
# Maximum number of files to include in an individual message.
|
|
127
|
-
attr_reader :max_per_message #: Integer? # @dynamic max_per_message
|
|
128
|
-
# Execution pattern for downloading files.
|
|
129
|
-
attr_reader :runner #: Riffer::Runner # @dynamic runner
|
|
130
|
-
# The object used to fetch a URL source's bytes
|
|
131
|
-
attr_reader :downloader #: untyped # @dynamic downloader
|
|
132
|
-
|
|
133
|
-
#--
|
|
134
|
-
#: () -> void
|
|
135
|
-
def initialize
|
|
136
|
-
@allow_downloads = false
|
|
137
|
-
@max_bytes = 3_500_000
|
|
138
|
-
@timeout = 60
|
|
139
|
-
@max_per_message = nil
|
|
140
|
-
@runner = Riffer::Runner::Sequential.new
|
|
141
|
-
@downloader = Riffer::Files::Downloader.new
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Sets the allow_downloads flag, coercing boolean-ish values so an env-var
|
|
145
|
-
# +"false"+ (truthy in Ruby) doesn't silently enable downloads. Raises
|
|
146
|
-
# Riffer::ArgumentError on an unrecognized value.
|
|
147
|
-
#--
|
|
148
|
-
#: (untyped) -> void
|
|
149
|
-
def allow_downloads=(value)
|
|
150
|
-
@allow_downloads = Riffer::Helpers::Boolean.coerce(value, attribute: "allow_downloads")
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Sets max_bytes, provided value is a positive integer.
|
|
154
|
-
# Raises Riffer::ArgumentError if value is not an Integer or less than or equal to 0.
|
|
155
|
-
#--
|
|
156
|
-
#: (untyped) -> void
|
|
157
|
-
def max_bytes=(value)
|
|
158
|
-
raise Riffer::ArgumentError, "max_bytes must be a positive integer" unless value.is_a?(Integer) && value.positive?
|
|
159
|
-
|
|
160
|
-
@max_bytes = value
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# Sets timeout, provided value is a positive integer.
|
|
164
|
-
# Raises Riffer::ArgumentError if value is not an Integer or is less than or equal to 0.
|
|
165
|
-
#--
|
|
166
|
-
#: (untyped) -> void
|
|
167
|
-
def timeout=(value)
|
|
168
|
-
raise Riffer::ArgumentError, "timeout must be a positive integer" unless value.is_a?(Integer) && value.positive?
|
|
169
|
-
|
|
170
|
-
@timeout = value
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Sets max_per_message, provided value is either nil or a positive integer.
|
|
174
|
-
# Raises Riffer::ArgumentError if value is not an Integer or nil, or is less than or equal to 0.
|
|
175
|
-
#--
|
|
176
|
-
#: (untyped) -> void
|
|
177
|
-
def max_per_message=(value)
|
|
178
|
-
if value.is_a?(Integer) && value.positive?
|
|
179
|
-
@max_per_message = value
|
|
180
|
-
elsif value.nil?
|
|
181
|
-
@max_per_message = nil
|
|
182
|
-
else
|
|
183
|
-
raise Riffer::ArgumentError, "max_per_message must be a positive integer or nil"
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Sets the runner used to process file downloads, provided value is a Riffer::Runner.
|
|
188
|
-
# Raises Riffer::ArgumentError if value is not a Riffer::Runner.
|
|
189
|
-
#--
|
|
190
|
-
#: (untyped) -> void
|
|
191
|
-
def runner=(value)
|
|
192
|
-
valid = value.is_a?(Riffer::Runner)
|
|
193
|
-
raise Riffer::ArgumentError, "runner must be a Riffer::Runner instance" unless valid
|
|
194
|
-
|
|
195
|
-
@runner = value
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Sets the object used to download bytes from a URL.
|
|
199
|
-
# Raises Riffer::ArgumentError if value does not respond to +#call+.
|
|
200
|
-
#--
|
|
201
|
-
#: (untyped) -> void
|
|
202
|
-
def downloader=(value)
|
|
203
|
-
raise Riffer::ArgumentError, "downloader must respond to #call" unless value.respond_to?(:call)
|
|
204
|
-
|
|
205
|
-
@downloader = value
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# Consumer-configured token pricing, keyed by +provider/model+ id. Riffer
|
|
210
|
-
# ships no price table, so an unconfigured model carries no cost.
|
|
211
|
-
class Pricing
|
|
212
|
-
# @rbs @rates: Hash[String, Riffer::Config::Pricing::Rates]
|
|
213
|
-
|
|
214
|
-
# Per-million-token rates for one model's four token buckets. +cache_read+
|
|
215
|
-
# and +cache_write+ fall back to the +input+ rate when unset.
|
|
216
|
-
class Rates
|
|
217
|
-
# Input rate per million tokens.
|
|
218
|
-
attr_reader :input #: Float # @dynamic input
|
|
219
|
-
|
|
220
|
-
# Output rate per million tokens.
|
|
221
|
-
attr_reader :output #: Float # @dynamic output
|
|
222
|
-
|
|
223
|
-
# Cache-read rate per million tokens.
|
|
224
|
-
attr_reader :cache_read #: Float? # @dynamic cache_read
|
|
225
|
-
|
|
226
|
-
# Cache-write rate per million tokens.
|
|
227
|
-
attr_reader :cache_write #: Float? # @dynamic cache_write
|
|
228
|
-
|
|
229
|
-
#--
|
|
230
|
-
#: (input: Float, output: Float, ?cache_read: Float?, ?cache_write: Float?) -> void
|
|
231
|
-
def initialize(input:, output:, cache_read: nil, cache_write: nil)
|
|
232
|
-
@input = input
|
|
233
|
-
@output = output
|
|
234
|
-
@cache_read = cache_read
|
|
235
|
-
@cache_write = cache_write
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
# Returns the cost for the given token counts.
|
|
239
|
-
#--
|
|
240
|
-
#: (input_tokens: Integer, output_tokens: Integer, ?cache_read_tokens: Integer?, ?cache_write_tokens: Integer?) -> Float
|
|
241
|
-
def cost_for(input_tokens:, output_tokens:, cache_read_tokens: nil, cache_write_tokens: nil)
|
|
242
|
-
read = cache_read_tokens || 0
|
|
243
|
-
write = cache_write_tokens || 0
|
|
244
|
-
uncached = input_tokens - read - write
|
|
245
|
-
uncached = 0 if uncached.negative?
|
|
246
|
-
|
|
247
|
-
per_million = (uncached * input) +
|
|
248
|
-
(read * (cache_read || input)) +
|
|
249
|
-
(write * (cache_write || input)) +
|
|
250
|
-
(output_tokens * output)
|
|
251
|
-
per_million / 1_000_000.0
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
#--
|
|
256
|
-
#: () -> void
|
|
257
|
-
def initialize
|
|
258
|
-
@rates = {}
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# Registers per-million-token rates for a +provider/model+ id/s. Raises
|
|
262
|
-
# Riffer::ArgumentError on a malformed id or a negative/non-numeric rate.
|
|
263
|
-
#--
|
|
264
|
-
#: ((String | Array[String]), input: Numeric, output: Numeric, ?cache_read: Numeric?, ?cache_write: Numeric?) -> void
|
|
265
|
-
def set(models, input:, output:, cache_read: nil, cache_write: nil)
|
|
266
|
-
ids = models.is_a?(Array) ? models : [models]
|
|
267
|
-
raise Riffer::ArgumentError, "at least one model id is required" if ids.empty?
|
|
268
|
-
|
|
269
|
-
ids.each { |id| validate_model!(id) }
|
|
270
|
-
|
|
271
|
-
rates = Rates.new(
|
|
272
|
-
input: coerce_rate(input, "input"),
|
|
273
|
-
output: coerce_rate(output, "output"),
|
|
274
|
-
cache_read: coerce_optional_rate(cache_read, "cache_read"),
|
|
275
|
-
cache_write: coerce_optional_rate(cache_write, "cache_write"),
|
|
276
|
-
)
|
|
277
|
-
ids.each { |id| @rates[id] = rates }
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
# Returns the rates registered for a +provider/model+ id.
|
|
281
|
-
#--
|
|
282
|
-
#: (String) -> Riffer::Config::Pricing::Rates?
|
|
283
|
-
def rates_for(model)
|
|
284
|
-
@rates[model]
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Returns true when no rates are registered.
|
|
288
|
-
#--
|
|
289
|
-
#: () -> bool
|
|
290
|
-
def empty?
|
|
291
|
-
@rates.empty?
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
private
|
|
295
|
-
|
|
296
|
-
#--
|
|
297
|
-
#: (String) -> void
|
|
298
|
-
def validate_model!(model)
|
|
299
|
-
segments = model.to_s.split("/", 2)
|
|
300
|
-
valid = segments.length == 2 && segments.none? { |segment| segment.strip.empty? }
|
|
301
|
-
return if valid
|
|
302
|
-
|
|
303
|
-
raise Riffer::ArgumentError,
|
|
304
|
-
"pricing model id must be in \"provider/model\" form, got #{model.inspect}"
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
#--
|
|
308
|
-
#: (untyped, String) -> Float
|
|
309
|
-
def coerce_rate(value, attribute)
|
|
310
|
-
number = value
|
|
311
|
-
float = value.is_a?(Numeric) ? number.to_f : nil #: Float?
|
|
312
|
-
unless float&.finite? && float >= 0
|
|
313
|
-
raise Riffer::ArgumentError,
|
|
314
|
-
"#{attribute} rate must be a non-negative number, got #{value.inspect}"
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
float
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
#--
|
|
321
|
-
#: (untyped, String) -> Float?
|
|
322
|
-
def coerce_optional_rate(value, attribute)
|
|
323
|
-
return nil if value.nil?
|
|
324
|
-
|
|
325
|
-
coerce_rate(value, attribute)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
5
|
VALID_MESSAGE_ID_STRATEGIES = %i[none uuid uuidv7].freeze
|
|
330
6
|
|
|
331
|
-
# Amazon Bedrock configuration.
|
|
332
7
|
attr_reader :amazon_bedrock #: Riffer::Config::AmazonBedrock # @dynamic amazon_bedrock
|
|
333
8
|
|
|
334
|
-
# Anthropic configuration.
|
|
335
9
|
attr_reader :anthropic #: Riffer::Config::Anthropic # @dynamic anthropic
|
|
336
10
|
|
|
337
|
-
# Azure OpenAI configuration.
|
|
338
11
|
attr_reader :azure_openai #: Riffer::Config::AzureOpenAI # @dynamic azure_openai
|
|
339
12
|
|
|
340
|
-
# Google Gemini configuration.
|
|
341
13
|
attr_reader :gemini #: Riffer::Config::Gemini # @dynamic gemini
|
|
342
14
|
|
|
343
|
-
# OpenAI configuration.
|
|
344
15
|
attr_reader :openai #: Riffer::Config::OpenAI # @dynamic openai
|
|
345
16
|
|
|
346
|
-
# OpenRouter configuration.
|
|
347
17
|
attr_reader :openrouter #: Riffer::Config::OpenRouter # @dynamic openrouter
|
|
348
18
|
|
|
349
|
-
# Evals configuration.
|
|
350
19
|
attr_reader :evals #: Riffer::Config::Evals # @dynamic evals
|
|
351
20
|
|
|
352
|
-
# MCP configuration. +credentials+ is an optional Proc returning per-run
|
|
353
|
-
# +tools/call+ headers (or +nil+ to deny); +discovery_runner+ runs tool
|
|
354
|
-
# discovery.
|
|
355
21
|
attr_reader :mcp #: Riffer::Config::Mcp # @dynamic mcp
|
|
356
22
|
|
|
357
|
-
# Global tool runtime configuration (experimental); defaults to
|
|
358
|
-
# <tt>Riffer::Tools::Runtime::Inline.new</tt>.
|
|
359
23
|
attr_reader :tool_runtime #: (singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc) # @dynamic tool_runtime
|
|
360
24
|
|
|
361
|
-
# Sets the global tool runtime. Raises Riffer::ArgumentError on an invalid
|
|
362
|
-
# value.
|
|
363
25
|
#--
|
|
364
26
|
#: ((singleton(Riffer::Tools::Runtime) | Riffer::Tools::Runtime | Proc)) -> void
|
|
365
27
|
def tool_runtime=(value)
|
|
@@ -373,24 +35,18 @@ class Riffer::Config
|
|
|
373
35
|
@tool_runtime = value
|
|
374
36
|
end
|
|
375
37
|
|
|
376
|
-
# Skills-related global configuration.
|
|
377
38
|
attr_reader :skills #: Riffer::Config::Skills # @dynamic skills
|
|
378
39
|
|
|
379
|
-
# Tracing-related global configuration.
|
|
380
40
|
attr_reader :tracing #: Riffer::Config::Tracing # @dynamic tracing
|
|
381
41
|
|
|
382
42
|
attr_reader :files #: Riffer::Config::Files # @dynamic files
|
|
383
43
|
|
|
384
|
-
# Consumer-configured per-model token pricing.
|
|
385
44
|
attr_reader :pricing #: Riffer::Config::Pricing # @dynamic pricing
|
|
386
45
|
|
|
387
|
-
#
|
|
388
|
-
#
|
|
389
|
-
# seeded messages passed to +Riffer::Agent#generate+ must carry their own.
|
|
46
|
+
# When not +:none+, seeded messages passed to +Riffer::Agent#generate+ must
|
|
47
|
+
# carry their own +id+.
|
|
390
48
|
attr_reader :message_id_strategy #: Symbol # @dynamic message_id_strategy
|
|
391
49
|
|
|
392
|
-
# Sets the message id strategy. Raises Riffer::ArgumentError unless the value
|
|
393
|
-
# is +:none+, +:uuid+, or +:uuidv7+.
|
|
394
50
|
#--
|
|
395
51
|
#: (Symbol) -> void
|
|
396
52
|
def message_id_strategy=(value)
|
|
@@ -401,20 +57,6 @@ class Riffer::Config
|
|
|
401
57
|
@message_id_strategy = value
|
|
402
58
|
end
|
|
403
59
|
|
|
404
|
-
# Experimental: when +true+, riffer maintains the +tool_use+ ↔ +tool_result+
|
|
405
|
-
# invariant itself — stripping orphaned exchanges and filling interrupted
|
|
406
|
-
# ones. Defaults to +false+; the surface may change without notice.
|
|
407
|
-
attr_reader :experimental_history_healing #: bool # @dynamic experimental_history_healing
|
|
408
|
-
|
|
409
|
-
# Sets the +experimental_history_healing+ flag, coercing boolean-ish values so
|
|
410
|
-
# an env-var +"false"+ (truthy in Ruby) doesn't silently enable healing.
|
|
411
|
-
# Raises Riffer::ArgumentError on an unrecognized value.
|
|
412
|
-
#--
|
|
413
|
-
#: (untyped) -> void
|
|
414
|
-
def experimental_history_healing=(value)
|
|
415
|
-
@experimental_history_healing = Riffer::Helpers::Boolean.coerce(value, attribute: "experimental_history_healing")
|
|
416
|
-
end
|
|
417
|
-
|
|
418
60
|
#--
|
|
419
61
|
#: () -> void
|
|
420
62
|
def initialize
|
|
@@ -425,13 +67,12 @@ class Riffer::Config
|
|
|
425
67
|
@openai = OpenAI.new
|
|
426
68
|
@openrouter = OpenRouter.new
|
|
427
69
|
@evals = Evals.new
|
|
428
|
-
@mcp = Mcp.new
|
|
70
|
+
@mcp = Mcp.new
|
|
429
71
|
@tool_runtime = Riffer::Tools::Runtime::Inline.new
|
|
430
72
|
@skills = Skills.new
|
|
431
73
|
@tracing = Tracing.new
|
|
432
74
|
@files = Files.new
|
|
433
75
|
@pricing = Pricing.new
|
|
434
76
|
@message_id_strategy = :none
|
|
435
|
-
@experimental_history_healing = false
|
|
436
77
|
end
|
|
437
78
|
end
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Base class for all evaluators. Set +instructions+ and the base class calls
|
|
5
|
-
# the judge automatically; override +#evaluate+ for custom logic. See
|
|
6
|
-
# examples/evaluators/ for reference implementations.
|
|
7
|
-
#
|
|
8
|
-
# class MyEvaluator < Riffer::Evals::Evaluator
|
|
9
|
-
# instructions "Assess medical accuracy of the response..."
|
|
10
|
-
# higher_is_better true
|
|
11
|
-
# judge_model "anthropic/claude-opus-4-5-20251101"
|
|
12
|
-
# end
|
|
13
|
-
#
|
|
14
4
|
class Riffer::Evals::Evaluator
|
|
15
5
|
# @rbs self.@instructions: String?
|
|
16
6
|
# @rbs self.@higher_is_better: bool?
|
|
@@ -18,12 +8,9 @@ class Riffer::Evals::Evaluator
|
|
|
18
8
|
# @rbs self.@identifier: String?
|
|
19
9
|
# @rbs @judge: Riffer::Evals::Judge?
|
|
20
10
|
|
|
21
|
-
# The identifier for an anonymous evaluator class.
|
|
22
11
|
DEFAULT_IDENTIFIER = "riffer/judge" #: String
|
|
23
12
|
|
|
24
13
|
class << self
|
|
25
|
-
# Gets or sets the evaluation instructions (criteria and scoring rubric).
|
|
26
|
-
#
|
|
27
14
|
#--
|
|
28
15
|
#: (?String?) -> String?
|
|
29
16
|
def instructions(value = nil)
|
|
@@ -32,8 +19,6 @@ class Riffer::Evals::Evaluator
|
|
|
32
19
|
@instructions = value.to_s
|
|
33
20
|
end
|
|
34
21
|
|
|
35
|
-
# Gets or sets whether higher scores are better.
|
|
36
|
-
#
|
|
37
22
|
#--
|
|
38
23
|
#: (?bool?) -> bool
|
|
39
24
|
def higher_is_better(value = nil)
|
|
@@ -46,8 +31,6 @@ class Riffer::Evals::Evaluator
|
|
|
46
31
|
@higher_is_better = value
|
|
47
32
|
end
|
|
48
33
|
|
|
49
|
-
# Gets or sets the judge model for LLM-as-judge evaluations.
|
|
50
|
-
#
|
|
51
34
|
#--
|
|
52
35
|
#: (?String?) -> String?
|
|
53
36
|
def judge_model(value = nil)
|
|
@@ -56,10 +39,6 @@ class Riffer::Evals::Evaluator
|
|
|
56
39
|
@judge_model = value.to_s
|
|
57
40
|
end
|
|
58
41
|
|
|
59
|
-
# Gets or sets the evaluator identifier, sent as the +agent+ tag on judge
|
|
60
|
-
# calls. Defaults to the snake_cased class name, or DEFAULT_IDENTIFIER for
|
|
61
|
-
# an anonymous class.
|
|
62
|
-
#
|
|
63
42
|
#--
|
|
64
43
|
#: (?String?) -> String
|
|
65
44
|
def identifier(value = nil)
|
|
@@ -70,18 +49,15 @@ class Riffer::Evals::Evaluator
|
|
|
70
49
|
|
|
71
50
|
private
|
|
72
51
|
|
|
73
|
-
# Anonymous classes derive an empty identifier.
|
|
74
52
|
#--
|
|
75
53
|
#: () -> String
|
|
76
54
|
def derived_identifier
|
|
55
|
+
# Anonymous classes derive an empty identifier.
|
|
77
56
|
derived = Riffer::Helpers::Identifier.for(self)
|
|
78
57
|
derived.empty? ? DEFAULT_IDENTIFIER : derived
|
|
79
58
|
end
|
|
80
59
|
end
|
|
81
60
|
|
|
82
|
-
# Evaluates an input/output pair. The default calls the judge with the
|
|
83
|
-
# class-level +instructions+; override for custom logic (e.g. rule-based
|
|
84
|
-
# evaluators).
|
|
85
61
|
#--
|
|
86
62
|
#: (input: String | Array[Hash[Symbol, untyped] | Riffer::Messages::Base], output: String, ?ground_truth: String?, ?messages: Array[Riffer::Messages::Base]) -> Riffer::Evals::Result
|
|
87
63
|
def evaluate(input:, output:, ground_truth: nil, messages: [])
|
|
@@ -120,9 +96,6 @@ class Riffer::Evals::Evaluator
|
|
|
120
96
|
|
|
121
97
|
protected
|
|
122
98
|
|
|
123
|
-
# Returns a Judge instance configured for this evaluator. Its calls carry
|
|
124
|
-
# the +kind+ (+"judge"+) and +agent+ (the evaluator identifier) tags.
|
|
125
|
-
#
|
|
126
99
|
#--
|
|
127
100
|
#: () -> Riffer::Evals::Judge
|
|
128
101
|
def judge
|
|
@@ -137,7 +110,6 @@ class Riffer::Evals::Evaluator
|
|
|
137
110
|
end
|
|
138
111
|
end
|
|
139
112
|
|
|
140
|
-
# Builds a Result for this evaluator.
|
|
141
113
|
#--
|
|
142
114
|
#: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?token_usage: Riffer::Providers::TokenUsage?) -> Riffer::Evals::Result
|
|
143
115
|
def result(score:, reason: nil, metadata: {}, token_usage: nil)
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Orchestrates running evaluators against an agent across multiple scenarios.
|
|
5
|
-
#
|
|
6
|
-
# result = Riffer::Evals::EvaluatorRunner.run(
|
|
7
|
-
# agent: MyAgent,
|
|
8
|
-
# scenarios: [
|
|
9
|
-
# { input: "What is Ruby?", ground_truth: "A programming language" },
|
|
10
|
-
# { input: "What is Python?" }
|
|
11
|
-
# ],
|
|
12
|
-
# evaluators: [AnswerRelevancyEvaluator]
|
|
13
|
-
# )
|
|
14
|
-
#
|
|
15
|
-
# result.scores # => { AnswerRelevancyEvaluator => 0.85 }
|
|
16
|
-
#
|
|
17
4
|
module Riffer::Evals::EvaluatorRunner
|
|
18
5
|
extend self
|
|
19
6
|
|
|
20
|
-
# Runs evaluators against an agent for the given scenarios. Raises
|
|
21
|
-
# Riffer::ArgumentError on an invalid agent or evaluator.
|
|
22
7
|
#--
|
|
23
8
|
#: (agent: singleton(Riffer::Agent), scenarios: Array[Hash[Symbol, untyped]], evaluators: Array[singleton(Riffer::Evals::Evaluator)], ?context: Hash[Symbol, untyped]?) -> Riffer::Evals::RunResult
|
|
24
9
|
def run(agent:, scenarios:, evaluators:, context: nil)
|
data/lib/riffer/evals/judge.rb
CHANGED
|
@@ -3,14 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
|
-
# Executes LLM-as-judge evaluations, using tool calling internally to get
|
|
7
|
-
# structured output from the judge model.
|
|
8
6
|
class Riffer::Evals::Judge
|
|
9
7
|
# @rbs @provider_instance: Riffer::Providers::Base?
|
|
10
8
|
# @rbs @provider_name: String?
|
|
11
9
|
# @rbs @model_name: String?
|
|
12
10
|
|
|
13
|
-
# Internal tool for structured evaluation output.
|
|
14
11
|
class EvaluationTool < Riffer::Tool
|
|
15
12
|
identifier "evaluation"
|
|
16
13
|
description "Submit your evaluation score and reasoning"
|
|
@@ -27,13 +24,9 @@ class Riffer::Evals::Judge
|
|
|
27
24
|
end
|
|
28
25
|
end
|
|
29
26
|
|
|
30
|
-
# The model string (provider/model format).
|
|
31
27
|
attr_reader :model #: String # @dynamic model
|
|
32
|
-
|
|
33
|
-
# The tags passed to the provider on every call.
|
|
34
28
|
attr_reader :tags #: Hash[String, String] # @dynamic tags
|
|
35
29
|
|
|
36
|
-
# Raises Riffer::ArgumentError unless +model+ is "provider/model" format.
|
|
37
30
|
#--
|
|
38
31
|
#: (model: String, ?tags: Hash[String, String]) -> void
|
|
39
32
|
def initialize(model:, tags: {})
|
|
@@ -46,7 +39,6 @@ class Riffer::Evals::Judge
|
|
|
46
39
|
@tags = tags
|
|
47
40
|
end
|
|
48
41
|
|
|
49
|
-
# Evaluates an input/output pair using the configured LLM.
|
|
50
42
|
#--
|
|
51
43
|
#: (instructions: String, input: String, output: String, ?ground_truth: String?) -> Hash[Symbol, untyped]
|
|
52
44
|
def evaluate(instructions:, input:, output:, ground_truth: nil)
|
data/lib/riffer/evals/result.rb
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents the result of a single evaluation.
|
|
5
4
|
class Riffer::Evals::Result
|
|
6
|
-
# The evaluator class that produced this result.
|
|
7
5
|
attr_reader :evaluator #: singleton(Riffer::Evals::Evaluator) # @dynamic evaluator
|
|
8
6
|
|
|
9
|
-
# The evaluation score (0.0 to 1.0).
|
|
10
7
|
attr_reader :score #: Float # @dynamic score
|
|
11
8
|
|
|
12
|
-
# Human-readable explanation of the score.
|
|
13
9
|
attr_reader :reason #: String? # @dynamic reason
|
|
14
10
|
|
|
15
|
-
# Additional metadata from the evaluation.
|
|
16
11
|
attr_reader :metadata #: Hash[Symbol, untyped] # @dynamic metadata
|
|
17
12
|
|
|
18
|
-
# Whether higher scores are better for this evaluator.
|
|
19
13
|
attr_reader :higher_is_better #: bool # @dynamic higher_is_better
|
|
20
14
|
|
|
21
|
-
#
|
|
22
|
-
# evaluator used an LLM. Nil for rule-based evaluators.
|
|
15
|
+
# Nil for rule-based evaluators.
|
|
23
16
|
attr_reader :token_usage #: Riffer::Providers::TokenUsage? # @dynamic token_usage
|
|
24
17
|
|
|
25
|
-
# Raises Riffer::ArgumentError if +score+ is not between 0.0 and 1.0.
|
|
26
18
|
#--
|
|
27
19
|
#: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool, ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
28
20
|
def initialize(evaluator:, score:, reason: nil, metadata: {}, higher_is_better: true, token_usage: nil)
|
|
@@ -35,8 +27,6 @@ class Riffer::Evals::Result
|
|
|
35
27
|
@token_usage = token_usage
|
|
36
28
|
end
|
|
37
29
|
|
|
38
|
-
# Returns a hash representation of the result.
|
|
39
|
-
#
|
|
40
30
|
#--
|
|
41
31
|
#: () -> Hash[Symbol, untyped]
|
|
42
32
|
def to_h
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents the complete result of an evaluation run across multiple scenarios.
|
|
5
4
|
class Riffer::Evals::RunResult
|
|
6
|
-
# Per-scenario evaluation results.
|
|
7
5
|
attr_reader :scenario_results #: Array[Riffer::Evals::ScenarioResult] # @dynamic scenario_results
|
|
8
6
|
|
|
9
7
|
#--
|
|
@@ -12,8 +10,6 @@ class Riffer::Evals::RunResult
|
|
|
12
10
|
@scenario_results = scenario_results
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
# Returns average scores keyed by evaluator class across all scenarios.
|
|
16
|
-
#
|
|
17
13
|
#--
|
|
18
14
|
#: () -> Hash[singleton(Riffer::Evals::Evaluator), Float]
|
|
19
15
|
def scores
|
|
@@ -35,26 +31,18 @@ class Riffer::Evals::RunResult
|
|
|
35
31
|
end
|
|
36
32
|
end
|
|
37
33
|
|
|
38
|
-
# Returns the summed token usage the agents under test spent across every
|
|
39
|
-
# scenario, or nil when none reported usage.
|
|
40
|
-
#
|
|
41
34
|
#--
|
|
42
35
|
#: () -> Riffer::Providers::TokenUsage?
|
|
43
36
|
def token_usage
|
|
44
37
|
scenario_results.filter_map(&:token_usage).reduce(:+)
|
|
45
38
|
end
|
|
46
39
|
|
|
47
|
-
# Returns the summed token usage across every scenario's LLM-as-judge
|
|
48
|
-
# evaluators, or nil when none reported usage.
|
|
49
|
-
#
|
|
50
40
|
#--
|
|
51
41
|
#: () -> Riffer::Providers::TokenUsage?
|
|
52
42
|
def evaluator_token_usage
|
|
53
43
|
scenario_results.filter_map(&:evaluator_token_usage).reduce(:+)
|
|
54
44
|
end
|
|
55
45
|
|
|
56
|
-
# Returns a hash representation of the run result.
|
|
57
|
-
#
|
|
58
46
|
#--
|
|
59
47
|
#: () -> Hash[Symbol, untyped]
|
|
60
48
|
def to_h
|