riffer 0.32.1 → 0.34.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/.release-please-manifest.json +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +39 -0
- data/README.md +13 -11
- data/docs/01_OVERVIEW.md +2 -0
- data/docs/04_AGENT_LIFECYCLE.md +15 -13
- data/docs/08_MESSAGES.md +39 -5
- data/docs/09_STREAM_EVENTS.md +14 -0
- data/docs/10_CONFIGURATION.md +73 -4
- data/docs/13_SKILLS.md +62 -4
- data/docs/14_MCP.md +2 -1
- data/docs/16_TRACING.md +274 -0
- data/docs/17_METRICS.md +153 -0
- data/docs/providers/07_CUSTOM_PROVIDERS.md +44 -0
- data/lib/riffer/agent/response.rb +11 -2
- data/lib/riffer/agent/run.rb +136 -35
- data/lib/riffer/agent.rb +1 -1
- data/lib/riffer/config.rb +230 -15
- data/lib/riffer/guardrail.rb +8 -0
- data/lib/riffer/guardrails/runner.rb +58 -0
- data/lib/riffer/helpers/boolean.rb +22 -0
- data/lib/riffer/mcp/authenticated_tool.rb +14 -20
- data/lib/riffer/mcp/registration.rb +4 -4
- data/lib/riffer/mcp/tool.rb +23 -0
- data/lib/riffer/mcp/tool_factory.rb +14 -22
- data/lib/riffer/messages/assistant.rb +15 -3
- data/lib/riffer/messages/base.rb +2 -1
- data/lib/riffer/metrics/instruments.rb +31 -0
- data/lib/riffer/metrics/no_op.rb +14 -0
- data/lib/riffer/metrics/otel.rb +80 -0
- data/lib/riffer/metrics.rb +94 -0
- data/lib/riffer/providers/amazon_bedrock.rb +57 -21
- data/lib/riffer/providers/anthropic.rb +59 -24
- data/lib/riffer/providers/azure_open_ai.rb +7 -0
- data/lib/riffer/providers/base.rb +247 -15
- data/lib/riffer/providers/finish_reason.rb +27 -0
- data/lib/riffer/providers/gemini.rb +59 -11
- data/lib/riffer/providers/mock.rb +30 -9
- data/lib/riffer/providers/open_ai.rb +78 -24
- data/lib/riffer/providers/open_router.rb +56 -16
- data/lib/riffer/providers/repository.rb +9 -0
- data/lib/riffer/providers/token_usage.rb +27 -11
- data/lib/riffer/skills/activate_tool.rb +11 -2
- data/lib/riffer/skills/adapter.rb +15 -0
- data/lib/riffer/skills/context.rb +63 -11
- data/lib/riffer/skills/markdown_adapter.rb +1 -1
- data/lib/riffer/skills/xml_adapter.rb +1 -1
- data/lib/riffer/stream_events/finish_reason_done.rb +34 -0
- data/lib/riffer/tools/runtime.rb +99 -3
- data/lib/riffer/tracing/capture.rb +92 -0
- data/lib/riffer/tracing/no_op.rb +61 -0
- data/lib/riffer/tracing/otel.rb +132 -0
- data/lib/riffer/tracing/stream_recorder.rb +51 -0
- data/lib/riffer/tracing.rb +78 -0
- data/lib/riffer/version.rb +1 -1
- data/sig/_private/opentelemetry.rbs +22 -0
- data/sig/generated/riffer/agent/response.rbs +9 -2
- data/sig/generated/riffer/agent/run.rbs +28 -8
- data/sig/generated/riffer/config.rbs +162 -16
- data/sig/generated/riffer/guardrail.rbs +6 -0
- data/sig/generated/riffer/guardrails/runner.rbs +22 -0
- data/sig/generated/riffer/helpers/boolean.rbs +11 -0
- data/sig/generated/riffer/mcp/authenticated_tool.rbs +6 -8
- data/sig/generated/riffer/mcp/registration.rbs +4 -4
- data/sig/generated/riffer/mcp/tool.rbs +19 -0
- data/sig/generated/riffer/mcp/tool_factory.rbs +8 -7
- data/sig/generated/riffer/messages/assistant.rbs +10 -4
- data/sig/generated/riffer/metrics/instruments.rbs +15 -0
- data/sig/generated/riffer/metrics/no_op.rbs +10 -0
- data/sig/generated/riffer/metrics/otel.rbs +48 -0
- data/sig/generated/riffer/metrics.rbs +72 -0
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +35 -14
- data/sig/generated/riffer/providers/anthropic.rbs +41 -20
- data/sig/generated/riffer/providers/azure_open_ai.rbs +5 -0
- data/sig/generated/riffer/providers/base.rbs +78 -2
- data/sig/generated/riffer/providers/finish_reason.rbs +19 -0
- data/sig/generated/riffer/providers/gemini.rbs +25 -2
- data/sig/generated/riffer/providers/mock.rbs +16 -5
- data/sig/generated/riffer/providers/open_ai.rbs +44 -22
- data/sig/generated/riffer/providers/open_router.rbs +31 -12
- data/sig/generated/riffer/providers/repository.rbs +7 -0
- data/sig/generated/riffer/providers/token_usage.rbs +20 -10
- data/sig/generated/riffer/skills/activate_tool.rbs +7 -1
- data/sig/generated/riffer/skills/adapter.rbs +10 -0
- data/sig/generated/riffer/skills/context.rbs +42 -4
- data/sig/generated/riffer/stream_events/finish_reason_done.rbs +21 -0
- data/sig/generated/riffer/tools/runtime.rbs +35 -0
- data/sig/generated/riffer/tracing/capture.rbs +46 -0
- data/sig/generated/riffer/tracing/no_op.rbs +46 -0
- data/sig/generated/riffer/tracing/otel.rbs +84 -0
- data/sig/generated/riffer/tracing/stream_recorder.rbs +31 -0
- data/sig/generated/riffer/tracing.rbs +52 -0
- data/sig/manual/riffer/helpers/boolean.rbs +5 -0
- data/sig/manual/riffer/metrics/no_op.rbs +5 -0
- data/sig/manual/riffer/metrics.rbs +5 -0
- data/sig/manual/riffer/providers.rbs +9 -0
- data/sig/manual/riffer/tracing/capture.rbs +5 -0
- data/sig/manual/riffer/tracing/no_op.rbs +5 -0
- data/sig/manual/riffer/tracing.rbs +5 -0
- metadata +42 -6
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Generated from lib/riffer/metrics/otel.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# OTEL-backed metrics backend. <tt>::OpenTelemetry</tt> constants appear only
|
|
4
|
+
# inside method bodies here, so the gem loads and eager-loads cleanly when the
|
|
5
|
+
# OpenTelemetry metrics API is absent.
|
|
6
|
+
class Riffer::Metrics::Otel
|
|
7
|
+
# :nodoc: all
|
|
8
|
+
SUPPORTED_API_VERSIONS: Gem::Requirement
|
|
9
|
+
|
|
10
|
+
# Builds a backend when the OpenTelemetry metrics API is loadable at a
|
|
11
|
+
# supported version; returns +nil+ so resolution falls back to NoOp.
|
|
12
|
+
# +provider+ defaults to the global <tt>OpenTelemetry.meter_provider</tt>.
|
|
13
|
+
# --
|
|
14
|
+
# : (?provider: untyped) -> Riffer::Metrics::Otel?
|
|
15
|
+
def self.build: (?provider: untyped) -> Riffer::Metrics::Otel?
|
|
16
|
+
|
|
17
|
+
# Whether the OpenTelemetry metrics API gem is loadable at a supported
|
|
18
|
+
# version.
|
|
19
|
+
# --
|
|
20
|
+
# : () -> bool
|
|
21
|
+
def self.available?: () -> bool
|
|
22
|
+
|
|
23
|
+
# Whether the given opentelemetry-metrics-api version is one riffer codes
|
|
24
|
+
# against. The gem is undeclared, so this guard is the only protection
|
|
25
|
+
# against an incompatible, still-pre-1.0 API.
|
|
26
|
+
# --
|
|
27
|
+
# : (Gem::Version) -> bool
|
|
28
|
+
def self.supported?: (Gem::Version) -> bool
|
|
29
|
+
|
|
30
|
+
# --
|
|
31
|
+
# : () -> Gem::Version?
|
|
32
|
+
private def self.api_version: () -> Gem::Version?
|
|
33
|
+
|
|
34
|
+
@meter: untyped
|
|
35
|
+
|
|
36
|
+
@instruments: Hash[String, untyped]
|
|
37
|
+
|
|
38
|
+
@mutex: Mutex
|
|
39
|
+
|
|
40
|
+
# --
|
|
41
|
+
# : (provider: untyped) -> void
|
|
42
|
+
def initialize: (provider: untyped) -> void
|
|
43
|
+
|
|
44
|
+
# Records a value onto the named histogram.
|
|
45
|
+
# --
|
|
46
|
+
# : (String, Numeric, unit: String?, description: String?, attributes: Hash[String, untyped]?) -> void
|
|
47
|
+
def record_histogram: (String, Numeric, unit: String?, description: String?, attributes: Hash[String, untyped]?) -> void
|
|
48
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Generated from lib/riffer/metrics.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Internal metrics port — records OTEL metric instruments when the host bundles
|
|
4
|
+
# the OpenTelemetry metrics API and no-ops otherwise, so riffer never declares
|
|
5
|
+
# an OTEL dependency.
|
|
6
|
+
module Riffer::Metrics
|
|
7
|
+
@backend: untyped
|
|
8
|
+
|
|
9
|
+
MUTEX: Mutex
|
|
10
|
+
|
|
11
|
+
# The Ruby API cannot attach a schema URL to a meter, so the semconv pin
|
|
12
|
+
# lives here as the documented contract version.
|
|
13
|
+
SCHEMA_URL: String
|
|
14
|
+
|
|
15
|
+
# A handle to a named histogram, safe to hold as a constant: it defers backend
|
|
16
|
+
# resolution to record time, so it survives a meter-provider swap or a runtime
|
|
17
|
+
# +enabled+ flip.
|
|
18
|
+
class Histogram
|
|
19
|
+
@name: String
|
|
20
|
+
|
|
21
|
+
@unit: String?
|
|
22
|
+
|
|
23
|
+
@description: String?
|
|
24
|
+
|
|
25
|
+
# --
|
|
26
|
+
# : (String, ?unit: String?, ?description: String?) -> void
|
|
27
|
+
def initialize: (String, ?unit: String?, ?description: String?) -> void
|
|
28
|
+
|
|
29
|
+
# --
|
|
30
|
+
# : (Numeric, ?attributes: Hash[String, untyped]?) -> void
|
|
31
|
+
def record: (Numeric, ?attributes: Hash[String, untyped]?) -> void
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns a handle to the named histogram.
|
|
35
|
+
# --
|
|
36
|
+
# : (String, ?unit: String?, ?description: String?) -> Riffer::Metrics::Histogram
|
|
37
|
+
def create_histogram: (String, ?unit: String?, ?description: String?) -> Riffer::Metrics::Histogram
|
|
38
|
+
|
|
39
|
+
# Records a value onto the named histogram.
|
|
40
|
+
# --
|
|
41
|
+
# : (String, Numeric, ?unit: String?, ?description: String?, ?attributes: Hash[String, untyped]?) -> void
|
|
42
|
+
def record_histogram: (String, Numeric, ?unit: String?, ?description: String?, ?attributes: Hash[String, untyped]?) -> void
|
|
43
|
+
|
|
44
|
+
# Mirrors a span's +recording?+ so a caller can skip work that exists only to
|
|
45
|
+
# feed a metric. True for any live backend — the OTEL backend or a
|
|
46
|
+
# consumer-supplied one — so a custom metrics sink still gets fed.
|
|
47
|
+
# --
|
|
48
|
+
# : () -> bool
|
|
49
|
+
def recording?: () -> bool
|
|
50
|
+
|
|
51
|
+
# Reads the monotonic clock in seconds — the time source for duration metrics,
|
|
52
|
+
# immune to wall-clock adjustments.
|
|
53
|
+
# --
|
|
54
|
+
# : () -> Float
|
|
55
|
+
def monotonic_now: () -> Float
|
|
56
|
+
|
|
57
|
+
# Discards the resolved backend so the next record re-resolves it; cached
|
|
58
|
+
# instruments live on that backend, so this clears them too.
|
|
59
|
+
# --
|
|
60
|
+
# : () -> void
|
|
61
|
+
def reset!: () -> void
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# --
|
|
66
|
+
# : () -> untyped
|
|
67
|
+
def backend: () -> untyped
|
|
68
|
+
|
|
69
|
+
# --
|
|
70
|
+
# : () -> untyped
|
|
71
|
+
def resolve_backend: () -> untyped
|
|
72
|
+
end
|
|
@@ -7,12 +7,19 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
7
7
|
# cross-region (+us.anthropic.claude-...+) ids.
|
|
8
8
|
ANTHROPIC_MODEL_PATTERN: Regexp
|
|
9
9
|
|
|
10
|
+
FINISH_REASONS: Hash[String, Symbol]
|
|
11
|
+
|
|
10
12
|
# Returns the skill adapter for the Bedrock model — XML for Anthropic models
|
|
11
13
|
# (which Bedrock hosts alongside other vendors'), else Markdown.
|
|
12
14
|
# --
|
|
13
15
|
# : (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
14
16
|
def self.skills_adapter: (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
15
17
|
|
|
18
|
+
# The GenAI semconv well-known provider name.
|
|
19
|
+
# --
|
|
20
|
+
# : () -> String
|
|
21
|
+
def self.semconv_provider_name: () -> String
|
|
22
|
+
|
|
16
23
|
# --
|
|
17
24
|
# : (?api_token: String?, ?region: String?, **untyped) -> void
|
|
18
25
|
def initialize: (?api_token: String?, ?region: String?, **untyped) -> void
|
|
@@ -42,6 +49,20 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
42
49
|
# : (untyped) -> Riffer::Providers::TokenUsage?
|
|
43
50
|
def extract_token_usage: (untyped) -> Riffer::Providers::TokenUsage?
|
|
44
51
|
|
|
52
|
+
# Converse's +input_tokens+ excludes the cache buckets; TokenUsage's
|
|
53
|
+
# input includes them.
|
|
54
|
+
# --
|
|
55
|
+
# : (untyped) -> Riffer::Providers::TokenUsage
|
|
56
|
+
def build_token_usage: (untyped) -> Riffer::Providers::TokenUsage
|
|
57
|
+
|
|
58
|
+
# --
|
|
59
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
60
|
+
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
61
|
+
|
|
62
|
+
# --
|
|
63
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
64
|
+
def build_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
65
|
+
|
|
45
66
|
# --
|
|
46
67
|
# : (untyped) -> String
|
|
47
68
|
def extract_content: (untyped) -> String
|
|
@@ -51,8 +72,8 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
51
72
|
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
52
73
|
|
|
53
74
|
# --
|
|
54
|
-
# : (Hash[Symbol, untyped],
|
|
55
|
-
def execute_stream: (Hash[Symbol, untyped],
|
|
75
|
+
# : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
76
|
+
def execute_stream: (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
56
77
|
|
|
57
78
|
# Re-raises a Bedrock stream-exception event as the matching
|
|
58
79
|
# +Aws::BedrockRuntime::Errors+ class. ConverseStream delivers API errors on
|
|
@@ -63,28 +84,28 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
63
84
|
def raise_if_stream_exception!: (untyped) -> void
|
|
64
85
|
|
|
65
86
|
# --
|
|
66
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
67
|
-
def handle_content_block_start_tool_use: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
87
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
88
|
+
def handle_content_block_start_tool_use: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
68
89
|
|
|
69
90
|
# --
|
|
70
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
71
|
-
def handle_content_block_delta_text_delta: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
91
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
92
|
+
def handle_content_block_delta_text_delta: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
72
93
|
|
|
73
94
|
# --
|
|
74
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
75
|
-
def handle_content_block_delta_tool_use: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
95
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
96
|
+
def handle_content_block_delta_tool_use: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
76
97
|
|
|
77
98
|
# --
|
|
78
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
79
|
-
def handle_content_block_stop_text_delta: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
99
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
100
|
+
def handle_content_block_stop_text_delta: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
80
101
|
|
|
81
102
|
# --
|
|
82
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
83
|
-
def handle_content_block_stop_tool_use: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
103
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
104
|
+
def handle_content_block_stop_tool_use: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
84
105
|
|
|
85
106
|
# --
|
|
86
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
87
|
-
def handle_metadata_usage: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
107
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
108
|
+
def handle_metadata_usage: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
88
109
|
|
|
89
110
|
# --
|
|
90
111
|
# : (Array[Riffer::Messages::Base]) -> Hash[Symbol, untyped]
|
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
6
6
|
WEB_SEARCH_TOOL_TYPE: String
|
|
7
7
|
|
|
8
|
+
FINISH_REASONS: Hash[String, Symbol]
|
|
9
|
+
|
|
8
10
|
# Returns the XML skill adapter for Anthropic/Claude.
|
|
9
11
|
#
|
|
10
12
|
# --
|
|
11
13
|
# : (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
12
14
|
def self.skills_adapter: (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
13
15
|
|
|
16
|
+
# The GenAI semconv well-known provider name.
|
|
17
|
+
# --
|
|
18
|
+
# : () -> String
|
|
19
|
+
def self.semconv_provider_name: () -> String
|
|
20
|
+
|
|
14
21
|
# --
|
|
15
22
|
# : (?api_key: String?, **untyped) -> void
|
|
16
23
|
def initialize: (?api_key: String?, **untyped) -> void
|
|
@@ -29,6 +36,20 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
29
36
|
# : (untyped) -> Riffer::Providers::TokenUsage?
|
|
30
37
|
def extract_token_usage: (untyped) -> Riffer::Providers::TokenUsage?
|
|
31
38
|
|
|
39
|
+
# --
|
|
40
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
41
|
+
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
42
|
+
|
|
43
|
+
# --
|
|
44
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
45
|
+
def build_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
46
|
+
|
|
47
|
+
# Anthropic's +input_tokens+ excludes the cache buckets; TokenUsage's
|
|
48
|
+
# input includes them.
|
|
49
|
+
# --
|
|
50
|
+
# : (untyped) -> Riffer::Providers::TokenUsage
|
|
51
|
+
def build_token_usage: (untyped) -> Riffer::Providers::TokenUsage
|
|
52
|
+
|
|
32
53
|
# --
|
|
33
54
|
# : (untyped) -> String
|
|
34
55
|
def extract_content: (untyped) -> String
|
|
@@ -38,8 +59,8 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
38
59
|
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
39
60
|
|
|
40
61
|
# --
|
|
41
|
-
# : (Hash[Symbol, untyped],
|
|
42
|
-
def execute_stream: (Hash[Symbol, untyped],
|
|
62
|
+
# : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
63
|
+
def execute_stream: (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
43
64
|
|
|
44
65
|
# --
|
|
45
66
|
# : (untyped, state: Hash[Symbol, untyped]) -> void
|
|
@@ -50,40 +71,40 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
50
71
|
def handle_raw_content_block_delta: (untyped, state: Hash[Symbol, untyped]) -> void
|
|
51
72
|
|
|
52
73
|
# --
|
|
53
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
54
|
-
def handle_text_event: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
74
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
75
|
+
def handle_text_event: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
55
76
|
|
|
56
77
|
# --
|
|
57
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
58
|
-
def handle_thinking_event: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
78
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
79
|
+
def handle_thinking_event: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
59
80
|
|
|
60
81
|
# --
|
|
61
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
62
|
-
def handle_input_json_event: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
82
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
83
|
+
def handle_input_json_event: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
63
84
|
|
|
64
85
|
# --
|
|
65
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
66
|
-
def handle_content_block_stop_tool_use: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
86
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
87
|
+
def handle_content_block_stop_tool_use: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
67
88
|
|
|
68
89
|
# --
|
|
69
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
70
|
-
def handle_content_block_stop_thinking: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
90
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
91
|
+
def handle_content_block_stop_thinking: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
71
92
|
|
|
72
93
|
# --
|
|
73
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
74
|
-
def handle_content_block_stop_text: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
94
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
95
|
+
def handle_content_block_stop_text: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
75
96
|
|
|
76
97
|
# --
|
|
77
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
78
|
-
def handle_content_block_stop_server_tool_use: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
98
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
99
|
+
def handle_content_block_stop_server_tool_use: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
79
100
|
|
|
80
101
|
# --
|
|
81
|
-
# : (untyped, state: Hash[Symbol, untyped], yielder:
|
|
82
|
-
def handle_content_block_stop_web_search_result: (untyped, state: Hash[Symbol, untyped], yielder:
|
|
102
|
+
# : (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
103
|
+
def handle_content_block_stop_web_search_result: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
83
104
|
|
|
84
105
|
# --
|
|
85
|
-
# : (untyped, accumulated_message: untyped, yielder:
|
|
86
|
-
def handle_message_stop: (untyped, accumulated_message: untyped, yielder:
|
|
106
|
+
# : (untyped, accumulated_message: untyped, yielder: Riffer::Providers::_EventSink) -> void
|
|
107
|
+
def handle_message_stop: (untyped, accumulated_message: untyped, yielder: Riffer::Providers::_EventSink) -> void
|
|
87
108
|
|
|
88
109
|
# --
|
|
89
110
|
# : (Array[Riffer::Messages::Base]) -> Hash[Symbol, untyped]
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
# gem. Credentials resolve from kwargs, then config, then
|
|
5
5
|
# +AZURE_OPENAI_API_KEY+ / +AZURE_OPENAI_ENDPOINT+.
|
|
6
6
|
class Riffer::Providers::AzureOpenAI < Riffer::Providers::OpenAI
|
|
7
|
+
# The GenAI semconv well-known provider name.
|
|
8
|
+
# --
|
|
9
|
+
# : () -> String
|
|
10
|
+
def self.semconv_provider_name: () -> String
|
|
11
|
+
|
|
7
12
|
# --
|
|
8
13
|
# : (**untyped) -> void
|
|
9
14
|
def initialize: (**untyped) -> void
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
# +extract_token_usage+, +extract_content+, +extract_tool_calls+) and the base
|
|
6
6
|
# class orchestrates them.
|
|
7
7
|
class Riffer::Providers::Base
|
|
8
|
+
@current_model: String?
|
|
9
|
+
|
|
8
10
|
@current_tools: Array[singleton(Riffer::Tool)]
|
|
9
11
|
|
|
10
12
|
WIRE_SEPARATOR: String
|
|
@@ -15,6 +17,14 @@ class Riffer::Providers::Base
|
|
|
15
17
|
# : (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
16
18
|
def self.skills_adapter: (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
17
19
|
|
|
20
|
+
# Returns the provider name stamped as <tt>gen_ai.provider.name</tt> on trace
|
|
21
|
+
# spans, ideally a GenAI semconv well-known value. Defaults to the snake_cased
|
|
22
|
+
# class name rather than raising like the abstract provider methods, so
|
|
23
|
+
# enabling tracing never breaks an otherwise-working custom provider.
|
|
24
|
+
# --
|
|
25
|
+
# : () -> String
|
|
26
|
+
def self.semconv_provider_name: () -> String
|
|
27
|
+
|
|
18
28
|
# Generates text using the provider.
|
|
19
29
|
#
|
|
20
30
|
# --
|
|
@@ -49,13 +59,26 @@ class Riffer::Providers::Base
|
|
|
49
59
|
def execute_generate: (Hash[Symbol, untyped]) -> untyped
|
|
50
60
|
|
|
51
61
|
# --
|
|
52
|
-
# : (Hash[Symbol, untyped],
|
|
53
|
-
def execute_stream: (Hash[Symbol, untyped],
|
|
62
|
+
# : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
63
|
+
def execute_stream: (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
54
64
|
|
|
55
65
|
# --
|
|
56
66
|
# : (untyped) -> Riffer::Providers::TokenUsage?
|
|
57
67
|
def extract_token_usage: (untyped) -> Riffer::Providers::TokenUsage?
|
|
58
68
|
|
|
69
|
+
# : (Riffer::Providers::TokenUsage) -> Riffer::Providers::TokenUsage
|
|
70
|
+
def apply_pricing: (Riffer::Providers::TokenUsage) -> Riffer::Providers::TokenUsage
|
|
71
|
+
|
|
72
|
+
# --
|
|
73
|
+
# : () -> Riffer::Config::Pricing::Rates?
|
|
74
|
+
def pricing_rates: () -> Riffer::Config::Pricing::Rates?
|
|
75
|
+
|
|
76
|
+
# Defaults to nil rather than raising — finish reasons are optional, so
|
|
77
|
+
# providers that don't report one stay valid.
|
|
78
|
+
# --
|
|
79
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
80
|
+
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
81
|
+
|
|
59
82
|
# --
|
|
60
83
|
# : (untyped) -> String
|
|
61
84
|
def extract_content: (untyped) -> String
|
|
@@ -64,6 +87,59 @@ class Riffer::Providers::Base
|
|
|
64
87
|
# : (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
65
88
|
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
66
89
|
|
|
90
|
+
# A deliberate whitelist — caller options outside it stay off spans.
|
|
91
|
+
REQUEST_PARAM_ATTRIBUTES: Hash[Symbol, String]
|
|
92
|
+
|
|
93
|
+
# --
|
|
94
|
+
# : [R] (String?, Array[Riffer::Messages::Base], Hash[Symbol, untyped]) { (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span) -> R } -> R
|
|
95
|
+
def in_chat_span: [R] (String?, Array[Riffer::Messages::Base], Hash[Symbol, untyped]) { (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span) -> R } -> R
|
|
96
|
+
|
|
97
|
+
# --
|
|
98
|
+
# : (String?, Hash[Symbol, untyped]) -> Hash[String, untyped]
|
|
99
|
+
def chat_span_attributes: (String?, Hash[Symbol, untyped]) -> Hash[String, untyped]
|
|
100
|
+
|
|
101
|
+
# --
|
|
102
|
+
# : (String?) -> Hash[String, untyped]
|
|
103
|
+
def chat_metric_base_attributes: (String?) -> Hash[String, untyped]
|
|
104
|
+
|
|
105
|
+
# --
|
|
106
|
+
# : (String?, String?) -> Hash[String, untyped]
|
|
107
|
+
def chat_metric_attributes: (String?, String?) -> Hash[String, untyped]
|
|
108
|
+
|
|
109
|
+
# Per-call only — the run level would double-count an aggregate.
|
|
110
|
+
# --
|
|
111
|
+
# : (String?, Riffer::Providers::TokenUsage?) -> void
|
|
112
|
+
def record_token_usage_metric: (String?, Riffer::Providers::TokenUsage?) -> void
|
|
113
|
+
|
|
114
|
+
# Per-call only — the run level would double-count an aggregate.
|
|
115
|
+
# --
|
|
116
|
+
# : (String?, Riffer::Providers::TokenUsage?) -> void
|
|
117
|
+
def record_cost_metric: (String?, Riffer::Providers::TokenUsage?) -> void
|
|
118
|
+
|
|
119
|
+
# --
|
|
120
|
+
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Symbol?, String?) -> void
|
|
121
|
+
def record_finish_reason: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Symbol?, String?) -> void
|
|
122
|
+
|
|
123
|
+
# --
|
|
124
|
+
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tracing::StreamRecorder) -> void
|
|
125
|
+
def record_stream_outcome: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Riffer::Tracing::StreamRecorder) -> void
|
|
126
|
+
|
|
127
|
+
# --
|
|
128
|
+
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Array[Riffer::Messages::Base]) -> void
|
|
129
|
+
def capture_input: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, Array[Riffer::Messages::Base]) -> void
|
|
130
|
+
|
|
131
|
+
# --
|
|
132
|
+
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), content: String?, tool_calls: Array[Riffer::Messages::Assistant::ToolCall], finish_reason: Symbol?) -> void
|
|
133
|
+
def capture_output: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span, content: String?, tool_calls: Array[Riffer::Messages::Assistant::ToolCall], finish_reason: Symbol?) -> void
|
|
134
|
+
|
|
135
|
+
# --
|
|
136
|
+
# : ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span)) -> bool
|
|
137
|
+
def capture_messages?: (Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span) -> bool
|
|
138
|
+
|
|
139
|
+
# --
|
|
140
|
+
# : (Riffer::Providers::_EventSink, Riffer::Providers::FinishReason?) -> void
|
|
141
|
+
def yield_finish_reason: (Riffer::Providers::_EventSink, Riffer::Providers::FinishReason?) -> void
|
|
142
|
+
|
|
67
143
|
# --
|
|
68
144
|
# : (String) -> Hash[Symbol, untyped]?
|
|
69
145
|
def parse_structured_output: (String) -> Hash[Symbol, untyped]?
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Generated from lib/riffer/providers/finish_reason.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Normalized reason an LLM call finished, paired with the provider's raw
|
|
4
|
+
# wire value. +reason+ carries the same meaning for every provider.
|
|
5
|
+
class Riffer::Providers::FinishReason
|
|
6
|
+
# The normalized vocabulary every provider maps into.
|
|
7
|
+
VALUES: Array[Symbol]
|
|
8
|
+
|
|
9
|
+
# The normalized reason.
|
|
10
|
+
attr_reader reason: Symbol
|
|
11
|
+
|
|
12
|
+
# The provider's raw finish-reason value, when one exists on the wire.
|
|
13
|
+
attr_reader raw: String?
|
|
14
|
+
|
|
15
|
+
# Raises Riffer::ArgumentError when +reason+ is outside VALUES.
|
|
16
|
+
# --
|
|
17
|
+
# : (reason: Symbol, ?raw: String?) -> void
|
|
18
|
+
def initialize: (reason: Symbol, ?raw: String?) -> void
|
|
19
|
+
end
|
|
@@ -16,6 +16,13 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
|
|
|
16
16
|
|
|
17
17
|
DEFAULT_READ_TIMEOUT: Integer
|
|
18
18
|
|
|
19
|
+
FINISH_REASONS: Hash[String, Symbol]
|
|
20
|
+
|
|
21
|
+
# The GenAI semconv well-known provider name.
|
|
22
|
+
# --
|
|
23
|
+
# : () -> String
|
|
24
|
+
def self.semconv_provider_name: () -> String
|
|
25
|
+
|
|
19
26
|
# --
|
|
20
27
|
# : (?api_key: String?, ?open_timeout: Integer?, ?read_timeout: Integer?, **untyped) -> void
|
|
21
28
|
def initialize: (?api_key: String?, ?open_timeout: Integer?, ?read_timeout: Integer?, **untyped) -> void
|
|
@@ -43,8 +50,24 @@ class Riffer::Providers::Gemini < Riffer::Providers::Base
|
|
|
43
50
|
def extract_token_usage: (Hash[Symbol, untyped]) -> Riffer::Providers::TokenUsage?
|
|
44
51
|
|
|
45
52
|
# --
|
|
46
|
-
# : (Hash[Symbol, untyped]
|
|
47
|
-
def
|
|
53
|
+
# : (Hash[Symbol, untyped]) -> Riffer::Providers::FinishReason?
|
|
54
|
+
def extract_finish_reason: (Hash[Symbol, untyped]) -> Riffer::Providers::FinishReason?
|
|
55
|
+
|
|
56
|
+
# Gemini reports STOP even when the candidate carries functionCall parts,
|
|
57
|
+
# so tool-call presence overrides the raw value.
|
|
58
|
+
# --
|
|
59
|
+
# : (String?, tool_calls: bool) -> Riffer::Providers::FinishReason?
|
|
60
|
+
def build_finish_reason: (String?, tool_calls: bool) -> Riffer::Providers::FinishReason?
|
|
61
|
+
|
|
62
|
+
# Gemini reports thinking tokens outside +candidatesTokenCount+;
|
|
63
|
+
# TokenUsage's output includes them.
|
|
64
|
+
# --
|
|
65
|
+
# : (Hash[Symbol, untyped]) -> Riffer::Providers::TokenUsage
|
|
66
|
+
def build_token_usage: (Hash[Symbol, untyped]) -> Riffer::Providers::TokenUsage
|
|
67
|
+
|
|
68
|
+
# --
|
|
69
|
+
# : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
70
|
+
def execute_stream: (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
48
71
|
|
|
49
72
|
# --
|
|
50
73
|
# : (Array[Riffer::Messages::Base]) -> Hash[Symbol, untyped]
|
|
@@ -14,6 +14,11 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
14
14
|
# : (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
15
15
|
def self.skills_adapter: (?String?) -> singleton(Riffer::Skills::Adapter)
|
|
16
16
|
|
|
17
|
+
# The GenAI semconv well-known provider name.
|
|
18
|
+
# --
|
|
19
|
+
# : () -> String
|
|
20
|
+
def self.semconv_provider_name: () -> String
|
|
21
|
+
|
|
17
22
|
# Array of recorded method calls for assertions.
|
|
18
23
|
attr_reader calls: Array[Hash[Symbol, untyped]]
|
|
19
24
|
|
|
@@ -29,15 +34,17 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
29
34
|
# : (**untyped) -> void
|
|
30
35
|
def initialize: (**untyped) -> void
|
|
31
36
|
|
|
32
|
-
# Stubs the next response; call repeatedly to queue several.
|
|
37
|
+
# Stubs the next response; call repeatedly to queue several. +finish_reason+
|
|
38
|
+
# defaults to +:tool_calls+ when tool calls are present, else +:stop+.
|
|
33
39
|
#
|
|
34
40
|
# provider.stub_response("Hello")
|
|
35
41
|
# provider.stub_response("", tool_calls: [{name: "my_tool", arguments: '{"key":"value"}'}])
|
|
36
42
|
# provider.stub_response("Final response", token_usage: Riffer::Providers::TokenUsage.new(input_tokens: 10, output_tokens: 5))
|
|
43
|
+
# provider.stub_response("Truncated...", finish_reason: :length)
|
|
37
44
|
#
|
|
38
45
|
# --
|
|
39
|
-
# : (String, ?tool_calls: Array[Hash[Symbol, untyped]], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
40
|
-
def stub_response: (String, ?tool_calls: Array[Hash[Symbol, untyped]], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
46
|
+
# : (String, ?tool_calls: Array[Hash[Symbol, untyped]], ?token_usage: Riffer::Providers::TokenUsage?, ?finish_reason: Symbol?) -> void
|
|
47
|
+
def stub_response: (String, ?tool_calls: Array[Hash[Symbol, untyped]], ?token_usage: Riffer::Providers::TokenUsage?, ?finish_reason: Symbol?) -> void
|
|
41
48
|
|
|
42
49
|
# Clears all stubbed responses.
|
|
43
50
|
#
|
|
@@ -63,6 +70,10 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
63
70
|
# : (untyped) -> Riffer::Providers::TokenUsage?
|
|
64
71
|
def extract_token_usage: (untyped) -> Riffer::Providers::TokenUsage?
|
|
65
72
|
|
|
73
|
+
# --
|
|
74
|
+
# : (untyped) -> Riffer::Providers::FinishReason?
|
|
75
|
+
def extract_finish_reason: (untyped) -> Riffer::Providers::FinishReason?
|
|
76
|
+
|
|
66
77
|
# --
|
|
67
78
|
# : (untyped) -> String
|
|
68
79
|
def extract_content: (untyped) -> String
|
|
@@ -72,8 +83,8 @@ class Riffer::Providers::Mock < Riffer::Providers::Base
|
|
|
72
83
|
def extract_tool_calls: (untyped) -> Array[Riffer::Messages::Assistant::ToolCall]
|
|
73
84
|
|
|
74
85
|
# --
|
|
75
|
-
# : (Hash[Symbol, untyped],
|
|
76
|
-
def execute_stream: (Hash[Symbol, untyped],
|
|
86
|
+
# : (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
87
|
+
def execute_stream: (Hash[Symbol, untyped], Riffer::Providers::_EventSink) -> void
|
|
77
88
|
|
|
78
89
|
# --
|
|
79
90
|
# : () -> Hash[Symbol, untyped]
|