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
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
require "base64"
|
|
5
5
|
require "uri"
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
# raw base64 data (+new+).
|
|
9
|
-
class Riffer::Messages::FilePart
|
|
7
|
+
class Riffer::Messages::User::FilePart
|
|
10
8
|
# @rbs @url_string: String?
|
|
11
9
|
# @rbs @data: String?
|
|
12
10
|
# @rbs @downloaded_data: String?
|
|
@@ -28,17 +26,12 @@ class Riffer::Messages::FilePart
|
|
|
28
26
|
SUPPORTED_MEDIA_TYPES = MEDIA_TYPES.values.uniq.freeze #: Array[String]
|
|
29
27
|
SHA256_PATTERN = /\A[0-9a-f]{64}\z/i #: Regexp
|
|
30
28
|
|
|
31
|
-
# The MIME type of the file.
|
|
32
29
|
attr_reader :media_type #: String # @dynamic media_type
|
|
33
30
|
|
|
34
|
-
# The filename, if available.
|
|
35
31
|
attr_reader :filename #: String? # @dynamic filename
|
|
36
32
|
|
|
37
|
-
# The expected SHA-256 of the file contents, if the caller supplied one.
|
|
38
33
|
attr_reader :sha256 #: String? # @dynamic sha256
|
|
39
34
|
|
|
40
|
-
# Raises Riffer::ArgumentError unless +data+ or +url+ is given and
|
|
41
|
-
# +media_type+ is supported.
|
|
42
35
|
#--
|
|
43
36
|
#: (media_type: String, ?data: String?, ?filename: String?, ?url: String?, ?sha256: String?) -> void
|
|
44
37
|
def initialize(media_type:, data: nil, filename: nil, url: nil, sha256: nil)
|
|
@@ -59,26 +52,16 @@ class Riffer::Messages::FilePart
|
|
|
59
52
|
@url_string = url
|
|
60
53
|
end
|
|
61
54
|
|
|
62
|
-
# Creates a FilePart from a URL, detecting +media_type+ from the path
|
|
63
|
-
# extension when omitted. Raises Riffer::ArgumentError if it can't be detected.
|
|
64
55
|
#--
|
|
65
|
-
#: (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::FilePart
|
|
56
|
+
#: (String, ?media_type: String?, ?filename: String?, ?sha256: String?) -> Riffer::Messages::User::FilePart
|
|
66
57
|
def self.from_url(url, media_type: nil, filename: nil, sha256: nil)
|
|
67
|
-
|
|
68
|
-
ext = ::File.extname(URI.parse(url).path.to_s).downcase
|
|
69
|
-
media_type = MEDIA_TYPES.fetch(ext) { raise Riffer::ArgumentError, "Cannot detect media type from URL; provide media_type explicitly" }
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
new(url: url, media_type: media_type, filename: filename, sha256: sha256)
|
|
58
|
+
new(url: url, media_type: media_type || detect_media_type(url), filename: filename, sha256: sha256)
|
|
73
59
|
end
|
|
74
60
|
|
|
75
|
-
# Builds a FilePart from a +{url:, media_type:}+ or +{data:, media_type:}+ hash,
|
|
76
|
-
# or returns +file+ unchanged when it is already a FilePart. Raises
|
|
77
|
-
# Riffer::ArgumentError on an invalid hash.
|
|
78
61
|
#--
|
|
79
|
-
#: ((Hash[Symbol, untyped] | Riffer::Messages::FilePart)) -> Riffer::Messages::FilePart
|
|
62
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::User::FilePart)) -> Riffer::Messages::User::FilePart
|
|
80
63
|
def self.from_hash(file)
|
|
81
|
-
return file if file.is_a?(Riffer::Messages::FilePart)
|
|
64
|
+
return file if file.is_a?(Riffer::Messages::User::FilePart)
|
|
82
65
|
|
|
83
66
|
url = file[:url]
|
|
84
67
|
data = file[:data]
|
|
@@ -87,7 +70,7 @@ class Riffer::Messages::FilePart
|
|
|
87
70
|
sha256 = file[:sha256]
|
|
88
71
|
|
|
89
72
|
if url
|
|
90
|
-
|
|
73
|
+
new(url: url, data: data, media_type: media_type || detect_media_type(url), filename: filename, sha256: sha256)
|
|
91
74
|
elsif data && media_type
|
|
92
75
|
new(data: data, media_type: media_type, filename: filename, sha256: sha256)
|
|
93
76
|
else
|
|
@@ -95,8 +78,14 @@ class Riffer::Messages::FilePart
|
|
|
95
78
|
end
|
|
96
79
|
end
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
|
|
81
|
+
#--
|
|
82
|
+
#: (String) -> String
|
|
83
|
+
def self.detect_media_type(url)
|
|
84
|
+
ext = ::File.extname(URI.parse(url).path.to_s).downcase
|
|
85
|
+
MEDIA_TYPES.fetch(ext) { raise Riffer::ArgumentError, "Cannot detect media type from URL; provide media_type explicitly" }
|
|
86
|
+
end
|
|
87
|
+
private_class_method :detect_media_type
|
|
88
|
+
|
|
100
89
|
#--
|
|
101
90
|
#: () -> String?
|
|
102
91
|
def data
|
|
@@ -120,60 +109,47 @@ class Riffer::Messages::FilePart
|
|
|
120
109
|
@data_bytes = bytes
|
|
121
110
|
end
|
|
122
111
|
|
|
123
|
-
# Whether data was supplied directly, as opposed to filled in later by the
|
|
124
|
-
# file resolver after a download
|
|
125
112
|
#: () -> bool
|
|
126
113
|
def inline_data?
|
|
127
114
|
!@data.nil?
|
|
128
115
|
end
|
|
129
116
|
|
|
130
|
-
# Caches bytes fetched for a URL source. Deliberately absent from +to_h+:
|
|
131
|
-
# the agent loop re-sends history on every turn, so the cache saves refreshing
|
|
132
|
-
# the same file, while persisted history stays free of megabytes of base64
|
|
133
117
|
#--
|
|
134
118
|
#: (String) -> void
|
|
135
119
|
def cache_downloaded_data(data)
|
|
136
120
|
@downloaded_data = data
|
|
137
121
|
end
|
|
138
122
|
|
|
139
|
-
# Returns the URL if the source was a URL, nil otherwise.
|
|
140
|
-
#
|
|
141
123
|
#--
|
|
142
124
|
#: () -> String?
|
|
143
125
|
def url
|
|
144
126
|
@url_string
|
|
145
127
|
end
|
|
146
128
|
|
|
147
|
-
# Returns true if the source was a URL.
|
|
148
|
-
#
|
|
149
129
|
#--
|
|
150
130
|
#: () -> bool
|
|
151
131
|
def url?
|
|
152
132
|
!@url_string.nil?
|
|
153
133
|
end
|
|
154
134
|
|
|
155
|
-
# Returns true if the file is an image.
|
|
156
|
-
#
|
|
157
135
|
#--
|
|
158
136
|
#: () -> bool
|
|
159
137
|
def image?
|
|
160
138
|
media_type.start_with?("image/")
|
|
161
139
|
end
|
|
162
140
|
|
|
163
|
-
# Returns true if the file is a document (not an image).
|
|
164
|
-
#
|
|
165
141
|
#--
|
|
166
142
|
#: () -> bool
|
|
167
143
|
def document?
|
|
168
144
|
!image?
|
|
169
145
|
end
|
|
170
146
|
|
|
171
|
-
# Serializes the FilePart to a hash.
|
|
172
|
-
#
|
|
173
147
|
#--
|
|
174
148
|
#: () -> Hash[Symbol, untyped]
|
|
175
149
|
def to_h
|
|
176
150
|
hash = { media_type: media_type } #: Hash[Symbol, untyped]
|
|
151
|
+
# Downloaded data is left out so persisted history stays free of megabytes
|
|
152
|
+
# of base64; the in-memory cache still spares refetching on every turn.
|
|
177
153
|
hash[:data] = @data if @data
|
|
178
154
|
hash[:url] = @url_string if @url_string
|
|
179
155
|
hash[:filename] = filename if filename
|
data/lib/riffer/messages/user.rb
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# Represents a user message in a conversation.
|
|
5
4
|
class Riffer::Messages::User < Riffer::Messages::Base
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
#--
|
|
6
|
+
#: ((Hash[Symbol, untyped] | Riffer::Messages::User)) -> Riffer::Messages::User
|
|
7
|
+
def self.from_hash(msg)
|
|
8
|
+
return msg if msg.is_a?(Riffer::Messages::User)
|
|
9
|
+
|
|
10
|
+
files = (msg[:files] || []).map { |f| Riffer::Messages::User::FilePart.from_hash(f) }
|
|
11
|
+
new(msg[:content], id: msg[:id], files: files)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :files #: Array[Riffer::Messages::User::FilePart] # @dynamic files
|
|
8
15
|
|
|
9
16
|
#--
|
|
10
|
-
#: (String, ?id: String?, ?files: Array[Riffer::Messages::FilePart]) -> void
|
|
17
|
+
#: (String, ?id: String?, ?files: Array[Riffer::Messages::User::FilePart]) -> void
|
|
11
18
|
def initialize(content, id: nil, files: [])
|
|
12
19
|
super(content, id: id)
|
|
13
20
|
@files = files
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
# (+true+/+false+ are +TrueClass+/+FalseClass+).
|
|
6
|
-
#
|
|
7
|
-
# required :verbose, Riffer::Params::Boolean
|
|
8
|
-
#
|
|
4
|
+
# Ruby has no +Boolean+ class, so params declare booleans with this sentinel.
|
|
9
5
|
module Riffer::Params::Boolean
|
|
10
6
|
end
|
data/lib/riffer/params/param.rb
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# A single parameter definition, handling type validation and JSON Schema
|
|
5
|
-
# generation.
|
|
6
4
|
class Riffer::Params::Param
|
|
7
|
-
# Maps Ruby types to JSON Schema type strings
|
|
8
5
|
TYPE_MAPPINGS = {
|
|
9
6
|
String => "string",
|
|
10
7
|
Integer => "integer",
|
|
@@ -16,11 +13,8 @@ class Riffer::Params::Param
|
|
|
16
13
|
Hash => "object",
|
|
17
14
|
}.freeze #: Hash[Module, String]
|
|
18
15
|
|
|
19
|
-
# Primitive types allowed for the <tt>of:</tt> keyword on Array params
|
|
20
16
|
PRIMITIVE_TYPES = (TYPE_MAPPINGS.keys - [Array, Hash]).freeze #: Array[Module]
|
|
21
17
|
|
|
22
|
-
# Maps JSON Schema type strings back to Ruby types (inverse of TYPE_MAPPINGS),
|
|
23
|
-
# collapsing the three boolean spellings onto Riffer::Params::Boolean.
|
|
24
18
|
JSON_TYPE_MAPPINGS = {
|
|
25
19
|
"string" => String,
|
|
26
20
|
"integer" => Integer,
|
|
@@ -30,32 +24,22 @@ class Riffer::Params::Param
|
|
|
30
24
|
"object" => Hash,
|
|
31
25
|
}.freeze #: Hash[String, Module]
|
|
32
26
|
|
|
33
|
-
# The parameter name.
|
|
34
27
|
attr_reader :name #: Symbol # @dynamic name
|
|
35
28
|
|
|
36
|
-
# The Ruby type.
|
|
37
29
|
attr_reader :type #: Module # @dynamic type
|
|
38
30
|
|
|
39
|
-
# Whether the parameter is required.
|
|
40
31
|
attr_reader :required #: bool # @dynamic required
|
|
41
32
|
|
|
42
|
-
# The parameter description, if any.
|
|
43
33
|
attr_reader :description #: String? # @dynamic description
|
|
44
34
|
|
|
45
|
-
# Allowed values, if constrained.
|
|
46
35
|
attr_reader :enum #: Array[untyped]? # @dynamic enum
|
|
47
36
|
|
|
48
|
-
# The default value, if any.
|
|
49
37
|
attr_reader :default #: untyped # @dynamic default
|
|
50
38
|
|
|
51
|
-
# Element type for a typed array (+of:+).
|
|
52
39
|
attr_reader :item_type #: Module? # @dynamic item_type
|
|
53
40
|
|
|
54
|
-
# Nested Params for object / array-of-object types.
|
|
55
41
|
attr_reader :nested_params #: Riffer::Params? # @dynamic nested_params
|
|
56
42
|
|
|
57
|
-
# Reconstructs a Param from a single JSON Schema property. Raises
|
|
58
|
-
# Riffer::ArgumentError on a type outside the Params-expressible subset.
|
|
59
43
|
#--
|
|
60
44
|
#: (Symbol, Hash[Symbol, untyped], required: bool) -> Riffer::Params::Param
|
|
61
45
|
def self.from_json_schema(name, schema, required:)
|
|
@@ -88,9 +72,6 @@ class Riffer::Params::Param
|
|
|
88
72
|
end
|
|
89
73
|
private_class_method :resolve_nesting
|
|
90
74
|
|
|
91
|
-
# Resolves a JSON Schema +type+ (or a <tt>[type, "null"]</tt> union) to its
|
|
92
|
-
# Ruby type. Returns a Module — Riffer::Params::Boolean is a Module, not a
|
|
93
|
-
# Class. Raises Riffer::ArgumentError on an unsupported type.
|
|
94
75
|
#--
|
|
95
76
|
#: (untyped) -> Module
|
|
96
77
|
def self.json_type_to_ruby(type)
|
|
@@ -112,8 +93,6 @@ class Riffer::Params::Param
|
|
|
112
93
|
@nested_params = nested_params
|
|
113
94
|
end
|
|
114
95
|
|
|
115
|
-
# Validates that a value matches the expected type.
|
|
116
|
-
#
|
|
117
96
|
#--
|
|
118
97
|
#: (untyped) -> bool
|
|
119
98
|
def valid_type?(value)
|
|
@@ -128,28 +107,20 @@ class Riffer::Params::Param
|
|
|
128
107
|
end
|
|
129
108
|
end
|
|
130
109
|
|
|
131
|
-
# Returns the JSON Schema type name for this parameter.
|
|
132
|
-
#
|
|
133
110
|
#--
|
|
134
111
|
#: () -> String
|
|
135
112
|
def type_name
|
|
136
113
|
TYPE_MAPPINGS[type] || type.to_s.downcase
|
|
137
114
|
end
|
|
138
115
|
|
|
139
|
-
# Converts this parameter to JSON Schema format. When +strict+, optional
|
|
140
|
-
# params are made nullable (<tt>["type", "null"]</tt>) so strict providers
|
|
141
|
-
# distinguish absent from present; optional params with an +enum+ use +anyOf+
|
|
142
|
-
# instead, since providers like Anthropic reject
|
|
143
|
-
# <tt>{"type": ["string", "null"], "enum": [...]}</tt>. Raises
|
|
144
|
-
# Riffer::ArgumentError when +strict+ and a Hash param has no block or an
|
|
145
|
-
# Array param has neither a block nor <tt>of:</tt>, since strict providers
|
|
146
|
-
# reject objects without +properties+ and arrays without +items+.
|
|
147
116
|
#--
|
|
148
117
|
#: (?strict: bool) -> Hash[Symbol, untyped]
|
|
149
118
|
def to_json_schema(strict: false)
|
|
150
119
|
validate_strict_shape! if strict
|
|
120
|
+
# Nullable so strict providers can distinguish absent from present.
|
|
151
121
|
nullable = strict && !required
|
|
152
122
|
|
|
123
|
+
# Providers like Anthropic reject a nullable type union combined with enum.
|
|
153
124
|
if nullable && enum
|
|
154
125
|
schema = { anyOf: [{ type: type_name, enum: enum }, { type: "null" }] } #: Hash[Symbol, untyped]
|
|
155
126
|
schema[:description] = description if description
|
|
@@ -181,9 +152,22 @@ class Riffer::Params::Param
|
|
|
181
152
|
|
|
182
153
|
private
|
|
183
154
|
|
|
155
|
+
# +dup+ would leave the copy sharing this one's nested Params, enum list and
|
|
156
|
+
# default, so defining a parameter or editing either value on one would reach
|
|
157
|
+
# the other.
|
|
158
|
+
#--
|
|
159
|
+
#: (Riffer::Params::Param) -> void
|
|
160
|
+
def initialize_copy(source)
|
|
161
|
+
super
|
|
162
|
+
@nested_params = source.nested_params&.dup
|
|
163
|
+
@enum = Riffer::Helpers::DeepDup.call(source.enum)
|
|
164
|
+
@default = Riffer::Helpers::DeepDup.call(source.default)
|
|
165
|
+
end
|
|
166
|
+
|
|
184
167
|
#--
|
|
185
168
|
#: () -> void
|
|
186
169
|
def validate_strict_shape!
|
|
170
|
+
# Strict providers reject objects without properties and arrays without items.
|
|
187
171
|
if type == Hash && nested_params.nil?
|
|
188
172
|
raise Riffer::ArgumentError,
|
|
189
173
|
"#{name}: a Hash param requires a block defining its properties under strict schemas"
|
data/lib/riffer/params.rb
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
|
-
# A DSL for defining tool parameters and structured-output schemas, used within
|
|
5
|
-
# a Tool's +params+ block.
|
|
6
|
-
#
|
|
7
|
-
# params do
|
|
8
|
-
# required :city, String, description: "The city name"
|
|
9
|
-
# optional :units, String, default: "celsius", enum: ["celsius", "fahrenheit"]
|
|
10
|
-
# end
|
|
11
|
-
#
|
|
12
4
|
class Riffer::Params
|
|
13
|
-
# The defined parameters.
|
|
14
5
|
attr_reader :parameters #: Array[Riffer::Params::Param] # @dynamic parameters
|
|
15
6
|
|
|
16
7
|
#--
|
|
@@ -19,13 +10,8 @@ class Riffer::Params
|
|
|
19
10
|
@parameters = []
|
|
20
11
|
end
|
|
21
12
|
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# outside the Params-expressible subset of JSON Schema.
|
|
25
|
-
#
|
|
26
|
-
# schema = params.to_json_schema(strict: false)
|
|
27
|
-
# Riffer::Params.from_json_schema(schema) # => equivalent Riffer::Params
|
|
28
|
-
#
|
|
13
|
+
# The inverse of +to_json_schema(strict: false)+. Raises Riffer::ArgumentError
|
|
14
|
+
# on features outside the Params-expressible subset of JSON Schema.
|
|
29
15
|
#--
|
|
30
16
|
#: (Hash[Symbol, untyped]) -> Riffer::Params
|
|
31
17
|
def self.from_json_schema(schema)
|
|
@@ -42,8 +28,6 @@ class Riffer::Params
|
|
|
42
28
|
params
|
|
43
29
|
end
|
|
44
30
|
|
|
45
|
-
# Defines a required parameter.
|
|
46
|
-
#
|
|
47
31
|
#--
|
|
48
32
|
#: (Symbol, Module, ?description: String?, ?enum: Array[untyped]?, ?of: Module?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> void
|
|
49
33
|
def required(name, type, description: nil, enum: nil, of: nil, &)
|
|
@@ -59,8 +43,6 @@ class Riffer::Params
|
|
|
59
43
|
)
|
|
60
44
|
end
|
|
61
45
|
|
|
62
|
-
# Defines an optional parameter.
|
|
63
|
-
#
|
|
64
46
|
#--
|
|
65
47
|
#: (Symbol, Module, ?description: String?, ?enum: Array[untyped]?, ?default: untyped, ?of: Module?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> void
|
|
66
48
|
def optional(name, type, description: nil, enum: nil, default: nil, of: nil, &)
|
|
@@ -77,15 +59,6 @@ class Riffer::Params
|
|
|
77
59
|
)
|
|
78
60
|
end
|
|
79
61
|
|
|
80
|
-
# Validates arguments against parameter definitions.
|
|
81
|
-
#
|
|
82
|
-
# A Float param accepts an Integer (JSON Schema <tt>"number"</tt> covers
|
|
83
|
-
# integers) and its value is coerced with +to_f+, so callers always get a
|
|
84
|
-
# Float. The same holds for the items of an <tt>of: Float</tt> array. No other
|
|
85
|
-
# type is coerced.
|
|
86
|
-
#
|
|
87
|
-
# Raises Riffer::ValidationError if validation fails.
|
|
88
|
-
#
|
|
89
62
|
#--
|
|
90
63
|
#: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
|
|
91
64
|
def validate(arguments)
|
|
@@ -125,11 +98,6 @@ class Riffer::Params
|
|
|
125
98
|
validated
|
|
126
99
|
end
|
|
127
100
|
|
|
128
|
-
# Converts all parameters to JSON Schema format. When +strict+ is true, every
|
|
129
|
-
# property is listed in +required+ and optional ones are made nullable
|
|
130
|
-
# instead, satisfying providers that enforce strict structured output schemas.
|
|
131
|
-
# Raises Riffer::ArgumentError when +strict+ and a Hash param (at any depth)
|
|
132
|
-
# has no block or an Array param has neither a block nor <tt>of:</tt>.
|
|
133
101
|
#--
|
|
134
102
|
#: (?strict: bool) -> Hash[Symbol, untyped]
|
|
135
103
|
def to_json_schema(strict: false)
|
|
@@ -138,6 +106,8 @@ class Riffer::Params
|
|
|
138
106
|
|
|
139
107
|
@parameters.each do |param|
|
|
140
108
|
properties[param.name.to_s] = param.to_json_schema(strict: strict)
|
|
109
|
+
# Providers enforcing strict structured output require every property in
|
|
110
|
+
# +required+; strict mode makes optional ones nullable instead.
|
|
141
111
|
required_params << param.name.to_s if strict || param.required
|
|
142
112
|
end
|
|
143
113
|
|
|
@@ -151,6 +121,15 @@ class Riffer::Params
|
|
|
151
121
|
|
|
152
122
|
private
|
|
153
123
|
|
|
124
|
+
# Without this, +dup+ shares parameters, so defining one on either copy
|
|
125
|
+
# would reach the other.
|
|
126
|
+
#--
|
|
127
|
+
#: (Riffer::Params) -> void
|
|
128
|
+
def initialize_copy(source)
|
|
129
|
+
super
|
|
130
|
+
@parameters = source.parameters.map(&:dup)
|
|
131
|
+
end
|
|
132
|
+
|
|
154
133
|
#--
|
|
155
134
|
#: (Module, Module?) ?{ (Riffer::Params) [self: Riffer::Params] -> void } -> Riffer::Params?
|
|
156
135
|
def build_nested(type, of, &block)
|
|
@@ -224,7 +203,6 @@ class Riffer::Params
|
|
|
224
203
|
end
|
|
225
204
|
end
|
|
226
205
|
|
|
227
|
-
# Returns the array with its valid items coerced by +coerce_value+.
|
|
228
206
|
#--
|
|
229
207
|
#: (Riffer::Params::Param, Array[untyped], Array[String]) -> Array[untyped]
|
|
230
208
|
def validate_typed_array(param, value, errors)
|
|
@@ -249,13 +227,11 @@ class Riffer::Params
|
|
|
249
227
|
end
|
|
250
228
|
end
|
|
251
229
|
|
|
252
|
-
# Coerces an already-validated value to the Ruby type its param declares.
|
|
253
|
-
# Only Float coerces today, because JSON Schema "number" accepts integers and
|
|
254
|
-
# callers should not get a type that depends on whether the model wrote a
|
|
255
|
-
# decimal point. Add a branch here rather than inline at a call site.
|
|
256
230
|
#--
|
|
257
231
|
#: (Module, untyped) -> untyped
|
|
258
232
|
def coerce_value(type, value)
|
|
233
|
+
# JSON Schema "number" accepts integers; callers shouldn't get a type that
|
|
234
|
+
# depends on whether the model wrote a decimal point.
|
|
259
235
|
return value.to_f if type == Float
|
|
260
236
|
|
|
261
237
|
value
|