riffer 0.47.2 → 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 +42 -0
- data/docs/AGENTS.md +39 -7
- data/docs/AGENT_LIFECYCLE.md +14 -16
- data/docs/CONFIGURATION.md +23 -34
- data/docs/EVALS.md +2 -1
- data/docs/MCP.md +0 -4
- data/docs/MESSAGES.md +85 -17
- data/docs/STREAM_EVENTS.md +8 -4
- data/docs/TOOL_ADVANCED.md +1 -3
- data/docs/TRACING.md +2 -2
- data/docs/providers/AMAZON_BEDROCK.md +32 -1
- data/docs/providers/CUSTOM_PROVIDERS.md +56 -4
- data/docs/providers/GEMINI.md +1 -1
- data/docs/providers/MOCK_PROVIDER.md +17 -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 +27 -44
- data/lib/riffer/agent/context.rb +2 -26
- data/lib/riffer/agent/outcome.rb +0 -20
- data/lib/riffer/agent/response.rb +4 -35
- data/lib/riffer/agent/run.rb +32 -48
- data/lib/riffer/agent/serializer.rb +10 -39
- data/lib/riffer/agent/session/repair.rb +3 -15
- data/lib/riffer/agent/session.rb +26 -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 +31 -127
- 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 +22 -23
- data/lib/riffer/evals/evaluator_runner.rb +0 -15
- data/lib/riffer/evals/judge.rb +9 -11
- 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 +5 -10
- 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 +37 -0
- 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 +73 -0
- data/lib/riffer/messages/assistant/tool_call.rb +55 -0
- data/lib/riffer/messages/assistant.rb +43 -15
- data/lib/riffer/messages/base.rb +5 -33
- data/lib/riffer/messages/system.rb +8 -1
- data/lib/riffer/messages/tool.rb +15 -13
- data/lib/riffer/messages/{file_part.rb → user/file_part.rb} +16 -40
- data/lib/riffer/messages/user.rb +11 -4
- data/lib/riffer/params/boolean.rb +1 -5
- data/lib/riffer/params/param.rb +15 -31
- data/lib/riffer/params.rb +15 -39
- data/lib/riffer/providers/amazon_bedrock.rb +104 -44
- data/lib/riffer/providers/anthropic.rb +11 -26
- data/lib/riffer/providers/azure_open_ai.rb +3 -8
- data/lib/riffer/providers/base.rb +30 -32
- data/lib/riffer/providers/finish_reason.rb +0 -6
- data/lib/riffer/providers/gemini/client.rb +4 -17
- data/lib/riffer/providers/gemini.rb +6 -12
- data/lib/riffer/providers/mock.rb +18 -27
- data/lib/riffer/providers/open_ai.rb +12 -21
- data/lib/riffer/providers/open_router.rb +109 -33
- data/lib/riffer/providers/repository.rb +2 -14
- data/lib/riffer/providers/token_usage.rb +19 -12
- 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 +13 -17
- 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 +6 -8
- 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 +8 -30
- data/lib/riffer/tools/toolable.rb +0 -37
- data/lib/riffer/tracing/capture.rb +5 -9
- 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 +24 -46
- 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 +4 -27
- data/sig/generated/riffer/agent/run.rbs +12 -31
- 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 +25 -125
- 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 +12 -21
- data/sig/generated/riffer/evals/evaluator_runner.rbs +0 -14
- data/sig/generated/riffer/evals/judge.rbs +4 -8
- 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 +6 -13
- 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 +13 -0
- 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 +45 -0
- data/sig/generated/riffer/messages/assistant/tool_call.rbs +34 -0
- data/sig/generated/riffer/messages/assistant.rbs +25 -31
- data/sig/generated/riffer/messages/base.rbs +0 -12
- data/sig/generated/riffer/messages/system.rbs +4 -1
- data/sig/generated/riffer/messages/tool.rbs +4 -10
- data/sig/generated/riffer/messages/user/file_part.rbs +76 -0
- data/sig/generated/riffer/messages/user.rbs +7 -5
- data/sig/generated/riffer/params/boolean.rbs +1 -4
- data/sig/generated/riffer/params/param.rbs +7 -31
- data/sig/generated/riffer/params.rbs +8 -38
- data/sig/generated/riffer/providers/amazon_bedrock.rbs +33 -29
- data/sig/generated/riffer/providers/anthropic.rbs +2 -12
- data/sig/generated/riffer/providers/azure_open_ai.rbs +0 -8
- data/sig/generated/riffer/providers/base.rbs +18 -34
- 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 +4 -10
- data/sig/generated/riffer/providers/mock.rbs +6 -27
- data/sig/generated/riffer/providers/open_ai.rbs +6 -13
- data/sig/generated/riffer/providers/open_router.rbs +37 -18
- data/sig/generated/riffer/providers/repository.rbs +2 -14
- data/sig/generated/riffer/providers/token_usage.rbs +9 -12
- 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 +6 -17
- 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 +4 -6
- 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 +4 -26
- data/sig/generated/riffer/tools/toolable.rbs +0 -37
- data/sig/generated/riffer/tracing/capture.rbs +6 -9
- 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/deep_dup.rbs +5 -0
- data/sig/manual/riffer/helpers/validate.rbs +5 -0
- metadata +39 -3
- data/sig/generated/riffer/messages/file_part.rbs +0 -101
data/lib/riffer/mcp/registry.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Thread-safe global store for MCP server registrations, keyed by manifest name.
|
|
5
4
|
module Riffer::Mcp::Registry
|
|
6
5
|
extend self
|
|
7
6
|
|
|
@@ -11,8 +10,6 @@ module Riffer::Mcp::Registry
|
|
|
11
10
|
@mutex = Mutex.new
|
|
12
11
|
@store = {} #: Hash[String, Riffer::Mcp::Registration]
|
|
13
12
|
|
|
14
|
-
# Registers an MCP server and starts async tool discovery, replacing any
|
|
15
|
-
# existing registration with the same name.
|
|
16
13
|
#--
|
|
17
14
|
#: ((Hash[Symbol, untyped] | Riffer::Mcp::Manifest)) -> Riffer::Mcp::Registration
|
|
18
15
|
def register(manifest_or_hash)
|
|
@@ -33,8 +30,6 @@ module Riffer::Mcp::Registry
|
|
|
33
30
|
registration
|
|
34
31
|
end
|
|
35
32
|
|
|
36
|
-
# Removes a registration by name.
|
|
37
|
-
#
|
|
38
33
|
#--
|
|
39
34
|
#: ((String | Symbol)) -> void
|
|
40
35
|
def unregister(name)
|
|
@@ -42,16 +37,12 @@ module Riffer::Mcp::Registry
|
|
|
42
37
|
removed&.retire!
|
|
43
38
|
end
|
|
44
39
|
|
|
45
|
-
# Returns a frozen snapshot of all current registrations.
|
|
46
|
-
#
|
|
47
40
|
#--
|
|
48
41
|
#: () -> Hash[String, Riffer::Mcp::Registration]
|
|
49
42
|
def registrations
|
|
50
43
|
@mutex.synchronize { @store.dup.freeze }
|
|
51
44
|
end
|
|
52
45
|
|
|
53
|
-
# Returns all registrations whose manifest tags intersect the given tags
|
|
54
|
-
# (normalized to symbols).
|
|
55
46
|
#--
|
|
56
47
|
#: (Array[Symbol]) -> Array[Riffer::Mcp::Registration]
|
|
57
48
|
def find_by_tags(tags)
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Searches available MCP tools by name or description.
|
|
5
4
|
class Riffer::Mcp::SearchTool < Riffer::Tool
|
|
6
5
|
IDENTIFIER = "mcp_search"
|
|
7
6
|
|
|
8
|
-
# Successful search response carrying the matched tool classes.
|
|
9
7
|
class Result < Riffer::Tools::Response
|
|
10
|
-
# Tool classes that matched the search query.
|
|
11
8
|
attr_reader :discovered_tools #: Array[singleton(Riffer::Tool)] # @dynamic discovered_tools
|
|
12
9
|
|
|
13
10
|
#--
|
|
@@ -25,7 +22,6 @@ class Riffer::Mcp::SearchTool < Riffer::Tool
|
|
|
25
22
|
required :query, String, description: "Non-empty substring to filter tools by name or description."
|
|
26
23
|
end
|
|
27
24
|
|
|
28
|
-
# Searches progressive MCP tools and returns a +Result+ with +discovered_tools+.
|
|
29
25
|
#--
|
|
30
26
|
#: (context: Riffer::Agent::Context?, query: String) -> Riffer::Tools::Response
|
|
31
27
|
def call(context:, query:)
|
data/lib/riffer/mcp/tool.rb
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Base class for MCP-generated tools.
|
|
5
4
|
class Riffer::Mcp::Tool < Riffer::Tool
|
|
6
5
|
# @rbs self.@mcp_server_tool_name: String?
|
|
7
6
|
# @rbs self.@input_schema: Hash[Symbol, untyped]?
|
|
8
7
|
|
|
9
|
-
# Returns the unprefixed tool name used for +tools/call+ on the MCP server.
|
|
10
8
|
#--
|
|
11
9
|
#: () -> String
|
|
12
10
|
def self.mcp_server_tool_name
|
|
13
11
|
@mcp_server_tool_name || raise(NotImplementedError, "#{self} must set @mcp_server_tool_name")
|
|
14
12
|
end
|
|
15
13
|
|
|
16
|
-
# Returns the server-published input schema, falling back to the params DSL.
|
|
17
|
-
# MCP schemas are server-defined, so +strict+ is not applied to them.
|
|
18
14
|
#--
|
|
19
15
|
#: (?strict: bool) -> Hash[Symbol, untyped]
|
|
20
16
|
def self.parameters_schema(strict: false)
|
|
17
|
+
# MCP schemas are server-defined, so +strict+ is not applied to them.
|
|
21
18
|
@input_schema || super
|
|
22
19
|
end
|
|
23
20
|
end
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Generates anonymous Riffer::Mcp::Tool subclasses from MCP tool definitions.
|
|
5
|
-
# Generated tools delegate +#call+ to the MCP client and skip Riffer's param
|
|
6
|
-
# validation — the MCP server validates inputs.
|
|
7
4
|
module Riffer::Mcp::ToolFactory
|
|
8
5
|
extend self
|
|
9
6
|
|
|
10
|
-
# Builds one Riffer::Mcp::Tool subclass per tool definition, prefixing names
|
|
11
|
-
# with the manifest name to avoid cross-server collisions (e.g.
|
|
12
|
-
# +jira__search+); the server-side name stays on +.mcp_server_tool_name+.
|
|
13
7
|
#--
|
|
14
8
|
#: (String, Riffer::Mcp::Client, Array[Hash[Symbol, untyped]]) -> Array[singleton(Riffer::Mcp::Tool)]
|
|
15
9
|
def build(manifest_name, client, tool_defs)
|
|
@@ -25,6 +19,8 @@ module Riffer::Mcp::ToolFactory
|
|
|
25
19
|
|
|
26
20
|
#: (String, Riffer::Mcp::Client, Hash[Symbol, untyped]) -> singleton(Riffer::Mcp::Tool)
|
|
27
21
|
def build_tool_class(manifest_name, client, descriptor)
|
|
22
|
+
# Prefixed to avoid cross-server collisions; the server-side name stays on
|
|
23
|
+
# mcp_server_tool_name.
|
|
28
24
|
prefixed = "#{sanitize_name_component(manifest_name)}__#{sanitize_name_component(descriptor[:name])}"
|
|
29
25
|
|
|
30
26
|
# steep does not model Class.new's class_eval semantics — the block body
|
|
@@ -37,6 +33,7 @@ module Riffer::Mcp::ToolFactory
|
|
|
37
33
|
@description = descriptor[:description]
|
|
38
34
|
@input_schema = descriptor[:input_schema]
|
|
39
35
|
|
|
36
|
+
# No Riffer param validation — the MCP server validates inputs.
|
|
40
37
|
define_method(:call) do |context:, **kwargs|
|
|
41
38
|
text(client.tools_call(self.class.mcp_server_tool_name, kwargs))
|
|
42
39
|
end
|
data/lib/riffer/mcp.rb
CHANGED
|
@@ -1,49 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Integration with Model Context Protocol (MCP) servers. Register servers
|
|
5
|
-
# globally; agents opt-in by tag via the +use_mcp+ DSL. Tags are
|
|
6
|
-
# application-defined; see +docs/MCP.md+.
|
|
7
|
-
#
|
|
8
|
-
# Riffer::Mcp.register(
|
|
9
|
-
# name: "github",
|
|
10
|
-
# tags: [:github],
|
|
11
|
-
# endpoint: "https://mcp.github.com",
|
|
12
|
-
# discovery_headers: -> { {"Authorization" => "Bearer #{ENV['GITHUB_TOKEN']}"} }
|
|
13
|
-
# )
|
|
14
|
-
#
|
|
15
|
-
# class MyAgent < Riffer::Agent
|
|
16
|
-
# model "openai/gpt-4o"
|
|
17
|
-
# use_mcp :github
|
|
18
|
-
# end
|
|
19
|
-
#
|
|
20
4
|
module Riffer::Mcp
|
|
21
5
|
extend self
|
|
22
6
|
|
|
23
|
-
# Base error for all MCP-related failures.
|
|
24
7
|
class Error < Riffer::Error; end
|
|
25
8
|
|
|
26
9
|
# Raised when +Riffer.config.mcp.credentials+ returns +nil+ during +tools/call+
|
|
27
|
-
# after the server's tools were included for this run.
|
|
10
|
+
# after the server's tools were already included for this run.
|
|
28
11
|
class CredentialsDeniedError < Error; end
|
|
29
12
|
|
|
30
|
-
#
|
|
31
|
-
# on discovery failure.
|
|
13
|
+
# Blocks until tool discovery completes.
|
|
32
14
|
#--
|
|
33
15
|
#: ((Hash[Symbol, untyped] | Riffer::Mcp::Manifest)) -> Riffer::Mcp::Registration
|
|
34
16
|
def register(manifest_or_hash)
|
|
35
17
|
Registry.register(manifest_or_hash)
|
|
36
18
|
end
|
|
37
19
|
|
|
38
|
-
# Removes a registration by name; subsequent agent runs won't see its tools.
|
|
39
20
|
#--
|
|
40
21
|
#: (String) -> void
|
|
41
22
|
def unregister(name)
|
|
42
23
|
Registry.unregister(name)
|
|
43
24
|
end
|
|
44
25
|
|
|
45
|
-
# Returns all current registrations keyed by name.
|
|
46
|
-
#
|
|
47
26
|
#--
|
|
48
27
|
#: () -> Hash[String, Riffer::Mcp::Registration]
|
|
49
28
|
def registrations
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
# Stored and replayed verbatim, never interpreted, so a provider that requires its own thinking
|
|
5
|
+
# blocks back gets them unchanged.
|
|
6
|
+
class Riffer::Messages::Assistant::ReasoningPart
|
|
7
|
+
TYPES = %i[text summary encrypted].freeze #: Array[Symbol]
|
|
8
|
+
|
|
9
|
+
attr_reader :type #: Symbol # @dynamic type
|
|
10
|
+
attr_reader :text #: String? # @dynamic text
|
|
11
|
+
attr_reader :data #: String? # @dynamic data
|
|
12
|
+
attr_reader :signature #: String? # @dynamic signature
|
|
13
|
+
attr_reader :id #: String? # @dynamic id
|
|
14
|
+
|
|
15
|
+
# Owned by the producing adapter (e.g. <tt>"anthropic-claude-v1"</tt>); adapters replay only the
|
|
16
|
+
# formats they recognize, so it is never validated here.
|
|
17
|
+
attr_reader :format #: String? # @dynamic format
|
|
18
|
+
|
|
19
|
+
#--
|
|
20
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::Assistant::ReasoningPart)) -> Riffer::Messages::Assistant::ReasoningPart
|
|
21
|
+
def self.from_hash(part)
|
|
22
|
+
return part if part.is_a?(Riffer::Messages::Assistant::ReasoningPart)
|
|
23
|
+
|
|
24
|
+
new(
|
|
25
|
+
type: part[:type].to_sym,
|
|
26
|
+
text: part[:text],
|
|
27
|
+
data: part[:data],
|
|
28
|
+
signature: part[:signature],
|
|
29
|
+
id: part[:id],
|
|
30
|
+
format: part[:format],
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#--
|
|
35
|
+
#: (type: Symbol, ?text: String?, ?data: String?, ?signature: String?, ?id: String?, ?format: String?) -> void
|
|
36
|
+
def initialize(type:, text: nil, data: nil, signature: nil, id: nil, format: nil)
|
|
37
|
+
unless TYPES.include?(type)
|
|
38
|
+
raise Riffer::ArgumentError,
|
|
39
|
+
"type must be one of #{TYPES.inspect}, got #{type.inspect}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@type = type
|
|
43
|
+
@text = text
|
|
44
|
+
@data = data
|
|
45
|
+
@signature = signature
|
|
46
|
+
@id = id
|
|
47
|
+
@format = format
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#--
|
|
51
|
+
#: () -> Hash[Symbol, untyped]
|
|
52
|
+
def to_h
|
|
53
|
+
{ type: type, text: text, data: data, signature: signature, id: id, format: format }.compact
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#--
|
|
57
|
+
#: (untyped) -> bool
|
|
58
|
+
def ==(other)
|
|
59
|
+
other.is_a?(Riffer::Messages::Assistant::ReasoningPart) && to_h == other.to_h
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#--
|
|
63
|
+
#: (untyped) -> bool
|
|
64
|
+
def eql?(other)
|
|
65
|
+
self == other
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
#--
|
|
69
|
+
#: () -> Integer
|
|
70
|
+
def hash
|
|
71
|
+
to_h.hash
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
class Riffer::Messages::Assistant::ToolCall
|
|
5
|
+
attr_reader :call_id #: String # @dynamic call_id
|
|
6
|
+
|
|
7
|
+
attr_reader :name #: String # @dynamic name
|
|
8
|
+
|
|
9
|
+
# JSON-encoded, exactly as the provider emitted them.
|
|
10
|
+
attr_reader :arguments #: String # @dynamic arguments
|
|
11
|
+
|
|
12
|
+
#--
|
|
13
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::Assistant::ToolCall)) -> Riffer::Messages::Assistant::ToolCall
|
|
14
|
+
def self.from_hash(call)
|
|
15
|
+
return call if call.is_a?(Riffer::Messages::Assistant::ToolCall)
|
|
16
|
+
|
|
17
|
+
if call.values_at(:call_id, :name, :arguments).any?(&:nil?)
|
|
18
|
+
raise Riffer::ArgumentError, "Tool call hash must include :call_id, :name, and :arguments"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
new(call_id: call[:call_id], name: call[:name], arguments: call[:arguments])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#--
|
|
25
|
+
#: (call_id: String, name: String, arguments: String) -> void
|
|
26
|
+
def initialize(call_id:, name:, arguments:)
|
|
27
|
+
@call_id = call_id
|
|
28
|
+
@name = name
|
|
29
|
+
@arguments = arguments
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#--
|
|
33
|
+
#: () -> Hash[Symbol, untyped]
|
|
34
|
+
def to_h
|
|
35
|
+
{ call_id: call_id, name: name, arguments: arguments }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#--
|
|
39
|
+
#: (untyped) -> bool
|
|
40
|
+
def ==(other)
|
|
41
|
+
other.is_a?(Riffer::Messages::Assistant::ToolCall) && to_h == other.to_h
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#--
|
|
45
|
+
#: (untyped) -> bool
|
|
46
|
+
def eql?(other)
|
|
47
|
+
self == other
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#--
|
|
51
|
+
#: () -> Integer
|
|
52
|
+
def hash
|
|
53
|
+
to_h.hash
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,35 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents an assistant (LLM) message in a conversation; may include tool
|
|
5
|
-
# calls when the LLM requests tool execution.
|
|
6
4
|
class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
7
|
-
|
|
5
|
+
# The other reasoning part types carry no prose.
|
|
6
|
+
REASONING_TEXT_TYPES = %i[text summary].freeze #: Array[Symbol]
|
|
7
|
+
|
|
8
|
+
#--
|
|
9
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::Assistant)) -> Riffer::Messages::Assistant
|
|
10
|
+
def self.from_hash(msg)
|
|
11
|
+
return msg if msg.is_a?(Riffer::Messages::Assistant)
|
|
12
|
+
|
|
13
|
+
new(
|
|
14
|
+
msg[:content],
|
|
15
|
+
id: msg[:id],
|
|
16
|
+
tool_calls: (msg[:tool_calls] || []).map { |tc| Riffer::Messages::Assistant::ToolCall.from_hash(tc) },
|
|
17
|
+
reasoning: (msg[:reasoning] || []).map { |part| Riffer::Messages::Assistant::ReasoningPart.from_hash(part) },
|
|
18
|
+
token_usage: msg[:token_usage] && Riffer::Providers::TokenUsage.from_hash(msg[:token_usage]),
|
|
19
|
+
structured_output: msg[:structured_output],
|
|
20
|
+
finish_reason: msg[:finish_reason]&.to_sym,
|
|
21
|
+
finish_reason_raw: msg[:finish_reason_raw],
|
|
22
|
+
)
|
|
23
|
+
end
|
|
8
24
|
|
|
9
|
-
# Array of tool calls requested by the assistant.
|
|
10
25
|
attr_reader :tool_calls #: Array[Riffer::Messages::Assistant::ToolCall] # @dynamic tool_calls
|
|
11
26
|
|
|
12
|
-
|
|
27
|
+
attr_reader :reasoning #: Array[Riffer::Messages::Assistant::ReasoningPart] # @dynamic reasoning
|
|
28
|
+
|
|
13
29
|
attr_reader :token_usage #: Riffer::Providers::TokenUsage? # @dynamic token_usage
|
|
14
30
|
|
|
15
|
-
# Parsed structured output hash, or nil when not applicable.
|
|
16
31
|
attr_reader :structured_output #: Hash[Symbol, untyped]? # @dynamic structured_output
|
|
17
32
|
|
|
18
|
-
# Normalized reason the provider finished this response, when reported (see
|
|
19
|
-
# <tt>Riffer::Providers::FinishReason::VALUES</tt>).
|
|
20
33
|
attr_reader :finish_reason #: Symbol? # @dynamic finish_reason
|
|
21
34
|
|
|
22
|
-
# The provider's raw finish-reason value behind +finish_reason+, when one
|
|
23
|
-
# exists on the wire.
|
|
24
35
|
attr_reader :finish_reason_raw #: String? # @dynamic finish_reason_raw
|
|
25
36
|
|
|
26
|
-
# Raises Riffer::ArgumentError when +finish_reason+ is outside the
|
|
27
|
-
# normalized vocabulary.
|
|
28
37
|
#--
|
|
29
38
|
#: (
|
|
30
39
|
# String,
|
|
31
40
|
# ?id: String?,
|
|
32
41
|
# ?tool_calls: Array[Riffer::Messages::Assistant::ToolCall],
|
|
42
|
+
# ?reasoning: Array[Riffer::Messages::Assistant::ReasoningPart],
|
|
33
43
|
# ?token_usage: Riffer::Providers::TokenUsage?,
|
|
34
44
|
# ?structured_output: Hash[Symbol, untyped]?,
|
|
35
45
|
# ?finish_reason: Symbol?,
|
|
@@ -39,6 +49,7 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
39
49
|
content,
|
|
40
50
|
id: nil,
|
|
41
51
|
tool_calls: [],
|
|
52
|
+
reasoning: [],
|
|
42
53
|
token_usage: nil,
|
|
43
54
|
structured_output: nil,
|
|
44
55
|
finish_reason: nil,
|
|
@@ -51,6 +62,7 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
51
62
|
|
|
52
63
|
super(content, id: id)
|
|
53
64
|
@tool_calls = tool_calls
|
|
65
|
+
@reasoning = reasoning
|
|
54
66
|
@token_usage = token_usage
|
|
55
67
|
@structured_output = structured_output
|
|
56
68
|
@finish_reason = finish_reason
|
|
@@ -75,20 +87,36 @@ class Riffer::Messages::Assistant < Riffer::Messages::Base
|
|
|
75
87
|
!@tool_calls.empty?
|
|
76
88
|
end
|
|
77
89
|
|
|
90
|
+
#--
|
|
91
|
+
#: () -> bool
|
|
92
|
+
def reasoning?
|
|
93
|
+
!@reasoning.empty?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#--
|
|
97
|
+
#: () -> String?
|
|
98
|
+
def reasoning_text
|
|
99
|
+
texts = reasoning.filter_map { |part| part.text if REASONING_TEXT_TYPES.include?(part.type) }
|
|
100
|
+
texts.empty? ? nil : texts.join("\n\n")
|
|
101
|
+
end
|
|
102
|
+
|
|
78
103
|
#--
|
|
79
104
|
#: (Riffer::Messages::Assistant) -> Riffer::Messages::Assistant
|
|
80
105
|
def +(other)
|
|
81
|
-
self.class.new(
|
|
106
|
+
self.class.new(
|
|
107
|
+
"#{content}\n\n#{other.content}",
|
|
108
|
+
tool_calls: tool_calls + other.tool_calls,
|
|
109
|
+
reasoning: reasoning + other.reasoning,
|
|
110
|
+
)
|
|
82
111
|
end
|
|
83
112
|
|
|
84
|
-
# Converts the message to a hash.
|
|
85
|
-
#
|
|
86
113
|
#--
|
|
87
114
|
#: () -> Hash[Symbol, untyped]
|
|
88
115
|
def to_h
|
|
89
116
|
hash = { role: role, content: content } #: Hash[Symbol, untyped]
|
|
90
117
|
hash[:id] = id if id
|
|
91
118
|
hash[:tool_calls] = tool_calls.map(&:to_h) unless tool_calls.empty?
|
|
119
|
+
hash[:reasoning] = reasoning.map(&:to_h) if reasoning?
|
|
92
120
|
hash[:token_usage] = token_usage.to_h if token_usage
|
|
93
121
|
hash[:structured_output] = structured_output if structured_output?
|
|
94
122
|
hash[:finish_reason] = finish_reason if finish_reason
|
data/lib/riffer/messages/base.rb
CHANGED
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require "securerandom"
|
|
5
5
|
|
|
6
|
-
# Base class for all message types. Subclasses must implement +role+.
|
|
7
6
|
class Riffer::Messages::Base
|
|
8
|
-
# Builds the matching message subclass from a hash, or returns +msg+ unchanged
|
|
9
|
-
# when it is already a message. Raises Riffer::ArgumentError on an invalid message.
|
|
10
7
|
#--
|
|
11
8
|
#: ((Hash[Symbol, untyped] | Riffer::Messages::Base)) -> Riffer::Messages::Base
|
|
12
9
|
def self.from_hash(msg)
|
|
@@ -15,34 +12,16 @@ class Riffer::Messages::Base
|
|
|
15
12
|
raise Riffer::ArgumentError, "Message hash must include a 'role' key" if msg[:role].nil? || msg[:role].empty?
|
|
16
13
|
|
|
17
14
|
case msg[:role].to_sym
|
|
18
|
-
when :user
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
when :
|
|
22
|
-
|
|
23
|
-
tc.is_a?(Riffer::Messages::Assistant::ToolCall) ? tc : Riffer::Messages::Assistant::ToolCall.new(**tc)
|
|
24
|
-
end
|
|
25
|
-
Riffer::Messages::Assistant.new(
|
|
26
|
-
msg[:content],
|
|
27
|
-
id: msg[:id],
|
|
28
|
-
tool_calls: tool_calls,
|
|
29
|
-
structured_output: msg[:structured_output],
|
|
30
|
-
finish_reason: msg[:finish_reason]&.to_sym,
|
|
31
|
-
finish_reason_raw: msg[:finish_reason_raw],
|
|
32
|
-
)
|
|
33
|
-
when :system
|
|
34
|
-
Riffer::Messages::System.new(msg[:content], id: msg[:id])
|
|
35
|
-
when :tool
|
|
36
|
-
Riffer::Messages::Tool.new(msg[:content], id: msg[:id], tool_call_id: msg[:tool_call_id], name: msg[:name])
|
|
37
|
-
else
|
|
38
|
-
raise Riffer::ArgumentError, "Unknown message role: #{msg[:role]}"
|
|
15
|
+
when :user then Riffer::Messages::User.from_hash(msg)
|
|
16
|
+
when :assistant then Riffer::Messages::Assistant.from_hash(msg)
|
|
17
|
+
when :system then Riffer::Messages::System.from_hash(msg)
|
|
18
|
+
when :tool then Riffer::Messages::Tool.from_hash(msg)
|
|
19
|
+
else raise Riffer::ArgumentError, "Unknown message role: #{msg[:role]}"
|
|
39
20
|
end
|
|
40
21
|
end
|
|
41
22
|
|
|
42
|
-
# The message content.
|
|
43
23
|
attr_reader :content #: String # @dynamic content
|
|
44
24
|
|
|
45
|
-
# The message id, or nil when +Riffer.config.message_id_strategy+ is +:none+.
|
|
46
25
|
attr_reader :id #: String? # @dynamic id
|
|
47
26
|
|
|
48
27
|
#--
|
|
@@ -52,8 +31,6 @@ class Riffer::Messages::Base
|
|
|
52
31
|
@id = id || generate_id
|
|
53
32
|
end
|
|
54
33
|
|
|
55
|
-
# Converts the message to a hash.
|
|
56
|
-
#
|
|
57
34
|
#--
|
|
58
35
|
#: () -> Hash[Symbol, untyped]
|
|
59
36
|
def to_h
|
|
@@ -62,23 +39,18 @@ class Riffer::Messages::Base
|
|
|
62
39
|
hash
|
|
63
40
|
end
|
|
64
41
|
|
|
65
|
-
# Returns the message role.
|
|
66
42
|
#--
|
|
67
43
|
#: () -> Symbol
|
|
68
44
|
def role
|
|
69
45
|
raise NotImplementedError, "Subclasses must implement #role"
|
|
70
46
|
end
|
|
71
47
|
|
|
72
|
-
# Whether this message carries pending tool calls (overridden by
|
|
73
|
-
# +Riffer::Messages::Assistant+).
|
|
74
48
|
#--
|
|
75
49
|
#: () -> bool
|
|
76
50
|
def has_tool_calls?
|
|
77
51
|
false
|
|
78
52
|
end
|
|
79
53
|
|
|
80
|
-
# Merges another same-role message into this one. +Tool+ messages are never
|
|
81
|
-
# merged.
|
|
82
54
|
#--
|
|
83
55
|
#: (untyped) -> Riffer::Messages::Base
|
|
84
56
|
def +(other)
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents a system message (instructions) in a conversation.
|
|
5
4
|
class Riffer::Messages::System < Riffer::Messages::Base
|
|
5
|
+
#--
|
|
6
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::System)) -> Riffer::Messages::System
|
|
7
|
+
def self.from_hash(msg)
|
|
8
|
+
return msg if msg.is_a?(Riffer::Messages::System)
|
|
9
|
+
|
|
10
|
+
new(msg[:content], id: msg[:id])
|
|
11
|
+
end
|
|
12
|
+
|
|
6
13
|
#--
|
|
7
14
|
#: () -> Symbol
|
|
8
15
|
def role
|
data/lib/riffer/messages/tool.rb
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents a tool execution result in a conversation.
|
|
5
4
|
class Riffer::Messages::Tool < Riffer::Messages::Base
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
#--
|
|
6
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::Tool)) -> Riffer::Messages::Tool
|
|
7
|
+
def self.from_hash(msg)
|
|
8
|
+
return msg if msg.is_a?(Riffer::Messages::Tool)
|
|
9
|
+
|
|
10
|
+
new(
|
|
11
|
+
msg[:content],
|
|
12
|
+
id: msg[:id],
|
|
13
|
+
tool_call_id: msg[:tool_call_id],
|
|
14
|
+
name: msg[:name],
|
|
15
|
+
error: msg[:error],
|
|
16
|
+
error_type: msg[:error_type]&.to_sym,
|
|
17
|
+
)
|
|
18
|
+
end
|
|
8
19
|
|
|
9
|
-
|
|
20
|
+
attr_reader :tool_call_id #: String # @dynamic tool_call_id
|
|
10
21
|
attr_reader :name #: String # @dynamic name
|
|
11
|
-
|
|
12
|
-
# The error message if the tool execution failed.
|
|
13
22
|
attr_reader :error #: String? # @dynamic error
|
|
14
|
-
|
|
15
|
-
# The type of error (:unknown_tool, :validation_error, :execution_error,
|
|
16
|
-
# :timeout_error, :unhandled_error).
|
|
17
23
|
attr_reader :error_type #: Symbol? # @dynamic error_type
|
|
18
24
|
|
|
19
25
|
#--
|
|
@@ -26,8 +32,6 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
|
|
|
26
32
|
@error_type = error_type
|
|
27
33
|
end
|
|
28
34
|
|
|
29
|
-
# Returns true if the tool execution resulted in an error.
|
|
30
|
-
#
|
|
31
35
|
#--
|
|
32
36
|
#: () -> bool
|
|
33
37
|
def error?
|
|
@@ -40,8 +44,6 @@ class Riffer::Messages::Tool < Riffer::Messages::Base
|
|
|
40
44
|
:tool
|
|
41
45
|
end
|
|
42
46
|
|
|
43
|
-
# Converts the message to a hash.
|
|
44
|
-
#
|
|
45
47
|
#--
|
|
46
48
|
#: () -> Hash[Symbol, untyped]
|
|
47
49
|
def to_h
|