ruby_llm 1.6.2 → 1.6.3
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/README.md +73 -91
- data/lib/ruby_llm/active_record/acts_as.rb +2 -10
- data/lib/ruby_llm/aliases.json +4 -0
- data/lib/ruby_llm/aliases.rb +7 -25
- data/lib/ruby_llm/chat.rb +2 -10
- data/lib/ruby_llm/configuration.rb +1 -12
- data/lib/ruby_llm/content.rb +0 -2
- data/lib/ruby_llm/embedding.rb +1 -2
- data/lib/ruby_llm/error.rb +0 -8
- data/lib/ruby_llm/image.rb +0 -4
- data/lib/ruby_llm/message.rb +2 -4
- data/lib/ruby_llm/model/info.rb +0 -10
- data/lib/ruby_llm/model/pricing.rb +0 -3
- data/lib/ruby_llm/model/pricing_category.rb +0 -2
- data/lib/ruby_llm/model/pricing_tier.rb +0 -1
- data/lib/ruby_llm/models.json +623 -452
- data/lib/ruby_llm/models.rb +5 -13
- data/lib/ruby_llm/provider.rb +1 -5
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -46
- data/lib/ruby_llm/providers/anthropic/media.rb +0 -1
- data/lib/ruby_llm/providers/anthropic/tools.rb +0 -1
- data/lib/ruby_llm/providers/anthropic.rb +1 -2
- data/lib/ruby_llm/providers/bedrock/chat.rb +0 -2
- data/lib/ruby_llm/providers/bedrock/media.rb +0 -1
- data/lib/ruby_llm/providers/bedrock/models.rb +0 -2
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -7
- data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -13
- data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -18
- data/lib/ruby_llm/providers/bedrock.rb +1 -2
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -2
- data/lib/ruby_llm/providers/deepseek/chat.rb +0 -1
- data/lib/ruby_llm/providers/gemini/capabilities.rb +26 -101
- data/lib/ruby_llm/providers/gemini/chat.rb +5 -7
- data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -2
- data/lib/ruby_llm/providers/gemini/images.rb +0 -1
- data/lib/ruby_llm/providers/gemini/media.rb +0 -1
- data/lib/ruby_llm/providers/gemini/models.rb +1 -2
- data/lib/ruby_llm/providers/gemini/tools.rb +0 -5
- data/lib/ruby_llm/providers/gpustack/chat.rb +0 -1
- data/lib/ruby_llm/providers/gpustack/models.rb +3 -4
- data/lib/ruby_llm/providers/mistral/capabilities.rb +2 -10
- data/lib/ruby_llm/providers/mistral/chat.rb +0 -2
- data/lib/ruby_llm/providers/mistral/embeddings.rb +0 -3
- data/lib/ruby_llm/providers/mistral/models.rb +0 -1
- data/lib/ruby_llm/providers/ollama/chat.rb +0 -1
- data/lib/ruby_llm/providers/ollama/media.rb +0 -1
- data/lib/ruby_llm/providers/openai/capabilities.rb +0 -15
- data/lib/ruby_llm/providers/openai/chat.rb +0 -3
- data/lib/ruby_llm/providers/openai/embeddings.rb +0 -3
- data/lib/ruby_llm/providers/openai/media.rb +0 -1
- data/lib/ruby_llm/providers/openai.rb +1 -3
- data/lib/ruby_llm/providers/openrouter/models.rb +1 -16
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -1
- data/lib/ruby_llm/providers/perplexity/chat.rb +0 -1
- data/lib/ruby_llm/providers/perplexity.rb +1 -5
- data/lib/ruby_llm/railtie.rb +0 -1
- data/lib/ruby_llm/stream_accumulator.rb +1 -3
- data/lib/ruby_llm/streaming.rb +15 -24
- data/lib/ruby_llm/tool.rb +2 -19
- data/lib/ruby_llm/tool_call.rb +0 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +0 -2
- data/lib/tasks/aliases.rake +5 -35
- data/lib/tasks/models_docs.rake +1 -11
- data/lib/tasks/models_update.rake +1 -1
- data/lib/tasks/vcr.rake +0 -7
- metadata +1 -1
data/lib/ruby_llm/models.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RubyLLM
|
4
|
-
# Registry of available AI models and their capabilities.
|
5
|
-
# to discover and work with models from different providers.
|
6
|
-
#
|
7
|
-
# Example:
|
8
|
-
# RubyLLM.models.all # All available models
|
9
|
-
# RubyLLM.models.chat_models # Models that support chat
|
10
|
-
# RubyLLM.models.by_provider('openai').chat_models # OpenAI chat models
|
11
|
-
# RubyLLM.models.find('claude-3') # Get info about a specific model
|
4
|
+
# Registry of available AI models and their capabilities.
|
12
5
|
class Models
|
13
6
|
include Enumerable
|
14
7
|
|
@@ -25,14 +18,14 @@ module RubyLLM
|
|
25
18
|
File.expand_path('models.json', __dir__)
|
26
19
|
end
|
27
20
|
|
21
|
+
def schema_file
|
22
|
+
File.expand_path('models_schema.json', __dir__)
|
23
|
+
end
|
24
|
+
|
28
25
|
def refresh!
|
29
|
-
# Collect models from both sources
|
30
26
|
provider_models = fetch_from_providers
|
31
27
|
parsera_models = fetch_from_parsera
|
32
|
-
|
33
|
-
# Merge with parsera data taking precedence
|
34
28
|
merged_models = merge_models(provider_models, parsera_models)
|
35
|
-
|
36
29
|
@instance = new(merged_models)
|
37
30
|
end
|
38
31
|
|
@@ -50,7 +43,6 @@ module RubyLLM
|
|
50
43
|
config ||= RubyLLM.config
|
51
44
|
provider_class = provider ? Provider.providers[provider.to_sym] : nil
|
52
45
|
|
53
|
-
# Check if provider is local
|
54
46
|
if provider_class
|
55
47
|
temp_instance = provider_class.new(config)
|
56
48
|
assume_exists = true if temp_instance.local?
|
data/lib/ruby_llm/provider.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RubyLLM
|
4
|
-
# Base class for LLM providers
|
5
|
-
# Handles the complexities of API communication, streaming responses,
|
6
|
-
# and error handling so individual providers can focus on their unique features.
|
7
|
-
# Encapsulates configuration and connection to eliminate parameter threading.
|
4
|
+
# Base class for LLM providers.
|
8
5
|
class Provider
|
9
6
|
include Streaming
|
10
7
|
|
@@ -210,7 +207,6 @@ module RubyLLM
|
|
210
207
|
|
211
208
|
def sync_response(connection, payload, additional_headers = {})
|
212
209
|
response = connection.post completion_url, payload do |req|
|
213
|
-
# Merge additional headers, with existing headers taking precedence
|
214
210
|
req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
|
215
211
|
end
|
216
212
|
parse_completion_response response
|
@@ -7,17 +7,10 @@ module RubyLLM
|
|
7
7
|
module Capabilities
|
8
8
|
module_function
|
9
9
|
|
10
|
-
# Determines the context window size for a given model
|
11
|
-
# @param model_id [String] the model identifier
|
12
|
-
# @return [Integer] the context window size in tokens
|
13
10
|
def determine_context_window(_model_id)
|
14
|
-
# All Claude 3 and 3.5 and 3.7 models have 200K token context windows
|
15
11
|
200_000
|
16
12
|
end
|
17
13
|
|
18
|
-
# Determines the maximum output tokens for a given model
|
19
|
-
# @param model_id [String] the model identifier
|
20
|
-
# @return [Integer] the maximum output tokens
|
21
14
|
def determine_max_tokens(model_id)
|
22
15
|
case model_id
|
23
16
|
when /claude-3-7-sonnet/, /claude-3-5/ then 8_192
|
@@ -25,52 +18,30 @@ module RubyLLM
|
|
25
18
|
end
|
26
19
|
end
|
27
20
|
|
28
|
-
# Gets the input price per million tokens for a given model
|
29
|
-
# @param model_id [String] the model identifier
|
30
|
-
# @return [Float] the price per million tokens for input
|
31
21
|
def get_input_price(model_id)
|
32
22
|
PRICES.dig(model_family(model_id), :input) || default_input_price
|
33
23
|
end
|
34
24
|
|
35
|
-
# Gets the output price per million tokens for a given model
|
36
|
-
# @param model_id [String] the model identifier
|
37
|
-
# @return [Float] the price per million tokens for output
|
38
25
|
def get_output_price(model_id)
|
39
26
|
PRICES.dig(model_family(model_id), :output) || default_output_price
|
40
27
|
end
|
41
28
|
|
42
|
-
# Determines if a model supports vision capabilities
|
43
|
-
# @param model_id [String] the model identifier
|
44
|
-
# @return [Boolean] true if the model supports vision
|
45
29
|
def supports_vision?(model_id)
|
46
|
-
# All Claude 3, 3.5, and 3.7 models support vision
|
47
30
|
!model_id.match?(/claude-[12]/)
|
48
31
|
end
|
49
32
|
|
50
|
-
# Determines if a model supports function calling
|
51
|
-
# @param model_id [String] the model identifier
|
52
|
-
# @return [Boolean] true if the model supports functions
|
53
33
|
def supports_functions?(model_id)
|
54
34
|
model_id.match?(/claude-3/)
|
55
35
|
end
|
56
36
|
|
57
|
-
# Determines if a model supports JSON mode
|
58
|
-
# @param model_id [String] the model identifier
|
59
|
-
# @return [Boolean] true if the model supports JSON mode
|
60
37
|
def supports_json_mode?(model_id)
|
61
38
|
model_id.match?(/claude-3/)
|
62
39
|
end
|
63
40
|
|
64
|
-
# Determines if a model supports extended thinking
|
65
|
-
# @param model_id [String] the model identifier
|
66
|
-
# @return [Boolean] true if the model supports extended thinking
|
67
41
|
def supports_extended_thinking?(model_id)
|
68
42
|
model_id.match?(/claude-3-7-sonnet/)
|
69
43
|
end
|
70
44
|
|
71
|
-
# Determines the model family for a given model ID
|
72
|
-
# @param model_id [String] the model identifier
|
73
|
-
# @return [Symbol] the model family identifier
|
74
45
|
def model_family(model_id)
|
75
46
|
case model_id
|
76
47
|
when /claude-3-7-sonnet/ then 'claude-3-7-sonnet'
|
@@ -83,14 +54,10 @@ module RubyLLM
|
|
83
54
|
end
|
84
55
|
end
|
85
56
|
|
86
|
-
# Returns the model type
|
87
|
-
# @param model_id [String] the model identifier (unused but kept for API consistency)
|
88
|
-
# @return [String] the model type, always 'chat' for Anthropic models
|
89
57
|
def model_type(_)
|
90
58
|
'chat'
|
91
59
|
end
|
92
60
|
|
93
|
-
# Pricing information for Anthropic models (per million tokens)
|
94
61
|
PRICES = {
|
95
62
|
'claude-3-7-sonnet': { input: 3.0, output: 15.0 },
|
96
63
|
'claude-3-5-sonnet': { input: 3.0, output: 15.0 },
|
@@ -100,14 +67,10 @@ module RubyLLM
|
|
100
67
|
'claude-2': { input: 3.0, output: 15.0 }
|
101
68
|
}.freeze
|
102
69
|
|
103
|
-
# Default input price if model not found in PRICES
|
104
|
-
# @return [Float] default price per million tokens for input
|
105
70
|
def default_input_price
|
106
71
|
3.0
|
107
72
|
end
|
108
73
|
|
109
|
-
# Default output price if model not found in PRICES
|
110
|
-
# @return [Float] default price per million tokens for output
|
111
74
|
def default_output_price
|
112
75
|
15.0
|
113
76
|
end
|
@@ -118,7 +81,6 @@ module RubyLLM
|
|
118
81
|
output: ['text']
|
119
82
|
}
|
120
83
|
|
121
|
-
# All Claude 3+ models support vision
|
122
84
|
unless model_id.match?(/claude-[12]/)
|
123
85
|
modalities[:input] << 'image'
|
124
86
|
modalities[:input] << 'pdf'
|
@@ -130,18 +92,13 @@ module RubyLLM
|
|
130
92
|
def capabilities_for(model_id)
|
131
93
|
capabilities = ['streaming']
|
132
94
|
|
133
|
-
# Function calling for Claude 3+
|
134
95
|
if model_id.match?(/claude-3/)
|
135
96
|
capabilities << 'function_calling'
|
136
97
|
capabilities << 'batch'
|
137
98
|
end
|
138
99
|
|
139
|
-
|
140
|
-
capabilities << 'reasoning' if model_id.match?(/claude-3-7/)
|
141
|
-
|
142
|
-
# Citations
|
100
|
+
capabilities << 'reasoning' if model_id.match?(/claude-3-7|-4/)
|
143
101
|
capabilities << 'citations' if model_id.match?(/claude-3\.5|claude-3-7/)
|
144
|
-
|
145
102
|
capabilities
|
146
103
|
end
|
147
104
|
|
@@ -154,13 +111,11 @@ module RubyLLM
|
|
154
111
|
output_per_million: prices[:output]
|
155
112
|
}
|
156
113
|
|
157
|
-
# Batch is typically half the price
|
158
114
|
batch_pricing = {
|
159
115
|
input_per_million: prices[:input] * 0.5,
|
160
116
|
output_per_million: prices[:output] * 0.5
|
161
117
|
}
|
162
118
|
|
163
|
-
# Add reasoning output pricing for 3.7 models
|
164
119
|
if model_id.match?(/claude-3-7/)
|
165
120
|
standard_pricing[:reasoning_output_per_million] = prices[:output] * 2.5
|
166
121
|
batch_pricing[:reasoning_output_per_million] = prices[:output] * 1.25
|
@@ -8,7 +8,6 @@ module RubyLLM
|
|
8
8
|
module_function
|
9
9
|
|
10
10
|
def format_content(content)
|
11
|
-
# Convert Hash/Array back to JSON string for API
|
12
11
|
return [format_text(content.to_json)] if content.is_a?(Hash) || content.is_a?(Array)
|
13
12
|
return [format_text(content)] unless content.is_a?(Content)
|
14
13
|
|
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
|
-
# Anthropic Claude API integration.
|
6
|
-
# Claude's unique message format and tool calling conventions.
|
5
|
+
# Anthropic Claude API integration.
|
7
6
|
class Anthropic < Provider
|
8
7
|
include Anthropic::Chat
|
9
8
|
include Anthropic::Embeddings
|
@@ -11,7 +11,6 @@ module RubyLLM
|
|
11
11
|
signature = sign_request("#{connection.connection.url_prefix}#{completion_url}", payload:)
|
12
12
|
response = connection.post completion_url, payload do |req|
|
13
13
|
req.headers.merge! build_headers(signature.headers, streaming: block_given?)
|
14
|
-
# Merge additional headers, with existing headers taking precedence
|
15
14
|
req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
|
16
15
|
end
|
17
16
|
Anthropic::Chat.parse_completion_response response
|
@@ -41,7 +40,6 @@ module RubyLLM
|
|
41
40
|
end
|
42
41
|
|
43
42
|
def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil) # rubocop:disable Lint/UnusedMethodArgument,Metrics/ParameterLists
|
44
|
-
# Hold model_id in instance variable for use in completion_url and stream_url
|
45
43
|
@model_id = model
|
46
44
|
|
47
45
|
system_messages, chat_messages = Anthropic::Chat.separate_messages(messages)
|
@@ -11,7 +11,6 @@ module RubyLLM
|
|
11
11
|
module_function
|
12
12
|
|
13
13
|
def format_content(content)
|
14
|
-
# Convert Hash/Array back to JSON string for API
|
15
14
|
return [Anthropic::Media.format_text(content.to_json)] if content.is_a?(Hash) || content.is_a?(Array)
|
16
15
|
return [Anthropic::Media.format_text(content)] unless content.is_a?(Content)
|
17
16
|
|
@@ -25,7 +25,6 @@ module RubyLLM
|
|
25
25
|
def parse_list_models_response(response, slug, capabilities)
|
26
26
|
models = Array(response.body['modelSummaries'])
|
27
27
|
|
28
|
-
# Filter to include only models we care about
|
29
28
|
models.select { |m| m['modelId'].include?('claude') }.map do |model_data|
|
30
29
|
model_id = model_data['modelId']
|
31
30
|
|
@@ -51,7 +50,6 @@ module RubyLLM
|
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
|
-
# Simple test-friendly method that only sets the ID
|
55
53
|
def create_model_info(model_data, slug, _capabilities)
|
56
54
|
model_id = model_data['modelId']
|
57
55
|
|
@@ -5,18 +5,6 @@ module RubyLLM
|
|
5
5
|
class Bedrock
|
6
6
|
module Streaming
|
7
7
|
# Base module for AWS Bedrock streaming functionality.
|
8
|
-
# Serves as the core module that includes all other streaming-related modules
|
9
|
-
# and provides fundamental streaming operations.
|
10
|
-
#
|
11
|
-
# Responsibilities:
|
12
|
-
# - Stream URL management
|
13
|
-
# - Stream handling and error processing
|
14
|
-
# - Coordinating the functionality of other streaming modules
|
15
|
-
#
|
16
|
-
# @example
|
17
|
-
# module MyStreamingImplementation
|
18
|
-
# include RubyLLM::Providers::Bedrock::Streaming::Base
|
19
|
-
# end
|
20
8
|
module Base
|
21
9
|
def self.included(base)
|
22
10
|
base.include ContentExtraction
|
@@ -5,13 +5,6 @@ module RubyLLM
|
|
5
5
|
class Bedrock
|
6
6
|
module Streaming
|
7
7
|
# Module for handling content extraction from AWS Bedrock streaming responses.
|
8
|
-
# Provides methods to extract and process various types of content from the response data.
|
9
|
-
#
|
10
|
-
# Responsibilities:
|
11
|
-
# - Extracting content from different response formats
|
12
|
-
# - Processing JSON deltas and content blocks
|
13
|
-
# - Extracting metadata (tokens, model IDs, tool calls)
|
14
|
-
# - Handling different content structures (arrays, blocks, completions)
|
15
8
|
module ContentExtraction
|
16
9
|
def json_delta?(data)
|
17
10
|
data['type'] == 'content_block_delta' && data.dig('delta', 'type') == 'input_json_delta'
|
@@ -5,18 +5,6 @@ module RubyLLM
|
|
5
5
|
class Bedrock
|
6
6
|
module Streaming
|
7
7
|
# Module for processing streaming messages from AWS Bedrock.
|
8
|
-
# Handles the core message processing logic, including validation and chunking.
|
9
|
-
#
|
10
|
-
# Responsibilities:
|
11
|
-
# - Processing incoming message chunks
|
12
|
-
# - Validating message structure and content
|
13
|
-
# - Managing message offsets and boundaries
|
14
|
-
# - Error handling during message processing
|
15
|
-
#
|
16
|
-
# @example Processing a message chunk
|
17
|
-
# offset = process_message(chunk, current_offset) do |processed_chunk|
|
18
|
-
# handle_processed_chunk(processed_chunk)
|
19
|
-
# end
|
20
8
|
module MessageProcessing
|
21
9
|
def process_chunk(chunk, &)
|
22
10
|
offset = 0
|
@@ -7,18 +7,6 @@ module RubyLLM
|
|
7
7
|
class Bedrock
|
8
8
|
module Streaming
|
9
9
|
# Module for processing payloads from AWS Bedrock streaming responses.
|
10
|
-
# Handles JSON payload extraction, decoding, and chunk creation.
|
11
|
-
#
|
12
|
-
# Responsibilities:
|
13
|
-
# - Extracting and validating JSON payloads
|
14
|
-
# - Decoding Base64-encoded response data
|
15
|
-
# - Creating response chunks from processed data
|
16
|
-
# - Error handling for JSON parsing and processing
|
17
|
-
#
|
18
|
-
# @example Processing a payload
|
19
|
-
# process_payload(raw_payload) do |chunk|
|
20
|
-
# yield_chunk_to_client(chunk)
|
21
|
-
# end
|
22
10
|
module PayloadProcessing
|
23
11
|
def process_payload(payload, &)
|
24
12
|
json_payload = extract_json_payload(payload)
|
@@ -5,19 +5,6 @@ module RubyLLM
|
|
5
5
|
class Bedrock
|
6
6
|
module Streaming
|
7
7
|
# Module for handling message preludes in AWS Bedrock streaming responses.
|
8
|
-
# Manages the parsing and validation of message headers and prelude data.
|
9
|
-
#
|
10
|
-
# Responsibilities:
|
11
|
-
# - Reading and validating message preludes
|
12
|
-
# - Calculating message positions and boundaries
|
13
|
-
# - Finding and validating prelude positions in chunks
|
14
|
-
# - Ensuring message integrity through length validation
|
15
|
-
#
|
16
|
-
# @example Reading a prelude
|
17
|
-
# if can_read_prelude?(chunk, offset)
|
18
|
-
# total_length, headers_length = read_prelude(chunk, offset)
|
19
|
-
# process_message_with_lengths(total_length, headers_length)
|
20
|
-
# end
|
21
8
|
module PreludeHandling
|
22
9
|
def can_read_prelude?(chunk, offset)
|
23
10
|
chunk.bytesize - offset >= 12
|
@@ -10,24 +10,6 @@ module RubyLLM
|
|
10
10
|
module Providers
|
11
11
|
class Bedrock
|
12
12
|
# Streaming implementation for the AWS Bedrock API.
|
13
|
-
# This module provides functionality for handling streaming responses from AWS Bedrock,
|
14
|
-
# including message processing, content extraction, and error handling.
|
15
|
-
#
|
16
|
-
# The implementation is split into several focused modules:
|
17
|
-
# - Base: Core streaming functionality and module coordination
|
18
|
-
# - ContentExtraction: Extracting content from response data
|
19
|
-
# - MessageProcessing: Processing streaming message chunks
|
20
|
-
# - PayloadProcessing: Handling JSON payloads and chunk creation
|
21
|
-
# - PreludeHandling: Managing message preludes and headers
|
22
|
-
#
|
23
|
-
# @example Using the streaming module
|
24
|
-
# class BedrockClient
|
25
|
-
# include RubyLLM::Providers::Bedrock::Streaming
|
26
|
-
#
|
27
|
-
# def stream_response(&block)
|
28
|
-
# handle_stream(&block)
|
29
|
-
# end
|
30
|
-
# end
|
31
13
|
module Streaming
|
32
14
|
include Base
|
33
15
|
end
|
@@ -10,7 +10,7 @@ module RubyLLM
|
|
10
10
|
def context_window_for(model_id)
|
11
11
|
case model_id
|
12
12
|
when /deepseek-(?:chat|reasoner)/ then 64_000
|
13
|
-
else 32_768
|
13
|
+
else 32_768
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -67,7 +67,6 @@ module RubyLLM
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
# Pricing information for DeepSeek models (USD per 1M tokens)
|
71
70
|
PRICES = {
|
72
71
|
chat: {
|
73
72
|
input_hit: 0.07,
|