dify_llm 1.9.2 → 1.14.1

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.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -8
  3. data/lib/generators/ruby_llm/agent/agent_generator.rb +36 -0
  4. data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -0
  5. data/lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt +0 -0
  6. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +110 -41
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +14 -15
  8. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -11
  9. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +2 -2
  10. data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +25 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +2 -2
  12. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +16 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +31 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +31 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt +9 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +27 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +14 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt +1 -0
  19. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +13 -0
  20. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +23 -0
  21. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +10 -0
  22. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +2 -0
  23. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -0
  24. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +14 -0
  25. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +13 -0
  26. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +21 -0
  27. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +17 -0
  28. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +40 -0
  29. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +27 -0
  30. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +2 -2
  31. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +2 -2
  32. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +19 -7
  33. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +1 -1
  34. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +5 -3
  35. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +9 -0
  36. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -1
  37. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +8 -0
  38. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +1 -1
  39. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +6 -0
  40. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +2 -0
  41. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -7
  42. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +9 -0
  43. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -7
  44. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +8 -0
  45. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +16 -0
  46. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +11 -12
  47. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +27 -17
  48. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -4
  49. data/lib/generators/ruby_llm/generator_helpers.rb +37 -17
  50. data/lib/generators/ruby_llm/install/install_generator.rb +22 -18
  51. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +1 -1
  52. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +4 -1
  53. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +4 -10
  54. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +2 -1
  55. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +2 -2
  56. data/lib/generators/ruby_llm/schema/schema_generator.rb +26 -0
  57. data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +2 -0
  58. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +9 -0
  59. data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +13 -0
  60. data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +13 -0
  61. data/lib/generators/ruby_llm/tool/tool_generator.rb +96 -0
  62. data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +19 -0
  63. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +50 -0
  64. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +7 -0
  65. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +49 -0
  66. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +2 -4
  67. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +1 -1
  68. data/lib/ruby_llm/active_record/acts_as.rb +10 -4
  69. data/lib/ruby_llm/active_record/acts_as_legacy.rb +132 -27
  70. data/lib/ruby_llm/active_record/chat_methods.rb +132 -28
  71. data/lib/ruby_llm/active_record/message_methods.rb +58 -8
  72. data/lib/ruby_llm/active_record/model_methods.rb +1 -1
  73. data/lib/ruby_llm/active_record/payload_helpers.rb +26 -0
  74. data/lib/ruby_llm/active_record/tool_call_methods.rb +15 -0
  75. data/lib/ruby_llm/agent.rb +365 -0
  76. data/lib/ruby_llm/aliases.json +106 -61
  77. data/lib/ruby_llm/attachment.rb +8 -3
  78. data/lib/ruby_llm/chat.rb +150 -22
  79. data/lib/ruby_llm/configuration.rb +65 -65
  80. data/lib/ruby_llm/connection.rb +11 -7
  81. data/lib/ruby_llm/content.rb +6 -2
  82. data/lib/ruby_llm/error.rb +37 -1
  83. data/lib/ruby_llm/message.rb +43 -15
  84. data/lib/ruby_llm/model/info.rb +15 -13
  85. data/lib/ruby_llm/models.json +25039 -12260
  86. data/lib/ruby_llm/models.rb +185 -24
  87. data/lib/ruby_llm/provider.rb +26 -4
  88. data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -119
  89. data/lib/ruby_llm/providers/anthropic/chat.rb +149 -17
  90. data/lib/ruby_llm/providers/anthropic/media.rb +2 -2
  91. data/lib/ruby_llm/providers/anthropic/models.rb +3 -9
  92. data/lib/ruby_llm/providers/anthropic/streaming.rb +25 -1
  93. data/lib/ruby_llm/providers/anthropic/tools.rb +20 -0
  94. data/lib/ruby_llm/providers/anthropic.rb +5 -1
  95. data/lib/ruby_llm/providers/azure/chat.rb +29 -0
  96. data/lib/ruby_llm/providers/azure/embeddings.rb +24 -0
  97. data/lib/ruby_llm/providers/azure/media.rb +45 -0
  98. data/lib/ruby_llm/providers/azure/models.rb +14 -0
  99. data/lib/ruby_llm/providers/azure.rb +148 -0
  100. data/lib/ruby_llm/providers/bedrock/auth.rb +122 -0
  101. data/lib/ruby_llm/providers/bedrock/chat.rb +357 -28
  102. data/lib/ruby_llm/providers/bedrock/media.rb +62 -33
  103. data/lib/ruby_llm/providers/bedrock/models.rb +104 -65
  104. data/lib/ruby_llm/providers/bedrock/streaming.rb +309 -8
  105. data/lib/ruby_llm/providers/bedrock.rb +69 -52
  106. data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -114
  107. data/lib/ruby_llm/providers/deepseek.rb +5 -1
  108. data/lib/ruby_llm/providers/dify/chat.rb +82 -7
  109. data/lib/ruby_llm/providers/dify/media.rb +2 -2
  110. data/lib/ruby_llm/providers/dify/streaming.rb +26 -4
  111. data/lib/ruby_llm/providers/dify.rb +4 -0
  112. data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -207
  113. data/lib/ruby_llm/providers/gemini/chat.rb +88 -6
  114. data/lib/ruby_llm/providers/gemini/images.rb +1 -1
  115. data/lib/ruby_llm/providers/gemini/models.rb +2 -4
  116. data/lib/ruby_llm/providers/gemini/streaming.rb +34 -2
  117. data/lib/ruby_llm/providers/gemini/tools.rb +35 -3
  118. data/lib/ruby_llm/providers/gemini.rb +4 -0
  119. data/lib/ruby_llm/providers/gpustack/capabilities.rb +20 -0
  120. data/lib/ruby_llm/providers/gpustack/chat.rb +1 -1
  121. data/lib/ruby_llm/providers/gpustack.rb +8 -0
  122. data/lib/ruby_llm/providers/mistral/capabilities.rb +8 -0
  123. data/lib/ruby_llm/providers/mistral/chat.rb +59 -1
  124. data/lib/ruby_llm/providers/mistral.rb +4 -0
  125. data/lib/ruby_llm/providers/ollama/capabilities.rb +20 -0
  126. data/lib/ruby_llm/providers/ollama/chat.rb +1 -1
  127. data/lib/ruby_llm/providers/ollama.rb +11 -1
  128. data/lib/ruby_llm/providers/openai/capabilities.rb +96 -192
  129. data/lib/ruby_llm/providers/openai/chat.rb +101 -7
  130. data/lib/ruby_llm/providers/openai/media.rb +5 -2
  131. data/lib/ruby_llm/providers/openai/models.rb +2 -4
  132. data/lib/ruby_llm/providers/openai/streaming.rb +11 -3
  133. data/lib/ruby_llm/providers/openai/temperature.rb +28 -0
  134. data/lib/ruby_llm/providers/openai/tools.rb +27 -2
  135. data/lib/ruby_llm/providers/openai.rb +11 -1
  136. data/lib/ruby_llm/providers/openrouter/chat.rb +168 -0
  137. data/lib/ruby_llm/providers/openrouter/images.rb +69 -0
  138. data/lib/ruby_llm/providers/openrouter/streaming.rb +74 -0
  139. data/lib/ruby_llm/providers/openrouter.rb +37 -1
  140. data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
  141. data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
  142. data/lib/ruby_llm/providers/perplexity.rb +4 -0
  143. data/lib/ruby_llm/providers/vertexai/models.rb +1 -1
  144. data/lib/ruby_llm/providers/vertexai.rb +23 -7
  145. data/lib/ruby_llm/providers/xai/chat.rb +15 -0
  146. data/lib/ruby_llm/providers/xai/models.rb +75 -0
  147. data/lib/ruby_llm/providers/xai.rb +32 -0
  148. data/lib/ruby_llm/stream_accumulator.rb +120 -18
  149. data/lib/ruby_llm/streaming.rb +60 -57
  150. data/lib/ruby_llm/thinking.rb +49 -0
  151. data/lib/ruby_llm/tokens.rb +47 -0
  152. data/lib/ruby_llm/tool.rb +48 -3
  153. data/lib/ruby_llm/tool_call.rb +6 -3
  154. data/lib/ruby_llm/version.rb +1 -1
  155. data/lib/ruby_llm.rb +14 -8
  156. data/lib/tasks/models.rake +61 -22
  157. data/lib/tasks/release.rake +1 -1
  158. data/lib/tasks/ruby_llm.rake +9 -1
  159. data/lib/tasks/vcr.rake +33 -1
  160. metadata +67 -16
  161. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +0 -13
  162. data/lib/ruby_llm/providers/bedrock/capabilities.rb +0 -167
  163. data/lib/ruby_llm/providers/bedrock/signing.rb +0 -831
  164. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -51
  165. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -71
  166. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -67
  167. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -80
  168. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -78
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Providers
5
- class Bedrock
6
- module Streaming
7
- # Base module for AWS Bedrock streaming functionality.
8
- module Base
9
- def self.included(base)
10
- base.include ContentExtraction
11
- base.include MessageProcessing
12
- base.include PayloadProcessing
13
- base.include PreludeHandling
14
- end
15
-
16
- def stream_url
17
- "model/#{@model_id}/invoke-with-response-stream"
18
- end
19
-
20
- def stream_response(connection, payload, additional_headers = {}, &block)
21
- signature = sign_request("#{connection.connection.url_prefix}#{stream_url}", payload:)
22
- accumulator = StreamAccumulator.new
23
-
24
- response = connection.post stream_url, payload do |req|
25
- req.headers.merge! build_headers(signature.headers, streaming: block_given?)
26
- # Merge additional headers, with existing headers taking precedence
27
- req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
28
- req.options.on_data = handle_stream do |chunk|
29
- accumulator.add chunk
30
- block.call chunk
31
- end
32
- end
33
-
34
- accumulator.to_message(response)
35
- end
36
-
37
- def handle_stream(&block)
38
- buffer = +''
39
- proc do |chunk, _bytes, env|
40
- if env && env.status != 200
41
- handle_failed_response(chunk, buffer, env)
42
- else
43
- process_chunk(chunk, &block)
44
- end
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Providers
5
- class Bedrock
6
- module Streaming
7
- # Module for handling content extraction from AWS Bedrock streaming responses.
8
- module ContentExtraction
9
- def json_delta?(data)
10
- data['type'] == 'content_block_delta' && data.dig('delta', 'type') == 'input_json_delta'
11
- end
12
-
13
- def extract_streaming_content(data)
14
- return '' unless data.is_a?(Hash)
15
-
16
- extract_content_by_type(data)
17
- end
18
-
19
- def extract_tool_calls(data)
20
- data.dig('message', 'tool_calls') || data['tool_calls']
21
- end
22
-
23
- def extract_model_id(data)
24
- data.dig('message', 'model') || @model_id
25
- end
26
-
27
- def extract_input_tokens(data)
28
- data.dig('message', 'usage', 'input_tokens')
29
- end
30
-
31
- def extract_output_tokens(data)
32
- data.dig('message', 'usage', 'output_tokens') || data.dig('usage', 'output_tokens')
33
- end
34
-
35
- def extract_cached_tokens(data)
36
- data.dig('message', 'usage', 'cache_read_input_tokens') || data.dig('usage', 'cache_read_input_tokens')
37
- end
38
-
39
- def extract_cache_creation_tokens(data)
40
- direct = data.dig('message', 'usage',
41
- 'cache_creation_input_tokens') || data.dig('usage', 'cache_creation_input_tokens')
42
- return direct if direct
43
-
44
- breakdown = data.dig('message', 'usage', 'cache_creation') || data.dig('usage', 'cache_creation')
45
- return unless breakdown.is_a?(Hash)
46
-
47
- breakdown.values.compact.sum
48
- end
49
-
50
- private
51
-
52
- def extract_content_by_type(data)
53
- case data['type']
54
- when 'content_block_start' then extract_block_start_content(data)
55
- when 'content_block_delta' then extract_delta_content(data)
56
- else ''
57
- end
58
- end
59
-
60
- def extract_block_start_content(data)
61
- data.dig('content_block', 'text').to_s
62
- end
63
-
64
- def extract_delta_content(data)
65
- data.dig('delta', 'text').to_s
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Providers
5
- class Bedrock
6
- module Streaming
7
- # Module for processing streaming messages from AWS Bedrock.
8
- module MessageProcessing
9
- def process_chunk(chunk, &)
10
- offset = 0
11
- offset = process_message(chunk, offset, &) while offset < chunk.bytesize
12
- rescue StandardError => e
13
- RubyLLM.logger.debug "Error processing chunk: #{e.message}"
14
- RubyLLM.logger.debug "Chunk size: #{chunk.bytesize}"
15
- end
16
-
17
- def process_message(chunk, offset, &)
18
- return chunk.bytesize unless can_read_prelude?(chunk, offset)
19
-
20
- message_info = extract_message_info(chunk, offset)
21
- return find_next_message(chunk, offset) unless message_info
22
-
23
- process_valid_message(chunk, offset, message_info, &)
24
- end
25
-
26
- def process_valid_message(chunk, offset, message_info, &)
27
- payload = extract_payload(chunk, message_info[:headers_end], message_info[:payload_end])
28
- return find_next_message(chunk, offset) unless valid_payload?(payload)
29
-
30
- process_payload(payload, &)
31
- offset + message_info[:total_length]
32
- end
33
-
34
- private
35
-
36
- def extract_message_info(chunk, offset)
37
- total_length, headers_length = read_prelude(chunk, offset)
38
- return unless valid_lengths?(total_length, headers_length)
39
-
40
- message_end = offset + total_length
41
- return unless chunk.bytesize >= message_end
42
-
43
- headers_end, payload_end = calculate_positions(offset, total_length, headers_length)
44
- return unless valid_positions?(headers_end, payload_end, chunk.bytesize)
45
-
46
- { total_length:, headers_length:, headers_end:, payload_end: }
47
- end
48
-
49
- def extract_payload(chunk, headers_end, payload_end)
50
- chunk[headers_end...payload_end]
51
- end
52
-
53
- def valid_payload?(payload)
54
- return false if payload.nil? || payload.empty?
55
-
56
- json_start = payload.index('{')
57
- json_end = payload.rindex('}')
58
-
59
- return false if json_start.nil? || json_end.nil? || json_start >= json_end
60
-
61
- true
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base64'
4
-
5
- module RubyLLM
6
- module Providers
7
- class Bedrock
8
- module Streaming
9
- # Module for processing payloads from AWS Bedrock streaming responses.
10
- module PayloadProcessing
11
- def process_payload(payload, &)
12
- json_payload = extract_json_payload(payload)
13
- parse_and_process_json(json_payload, &)
14
- rescue JSON::ParserError => e
15
- log_json_parse_error(e, json_payload)
16
- rescue StandardError => e
17
- log_general_error(e)
18
- end
19
-
20
- private
21
-
22
- def extract_json_payload(payload)
23
- json_start = payload.index('{')
24
- json_end = payload.rindex('}')
25
- payload[json_start..json_end]
26
- end
27
-
28
- def parse_and_process_json(json_payload, &)
29
- json_data = JSON.parse(json_payload)
30
- process_json_data(json_data, &)
31
- end
32
-
33
- def process_json_data(json_data, &)
34
- return unless json_data['bytes']
35
-
36
- data = decode_and_parse_data(json_data)
37
- create_and_yield_chunk(data, &)
38
- end
39
-
40
- def decode_and_parse_data(json_data)
41
- decoded_bytes = Base64.strict_decode64(json_data['bytes'])
42
- JSON.parse(decoded_bytes)
43
- end
44
-
45
- def create_and_yield_chunk(data, &block)
46
- block.call(build_chunk(data))
47
- end
48
-
49
- def build_chunk(data)
50
- Chunk.new(
51
- **extract_chunk_attributes(data)
52
- )
53
- end
54
-
55
- def extract_chunk_attributes(data)
56
- {
57
- role: :assistant,
58
- model_id: extract_model_id(data),
59
- content: extract_streaming_content(data),
60
- input_tokens: extract_input_tokens(data),
61
- output_tokens: extract_output_tokens(data),
62
- cached_tokens: extract_cached_tokens(data),
63
- cache_creation_tokens: extract_cache_creation_tokens(data),
64
- tool_calls: extract_tool_calls(data)
65
- }
66
- end
67
-
68
- def log_json_parse_error(error, json_payload)
69
- RubyLLM.logger.debug "Failed to parse payload as JSON: #{error.message}"
70
- RubyLLM.logger.debug "Attempted JSON payload: #{json_payload.inspect}"
71
- end
72
-
73
- def log_general_error(error)
74
- RubyLLM.logger.debug "Error processing payload: #{error.message}"
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Providers
5
- class Bedrock
6
- module Streaming
7
- # Module for handling message preludes in AWS Bedrock streaming responses.
8
- module PreludeHandling
9
- def can_read_prelude?(chunk, offset)
10
- chunk.bytesize - offset >= 12
11
- end
12
-
13
- def read_prelude(chunk, offset)
14
- total_length = chunk[offset...(offset + 4)].unpack1('N')
15
- headers_length = chunk[(offset + 4)...(offset + 8)].unpack1('N')
16
- [total_length, headers_length]
17
- end
18
-
19
- def valid_lengths?(total_length, headers_length)
20
- valid_length_constraints?(total_length, headers_length)
21
- end
22
-
23
- def calculate_positions(offset, total_length, headers_length)
24
- headers_end = offset + 12 + headers_length
25
- payload_end = offset + total_length - 4 # Subtract 4 bytes for message CRC
26
- [headers_end, payload_end]
27
- end
28
-
29
- def valid_positions?(headers_end, payload_end, chunk_size)
30
- return false if headers_end >= payload_end
31
- return false if headers_end >= chunk_size
32
- return false if payload_end > chunk_size
33
-
34
- true
35
- end
36
-
37
- def find_next_message(chunk, offset)
38
- next_prelude = find_next_prelude(chunk, offset + 4)
39
- next_prelude || chunk.bytesize
40
- end
41
-
42
- def find_next_prelude(chunk, start_offset)
43
- scan_range(chunk, start_offset).each do |pos|
44
- return pos if valid_prelude_at_position?(chunk, pos)
45
- end
46
- nil
47
- end
48
-
49
- private
50
-
51
- def scan_range(chunk, start_offset)
52
- (start_offset...(chunk.bytesize - 8))
53
- end
54
-
55
- def valid_prelude_at_position?(chunk, pos)
56
- lengths = extract_potential_lengths(chunk, pos)
57
- valid_length_constraints?(*lengths)
58
- end
59
-
60
- def extract_potential_lengths(chunk, pos)
61
- [
62
- chunk[pos...(pos + 4)].unpack1('N'),
63
- chunk[(pos + 4)...(pos + 8)].unpack1('N')
64
- ]
65
- end
66
-
67
- def valid_length_constraints?(total_length, headers_length)
68
- return false if total_length.nil? || headers_length.nil?
69
- return false if total_length <= 0 || total_length > 1_000_000
70
- return false if headers_length <= 0 || headers_length >= total_length
71
-
72
- true
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end