riffer 0.48.0 → 0.49.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 +19 -0
- data/docs/AGENTS.md +3 -5
- data/docs/AGENT_LIFECYCLE.md +13 -16
- data/docs/CONFIGURATION.md +22 -33
- data/docs/TOOL_ADVANCED.md +1 -3
- data/docs/TRACING.md +1 -1
- data/docs/providers/AMAZON_BEDROCK.md +31 -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 +101 -42
- data/lib/riffer/providers/anthropic.rb +9 -24
- data/lib/riffer/providers/azure_open_ai.rb +3 -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 +7 -17
- 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 +29 -25
- data/sig/generated/riffer/providers/anthropic.rbs +0 -10
- data/sig/generated/riffer/providers/azure_open_ai.rbs +0 -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 +2 -9
- 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
|
@@ -1,31 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents an interrupt during streaming, fired when a callback throws
|
|
5
|
-
# +:riffer_interrupt+.
|
|
6
4
|
class Riffer::StreamEvents::Interrupt < Riffer::StreamEvents::Base
|
|
7
|
-
# The reason provided with the interrupt, if any.
|
|
8
5
|
attr_reader :reason #: (String | Symbol)? # @dynamic reason
|
|
9
6
|
|
|
10
|
-
# Call ids of tool_use blocks riffer filled with placeholder results when the
|
|
11
|
-
# interrupt fired (only when history healing is on).
|
|
12
|
-
attr_reader :healed_tool_call_ids #: Array[String] # @dynamic healed_tool_call_ids
|
|
13
|
-
|
|
14
7
|
#--
|
|
15
|
-
#: (?reason: (String | Symbol)
|
|
16
|
-
def initialize(reason: nil
|
|
8
|
+
#: (?reason: (String | Symbol)?) -> void
|
|
9
|
+
def initialize(reason: nil)
|
|
17
10
|
super(role: :system)
|
|
18
11
|
@reason = reason
|
|
19
|
-
@healed_tool_call_ids = healed_tool_call_ids
|
|
20
12
|
end
|
|
21
13
|
|
|
22
|
-
# Converts the event to a hash.
|
|
23
14
|
#--
|
|
24
15
|
#: () -> Hash[Symbol, untyped]
|
|
25
16
|
def to_h
|
|
26
17
|
h = { role: @role, interrupt: true } #: Hash[Symbol, untyped]
|
|
27
18
|
h[:reason] = @reason if @reason
|
|
28
|
-
h[:healed_tool_call_ids] = @healed_tool_call_ids unless @healed_tool_call_ids.empty?
|
|
29
19
|
h
|
|
30
20
|
end
|
|
31
21
|
end
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents an incremental reasoning chunk during streaming; only emitted by
|
|
5
|
-
# providers that support reasoning (e.g. OpenAI with the reasoning option).
|
|
6
4
|
class Riffer::StreamEvents::ReasoningDelta < Riffer::StreamEvents::Base
|
|
7
|
-
# The incremental reasoning content.
|
|
8
5
|
attr_reader :content #: String # @dynamic content
|
|
9
6
|
|
|
10
7
|
#--
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents one completed reasoning block during streaming; only emitted by
|
|
5
|
-
# providers that support reasoning (e.g. OpenAI with the reasoning option).
|
|
6
4
|
class Riffer::StreamEvents::ReasoningDone < Riffer::StreamEvents::Base
|
|
7
|
-
#
|
|
8
|
-
# message. Its +text+ is the content the preceding ReasoningDelta events
|
|
9
|
-
# added up to.
|
|
5
|
+
# +text+ is the concatenation of the preceding ReasoningDelta events.
|
|
10
6
|
attr_reader :part #: Riffer::Messages::Assistant::ReasoningPart # @dynamic part
|
|
11
7
|
|
|
12
8
|
#--
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Emitted when a skill is activated during streaming, via the +on_activate+
|
|
5
|
-
# callback when the LLM calls the activation tool.
|
|
6
4
|
class Riffer::StreamEvents::SkillActivation < Riffer::StreamEvents::Base
|
|
7
|
-
# The activated skill name.
|
|
8
5
|
attr_reader :name #: String # @dynamic name
|
|
9
6
|
|
|
10
7
|
#--
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents an incremental text chunk during streaming.
|
|
5
4
|
class Riffer::StreamEvents::TextDelta < Riffer::StreamEvents::Base
|
|
6
|
-
# The incremental text content.
|
|
7
5
|
attr_reader :content #: String # @dynamic content
|
|
8
6
|
|
|
9
7
|
#--
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents completed text generation during streaming.
|
|
5
4
|
class Riffer::StreamEvents::TextDone < Riffer::StreamEvents::Base
|
|
6
|
-
# The complete text content.
|
|
7
5
|
attr_reader :content #: String # @dynamic content
|
|
8
6
|
|
|
9
7
|
#--
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Final token usage for the response, emitted when the LLM finishes.
|
|
5
4
|
class Riffer::StreamEvents::TokenUsageDone < Riffer::StreamEvents::Base
|
|
6
|
-
# The token usage data for this response.
|
|
7
5
|
attr_reader :token_usage #: Riffer::Providers::TokenUsage # @dynamic token_usage
|
|
8
6
|
|
|
9
7
|
#--
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents an incremental tool call chunk (partial argument data) during
|
|
5
|
-
# streaming.
|
|
6
4
|
class Riffer::StreamEvents::ToolCallDelta < Riffer::StreamEvents::Base
|
|
7
|
-
# The tool call item identifier.
|
|
8
5
|
attr_reader :item_id #: String # @dynamic item_id
|
|
9
6
|
|
|
10
|
-
#
|
|
7
|
+
# Providers may send the name only on the first delta.
|
|
11
8
|
attr_reader :name #: String? # @dynamic name
|
|
12
9
|
|
|
13
|
-
# The incremental arguments JSON fragment.
|
|
14
10
|
attr_reader :arguments_delta #: String # @dynamic arguments_delta
|
|
15
11
|
|
|
16
12
|
#--
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents a completed tool call during streaming.
|
|
5
4
|
class Riffer::StreamEvents::ToolCallDone < Riffer::StreamEvents::Base
|
|
6
|
-
# The tool call item identifier.
|
|
7
5
|
attr_reader :item_id #: String # @dynamic item_id
|
|
8
6
|
|
|
9
|
-
# The call identifier for response matching.
|
|
10
7
|
attr_reader :call_id #: String # @dynamic call_id
|
|
11
8
|
|
|
12
|
-
# The tool name.
|
|
13
9
|
attr_reader :name #: String # @dynamic name
|
|
14
10
|
|
|
15
|
-
# The complete arguments JSON string.
|
|
16
11
|
attr_reader :arguments #: String # @dynamic arguments
|
|
17
12
|
|
|
18
13
|
#--
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# The result of a completed server-side web search during streaming.
|
|
5
4
|
class Riffer::StreamEvents::WebSearchDone < Riffer::StreamEvents::Base
|
|
6
|
-
# The search query used.
|
|
7
5
|
attr_reader :query #: String # @dynamic query
|
|
8
6
|
|
|
9
|
-
# The search result sources with title and url.
|
|
10
7
|
attr_reader :sources #: Array[Hash[Symbol, String?]] # @dynamic sources
|
|
11
8
|
|
|
12
9
|
#--
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# A web search status notification, emitted as a server-side web search
|
|
5
|
-
# progresses.
|
|
6
4
|
class Riffer::StreamEvents::WebSearchStatus < Riffer::StreamEvents::Base
|
|
7
|
-
#
|
|
5
|
+
# One of "in_progress", "searching", "open_page", or "completed".
|
|
8
6
|
attr_reader :status #: String # @dynamic status
|
|
9
7
|
|
|
10
|
-
# The URL being fetched (present for "open_page" status).
|
|
11
8
|
attr_reader :url #: String? # @dynamic url
|
|
12
9
|
|
|
13
|
-
# The search query (present when available during status changes).
|
|
14
10
|
attr_reader :query #: String? # @dynamic query
|
|
15
11
|
|
|
16
12
|
#--
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rbs-inline stays disabled
|
|
4
|
-
#
|
|
5
|
-
# sig/_private/riffer/testing/minitest.rbs.
|
|
3
|
+
# rbs-inline stays disabled: a shipped signature must never name Minitest. The
|
|
4
|
+
# stub lives in sig/_private/riffer/testing/minitest.rbs.
|
|
6
5
|
|
|
7
6
|
require "riffer"
|
|
8
7
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
8
|
+
# Minitest is never a riffer dependency; the consumer's test_helper loads it
|
|
9
|
+
# before requiring this file.
|
|
11
10
|
module Riffer::Testing::MinitestCleanup
|
|
12
11
|
# Minitest reserves +after_teardown+ for library extensions; +teardown+
|
|
13
12
|
# belongs to the test author.
|
data/lib/riffer/testing.rb
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Builds throwaway agents and tools a test suite can resolve by identifier or
|
|
5
|
-
# constant name, and removes them again. Require <tt>riffer/testing/rspec</tt>
|
|
6
|
-
# or <tt>riffer/testing/minitest</tt> to get +stub_agent+/+stub_tool+ in every
|
|
7
|
-
# example plus per-test cleanup; otherwise include this module and call +reset!+
|
|
8
|
-
# from your own teardown, or call the methods on the module directly.
|
|
9
|
-
#
|
|
10
|
-
# Tracking is not synchronized — stub from a single-threaded test, before
|
|
11
|
-
# concurrent lookups begin.
|
|
12
4
|
module Riffer::Testing
|
|
13
5
|
extend self
|
|
14
6
|
|
|
@@ -17,43 +9,18 @@ module Riffer::Testing
|
|
|
17
9
|
CONST_NAME_PATTERN = /\A[A-Z][A-Za-z0-9_]*\z/ #: Regexp
|
|
18
10
|
private_constant :CONST_NAME_PATTERN
|
|
19
11
|
|
|
20
|
-
# Builds an agent class, evaluates the optional body in it, and makes it
|
|
21
|
-
# resolvable until the next +reset!+. A +name+ assigns a top-level constant
|
|
22
|
-
# and derives the identifier; the body may set +identifier+ like any other
|
|
23
|
-
# agent config.
|
|
24
|
-
#
|
|
25
|
-
# agent = stub_agent("SupportAgent") { model "mock/gpt-5-mini" }
|
|
26
|
-
#
|
|
27
|
-
# Raises Riffer::ArgumentError when the constant is already defined or the
|
|
28
|
-
# stub ends up with no identifier, and Riffer::DuplicateIdentifierError when
|
|
29
|
-
# another agent already holds the identifier.
|
|
30
|
-
#
|
|
31
12
|
#--
|
|
32
13
|
#: (?(String | Symbol)?, ?base: singleton(Riffer::Agent)) ?{ () [self: singleton(Riffer::Agent)] -> void } -> singleton(Riffer::Agent)
|
|
33
14
|
def stub_agent(name = nil, base: Riffer::Agent, &body)
|
|
34
15
|
build_stub(name, base: base, &body) #: singleton(Riffer::Agent)
|
|
35
16
|
end
|
|
36
17
|
|
|
37
|
-
# Builds a tool class, evaluates the optional body in it, and makes it
|
|
38
|
-
# resolvable until the next +reset!+. A +name+ assigns a top-level constant
|
|
39
|
-
# and derives the identifier; the body may set +identifier+ like any other
|
|
40
|
-
# tool config.
|
|
41
|
-
#
|
|
42
|
-
# tool = stub_tool("KbSearch") { def call(context:, **) = text("stubbed") }
|
|
43
|
-
#
|
|
44
|
-
# Raises Riffer::ArgumentError when the constant is already defined or the
|
|
45
|
-
# stub ends up with no identifier, and Riffer::DuplicateIdentifierError when
|
|
46
|
-
# another tool already holds the identifier.
|
|
47
|
-
#
|
|
48
18
|
#--
|
|
49
19
|
#: (?(String | Symbol)?, ?base: singleton(Riffer::Tool)) ?{ () [self: singleton(Riffer::Tool)] -> void } -> singleton(Riffer::Tool)
|
|
50
20
|
def stub_tool(name = nil, base: Riffer::Tool, &body)
|
|
51
21
|
build_stub(name, base: base, &body) #: singleton(Riffer::Tool)
|
|
52
22
|
end
|
|
53
23
|
|
|
54
|
-
# Removes every stub built since the last reset — its registration and any
|
|
55
|
-
# constant it created — newest first, and forgets them. A no-op when nothing
|
|
56
|
-
# has been stubbed.
|
|
57
24
|
#--
|
|
58
25
|
#: () -> void
|
|
59
26
|
def reset!
|
|
@@ -66,12 +33,12 @@ module Riffer::Testing
|
|
|
66
33
|
tracked.clear
|
|
67
34
|
end
|
|
68
35
|
|
|
69
|
-
# The stub classes awaiting cleanup. Lives on the module rather than the
|
|
70
|
-
# caller so an including test case and a direct
|
|
71
|
-
# <tt>Riffer::Testing.stub_*</tt> call share one list.
|
|
72
36
|
#--
|
|
73
37
|
#: () -> Array[[Class, String?]]
|
|
74
38
|
def self.registrations # :nodoc:
|
|
39
|
+
# Lives on the module rather than the caller so an including test case and
|
|
40
|
+
# a direct <tt>Riffer::Testing.stub_*</tt> call share one list. Not
|
|
41
|
+
# synchronized: stub from a single-threaded test.
|
|
75
42
|
@registrations ||= []
|
|
76
43
|
end
|
|
77
44
|
|
|
@@ -118,11 +85,11 @@ module Riffer::Testing
|
|
|
118
85
|
const_name
|
|
119
86
|
end
|
|
120
87
|
|
|
121
|
-
# A test may have removed or replaced the constant itself, so never clobber
|
|
122
|
-
# one that no longer points at the stub.
|
|
123
88
|
#--
|
|
124
89
|
#: (String, Class) -> void
|
|
125
90
|
def remove_stub_const(const_name, stub)
|
|
91
|
+
# A test may have removed or replaced the constant itself, so never clobber
|
|
92
|
+
# one that no longer points at the stub.
|
|
126
93
|
return unless Object.const_defined?(const_name, false) && Object.const_get(const_name, false).equal?(stub)
|
|
127
94
|
|
|
128
95
|
Object.send(:remove_const, const_name)
|
data/lib/riffer/tool.rb
CHANGED
|
@@ -3,64 +3,36 @@
|
|
|
3
3
|
|
|
4
4
|
require "timeout"
|
|
5
5
|
|
|
6
|
-
# Base class for all tools in the Riffer framework. Subclasses must implement
|
|
7
|
-
# the +call+ method.
|
|
8
|
-
#
|
|
9
|
-
# class WeatherLookupTool < Riffer::Tool
|
|
10
|
-
# description "Provides current weather information for a specified city."
|
|
11
|
-
#
|
|
12
|
-
# params do
|
|
13
|
-
# required :city, String, description: "The city to look up"
|
|
14
|
-
# optional :units, String, default: "celsius"
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
17
|
-
# def call(context:, city:, units: nil)
|
|
18
|
-
# # Implementation
|
|
19
|
-
# end
|
|
20
|
-
# end
|
|
21
|
-
#
|
|
22
6
|
class Riffer::Tool
|
|
23
7
|
extend Riffer::Tools::Toolable
|
|
24
8
|
extend Riffer::Registrable
|
|
25
9
|
|
|
26
10
|
kind :tool
|
|
27
11
|
|
|
28
|
-
# Executes the tool with the given arguments.
|
|
29
12
|
#--
|
|
30
13
|
#: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
31
14
|
def call(context:, **kwargs)
|
|
32
15
|
raise NotImplementedError, "#{self.class} must implement #call"
|
|
33
16
|
end
|
|
34
17
|
|
|
35
|
-
# Creates a text response.
|
|
36
|
-
#
|
|
37
18
|
#--
|
|
38
19
|
#: (untyped) -> Riffer::Tools::Response
|
|
39
20
|
def text(result)
|
|
40
21
|
Riffer::Tools::Response.text(result)
|
|
41
22
|
end
|
|
42
23
|
|
|
43
|
-
# Creates a JSON response.
|
|
44
|
-
#
|
|
45
24
|
#--
|
|
46
25
|
#: (untyped) -> Riffer::Tools::Response
|
|
47
26
|
def json(result)
|
|
48
27
|
Riffer::Tools::Response.json(result)
|
|
49
28
|
end
|
|
50
29
|
|
|
51
|
-
# Creates an error response.
|
|
52
|
-
#
|
|
53
30
|
#--
|
|
54
31
|
#: (String, ?type: Symbol) -> Riffer::Tools::Response
|
|
55
32
|
def error(message, type: :execution_error)
|
|
56
33
|
Riffer::Tools::Response.error(message, type: type)
|
|
57
34
|
end
|
|
58
35
|
|
|
59
|
-
# Executes the tool with validation and timeout, folding every +StandardError+
|
|
60
|
-
# into an error Response. Anything outside +StandardError+ — an unimplemented
|
|
61
|
-
# +#call+ above all — still propagates, because a broken tool is a broken
|
|
62
|
-
# deploy rather than a bad request.
|
|
63
|
-
#
|
|
64
36
|
#--
|
|
65
37
|
#: (context: Riffer::Agent::Context?, **untyped) -> Riffer::Tools::Response
|
|
66
38
|
def call_with_validation(context:, **kwargs)
|
|
@@ -88,6 +60,8 @@ class Riffer::Tool
|
|
|
88
60
|
)
|
|
89
61
|
rescue Riffer::ToolExecutionError => e
|
|
90
62
|
Riffer::Tools::Response.error(e.message, type: :execution_error)
|
|
63
|
+
# Anything outside StandardError — an unimplemented #call above all — still
|
|
64
|
+
# propagates: a broken tool is a broken deploy, not a bad request.
|
|
91
65
|
rescue StandardError => e
|
|
92
66
|
Riffer::Tools::Response.error(
|
|
93
67
|
"Error executing tool: #{e.class}: #{e.message}",
|
|
@@ -3,39 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# class MyTool < Riffer::Tool
|
|
9
|
-
# def call(context:, **kwargs)
|
|
10
|
-
# result = perform_operation
|
|
11
|
-
# Riffer::Tools::Response.success(result)
|
|
12
|
-
# rescue MyError => e
|
|
13
|
-
# Riffer::Tools::Response.error(e.message)
|
|
14
|
-
# end
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
6
|
+
# Every tool's +call+ must return one.
|
|
17
7
|
class Riffer::Tools::Response
|
|
18
8
|
# @rbs @success: bool
|
|
19
9
|
|
|
20
10
|
VALID_FORMATS = %i[text json].freeze #: Array[Symbol]
|
|
21
11
|
|
|
22
|
-
# The response content.
|
|
23
12
|
attr_reader :content #: String # @dynamic content
|
|
24
|
-
|
|
25
|
-
# The error message, or +nil+ on success.
|
|
26
13
|
attr_reader :error_message #: String? # @dynamic error_message
|
|
27
|
-
|
|
28
|
-
# The error type, or +nil+ on success.
|
|
29
14
|
attr_reader :error_type #: Symbol? # @dynamic error_type
|
|
30
15
|
|
|
31
|
-
#
|
|
32
|
-
#
|
|
16
|
+
# Kept out of every serialized form so it never reaches an LLM or a message
|
|
17
|
+
# payload.
|
|
33
18
|
attr_reader :exception #: Exception? # @dynamic exception
|
|
34
19
|
|
|
35
|
-
# Creates a success response.
|
|
36
|
-
#
|
|
37
|
-
# Raises Riffer::ArgumentError if format is invalid.
|
|
38
|
-
#
|
|
39
20
|
#--
|
|
40
21
|
#: (untyped, ?format: Symbol) -> Riffer::Tools::Response
|
|
41
22
|
def self.success(result, format: :text)
|
|
@@ -47,42 +28,32 @@ class Riffer::Tools::Response
|
|
|
47
28
|
new(content: content, success: true)
|
|
48
29
|
end
|
|
49
30
|
|
|
50
|
-
# Creates a success response with text format.
|
|
51
|
-
#
|
|
52
31
|
#--
|
|
53
32
|
#: (untyped) -> Riffer::Tools::Response
|
|
54
33
|
def self.text(result)
|
|
55
34
|
success(result, format: :text)
|
|
56
35
|
end
|
|
57
36
|
|
|
58
|
-
# Creates a success response with JSON format.
|
|
59
|
-
#
|
|
60
37
|
#--
|
|
61
38
|
#: (untyped) -> Riffer::Tools::Response
|
|
62
39
|
def self.json(result)
|
|
63
40
|
success(result, format: :json)
|
|
64
41
|
end
|
|
65
42
|
|
|
66
|
-
# Creates an error response.
|
|
67
|
-
#
|
|
68
43
|
#--
|
|
69
44
|
#: (String, ?type: Symbol, ?exception: Exception?) -> Riffer::Tools::Response
|
|
70
45
|
def self.error(message, type: :execution_error, exception: nil)
|
|
71
46
|
new(content: message, success: false, error_message: message, error_type: type, exception: exception)
|
|
72
47
|
end
|
|
73
48
|
|
|
74
|
-
# Returns true if the tool execution succeeded.
|
|
75
49
|
#--
|
|
76
50
|
#: () -> bool
|
|
77
51
|
def success? = @success
|
|
78
52
|
|
|
79
|
-
# Returns true if the tool execution failed.
|
|
80
53
|
#--
|
|
81
54
|
#: () -> bool
|
|
82
55
|
def error? = !@success
|
|
83
56
|
|
|
84
|
-
# Returns a hash representation of the response.
|
|
85
|
-
#
|
|
86
57
|
#--
|
|
87
58
|
#: () -> Hash[Symbol, untyped]
|
|
88
59
|
def to_h
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Executes tool calls concurrently using fibers via the +async+ gem.
|
|
5
|
-
#
|
|
6
|
-
# class MyAgent < Riffer::Agent
|
|
7
|
-
# tool_runtime Riffer::Tools::Runtime::Fibers
|
|
8
|
-
# end
|
|
9
|
-
#
|
|
10
4
|
class Riffer::Tools::Runtime::Fibers < Riffer::Tools::Runtime
|
|
11
5
|
#--
|
|
12
6
|
#: (?max_concurrency: Integer?) -> void
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Executes tool calls concurrently using threads.
|
|
5
|
-
#
|
|
6
|
-
# class MyAgent < Riffer::Agent
|
|
7
|
-
# tool_runtime Riffer::Tools::Runtime::Threaded
|
|
8
|
-
# end
|
|
9
|
-
#
|
|
10
4
|
class Riffer::Tools::Runtime::Threaded < Riffer::Tools::Runtime
|
|
11
5
|
DEFAULT_MAX_CONCURRENCY = 5 #: Integer
|
|
12
6
|
|
data/lib/riffer/tools/runtime.rb
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
|
-
# Handles tool call execution for an agent, composing with a Riffer::Runner for
|
|
7
|
-
# concurrency. Subclass and override +dispatch_tool_call+ to customize dispatch
|
|
8
|
-
# (e.g. HTTP, gRPC).
|
|
9
6
|
class Riffer::Tools::Runtime
|
|
10
7
|
# @rbs @runner: Riffer::Runner
|
|
11
8
|
|
|
@@ -20,17 +17,15 @@ class Riffer::Tools::Runtime
|
|
|
20
17
|
@runner = runner
|
|
21
18
|
end
|
|
22
19
|
|
|
23
|
-
# Executes a batch of tool calls, returning <tt>[tool_call, response]</tt> pairs.
|
|
24
20
|
#--
|
|
25
21
|
#: (Array[Riffer::Messages::Assistant::ToolCall], tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?, ?tags: Hash[String, String]) -> Array[[Riffer::Messages::Assistant::ToolCall, Riffer::Tools::Response]]
|
|
26
22
|
def execute(tool_calls, tools:, context:, assistant_message: nil, tags: {})
|
|
27
23
|
# Each Runner worker runs in its own thread/fiber, where the OTEL context
|
|
28
24
|
# starts empty — capture here so the execute_tool span parents correctly.
|
|
29
|
-
# tags are an ordinary local captured in the block, so they reach the
|
|
30
|
-
# worker's span without re-propagation.
|
|
31
25
|
trace_context = Riffer::Tracing.current_context
|
|
32
26
|
@runner.map(tool_calls, context: context) do |tool_call|
|
|
33
27
|
Riffer::Tracing.with_context(trace_context) do
|
|
28
|
+
# Outside around_tool_call so host enrichment spans nest beneath it.
|
|
34
29
|
instrument_tool_call(tool_call, tags) do
|
|
35
30
|
around_tool_call(tool_call, context: context, assistant_message: assistant_message) do
|
|
36
31
|
dispatch_tool_call(tool_call, tools: tools, context: context, assistant_message: assistant_message)
|
|
@@ -40,20 +35,7 @@ class Riffer::Tools::Runtime
|
|
|
40
35
|
end
|
|
41
36
|
end
|
|
42
37
|
|
|
43
|
-
#
|
|
44
|
-
# customize. Must +yield+ to continue.
|
|
45
|
-
#
|
|
46
|
-
# class InstrumentedRuntime < Riffer::Tools::Runtime::Inline
|
|
47
|
-
# private
|
|
48
|
-
#
|
|
49
|
-
# def around_tool_call(tool_call, context:, assistant_message: nil)
|
|
50
|
-
# start = Time.now
|
|
51
|
-
# result = yield
|
|
52
|
-
# Rails.logger.info("Tool #{tool_call.name} took #{Time.now - start}s")
|
|
53
|
-
# result
|
|
54
|
-
# end
|
|
55
|
-
# end
|
|
56
|
-
#
|
|
38
|
+
# Overrides must +yield+ and return its Response.
|
|
57
39
|
#--
|
|
58
40
|
#: (Riffer::Messages::Assistant::ToolCall, context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) { () -> Riffer::Tools::Response } -> Riffer::Tools::Response
|
|
59
41
|
def around_tool_call(_tool_call, context:, assistant_message: nil)
|
|
@@ -73,6 +55,7 @@ class Riffer::Tools::Runtime
|
|
|
73
55
|
[tool_call, result] #: [Riffer::Messages::Assistant::ToolCall, Riffer::Tools::Response]
|
|
74
56
|
end
|
|
75
57
|
|
|
58
|
+
# Subclasses override this to dispatch elsewhere (e.g. HTTP, gRPC).
|
|
76
59
|
#--
|
|
77
60
|
#: (Riffer::Messages::Assistant::ToolCall, tools: Array[singleton(Riffer::Tool)], context: Riffer::Agent::Context?, ?assistant_message: Riffer::Messages::Assistant?) -> Riffer::Tools::Response
|
|
78
61
|
def dispatch_tool_call(tool_call, tools:, context:, assistant_message: nil)
|
|
@@ -108,7 +91,6 @@ class Riffer::Tools::Runtime
|
|
|
108
91
|
JSON.parse(arguments, symbolize_names: true)
|
|
109
92
|
end
|
|
110
93
|
|
|
111
|
-
# Emitted outside +around_tool_call+ so host enrichment spans nest beneath it.
|
|
112
94
|
#--
|
|
113
95
|
#: [R] (Riffer::Messages::Assistant::ToolCall, ?Hash[String, String]) { ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span)) -> R } -> R
|
|
114
96
|
def in_tool_span(tool_call, tags = {})
|
|
@@ -137,23 +119,20 @@ class Riffer::Tools::Runtime
|
|
|
137
119
|
}.merge(tag_attributes(tags))
|
|
138
120
|
end
|
|
139
121
|
|
|
140
|
-
# Maps normalized tags to their namespaced span attribute form. An empty map
|
|
141
|
-
# yields an empty hash, so merging it is a no-op.
|
|
142
122
|
#--
|
|
143
123
|
#: (Hash[String, String]) -> Hash[String, String]
|
|
144
124
|
def tag_attributes(tags)
|
|
145
125
|
tags.transform_keys { |key| "riffer.tag.#{key}" }
|
|
146
126
|
end
|
|
147
127
|
|
|
148
|
-
# A deliberate error Response is a handled outcome, so its status stays unset.
|
|
149
|
-
# An error status is reserved for a Response carrying the exception it was
|
|
150
|
-
# folded from — the tool failed for a reason nobody anticipated.
|
|
151
128
|
#--
|
|
152
129
|
#: ((Riffer::Tracing::Otel::Span | Riffer::Tracing::NoOp::Span), Riffer::Tools::Response) -> void
|
|
153
130
|
def record_tool_outcome(span, result)
|
|
154
131
|
error_type = result.error_type
|
|
155
132
|
span.set_attribute("error.type", error_type.to_s) if error_type
|
|
156
133
|
|
|
134
|
+
# A deliberate error Response is a handled outcome, so only one carrying the
|
|
135
|
+
# exception it was folded from gets an error status.
|
|
157
136
|
exception = result.exception
|
|
158
137
|
if exception
|
|
159
138
|
span.record_exception(exception)
|