dify_llm 1.6.4

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 (129) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +157 -0
  4. data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +3 -0
  5. data/lib/generators/ruby_llm/install/templates/create_chats_legacy_migration.rb.tt +8 -0
  6. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +8 -0
  7. data/lib/generators/ruby_llm/install/templates/create_messages_legacy_migration.rb.tt +16 -0
  8. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +16 -0
  9. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +43 -0
  10. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +15 -0
  11. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +9 -0
  12. data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -0
  13. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
  14. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +3 -0
  15. data/lib/generators/ruby_llm/install_generator.rb +184 -0
  16. data/lib/generators/ruby_llm/migrate_model_fields/templates/migration.rb.tt +142 -0
  17. data/lib/generators/ruby_llm/migrate_model_fields_generator.rb +84 -0
  18. data/lib/ruby_llm/active_record/acts_as.rb +137 -0
  19. data/lib/ruby_llm/active_record/acts_as_legacy.rb +398 -0
  20. data/lib/ruby_llm/active_record/chat_methods.rb +315 -0
  21. data/lib/ruby_llm/active_record/message_methods.rb +72 -0
  22. data/lib/ruby_llm/active_record/model_methods.rb +84 -0
  23. data/lib/ruby_llm/aliases.json +274 -0
  24. data/lib/ruby_llm/aliases.rb +38 -0
  25. data/lib/ruby_llm/attachment.rb +191 -0
  26. data/lib/ruby_llm/chat.rb +212 -0
  27. data/lib/ruby_llm/chunk.rb +6 -0
  28. data/lib/ruby_llm/configuration.rb +69 -0
  29. data/lib/ruby_llm/connection.rb +137 -0
  30. data/lib/ruby_llm/content.rb +50 -0
  31. data/lib/ruby_llm/context.rb +29 -0
  32. data/lib/ruby_llm/embedding.rb +29 -0
  33. data/lib/ruby_llm/error.rb +76 -0
  34. data/lib/ruby_llm/image.rb +49 -0
  35. data/lib/ruby_llm/message.rb +76 -0
  36. data/lib/ruby_llm/mime_type.rb +67 -0
  37. data/lib/ruby_llm/model/info.rb +103 -0
  38. data/lib/ruby_llm/model/modalities.rb +22 -0
  39. data/lib/ruby_llm/model/pricing.rb +48 -0
  40. data/lib/ruby_llm/model/pricing_category.rb +46 -0
  41. data/lib/ruby_llm/model/pricing_tier.rb +33 -0
  42. data/lib/ruby_llm/model.rb +7 -0
  43. data/lib/ruby_llm/models.json +31418 -0
  44. data/lib/ruby_llm/models.rb +235 -0
  45. data/lib/ruby_llm/models_schema.json +168 -0
  46. data/lib/ruby_llm/provider.rb +215 -0
  47. data/lib/ruby_llm/providers/anthropic/capabilities.rb +134 -0
  48. data/lib/ruby_llm/providers/anthropic/chat.rb +106 -0
  49. data/lib/ruby_llm/providers/anthropic/embeddings.rb +20 -0
  50. data/lib/ruby_llm/providers/anthropic/media.rb +91 -0
  51. data/lib/ruby_llm/providers/anthropic/models.rb +48 -0
  52. data/lib/ruby_llm/providers/anthropic/streaming.rb +43 -0
  53. data/lib/ruby_llm/providers/anthropic/tools.rb +107 -0
  54. data/lib/ruby_llm/providers/anthropic.rb +36 -0
  55. data/lib/ruby_llm/providers/bedrock/capabilities.rb +167 -0
  56. data/lib/ruby_llm/providers/bedrock/chat.rb +63 -0
  57. data/lib/ruby_llm/providers/bedrock/media.rb +60 -0
  58. data/lib/ruby_llm/providers/bedrock/models.rb +98 -0
  59. data/lib/ruby_llm/providers/bedrock/signing.rb +831 -0
  60. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +51 -0
  61. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +56 -0
  62. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +67 -0
  63. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +78 -0
  64. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +78 -0
  65. data/lib/ruby_llm/providers/bedrock/streaming.rb +18 -0
  66. data/lib/ruby_llm/providers/bedrock.rb +82 -0
  67. data/lib/ruby_llm/providers/deepseek/capabilities.rb +130 -0
  68. data/lib/ruby_llm/providers/deepseek/chat.rb +16 -0
  69. data/lib/ruby_llm/providers/deepseek.rb +30 -0
  70. data/lib/ruby_llm/providers/dify/capabilities.rb +16 -0
  71. data/lib/ruby_llm/providers/dify/chat.rb +59 -0
  72. data/lib/ruby_llm/providers/dify/media.rb +37 -0
  73. data/lib/ruby_llm/providers/dify/streaming.rb +28 -0
  74. data/lib/ruby_llm/providers/dify.rb +48 -0
  75. data/lib/ruby_llm/providers/gemini/capabilities.rb +276 -0
  76. data/lib/ruby_llm/providers/gemini/chat.rb +171 -0
  77. data/lib/ruby_llm/providers/gemini/embeddings.rb +37 -0
  78. data/lib/ruby_llm/providers/gemini/images.rb +47 -0
  79. data/lib/ruby_llm/providers/gemini/media.rb +54 -0
  80. data/lib/ruby_llm/providers/gemini/models.rb +40 -0
  81. data/lib/ruby_llm/providers/gemini/streaming.rb +61 -0
  82. data/lib/ruby_llm/providers/gemini/tools.rb +77 -0
  83. data/lib/ruby_llm/providers/gemini.rb +36 -0
  84. data/lib/ruby_llm/providers/gpustack/chat.rb +27 -0
  85. data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
  86. data/lib/ruby_llm/providers/gpustack/models.rb +90 -0
  87. data/lib/ruby_llm/providers/gpustack.rb +34 -0
  88. data/lib/ruby_llm/providers/mistral/capabilities.rb +155 -0
  89. data/lib/ruby_llm/providers/mistral/chat.rb +24 -0
  90. data/lib/ruby_llm/providers/mistral/embeddings.rb +33 -0
  91. data/lib/ruby_llm/providers/mistral/models.rb +48 -0
  92. data/lib/ruby_llm/providers/mistral.rb +32 -0
  93. data/lib/ruby_llm/providers/ollama/chat.rb +27 -0
  94. data/lib/ruby_llm/providers/ollama/media.rb +45 -0
  95. data/lib/ruby_llm/providers/ollama/models.rb +36 -0
  96. data/lib/ruby_llm/providers/ollama.rb +30 -0
  97. data/lib/ruby_llm/providers/openai/capabilities.rb +291 -0
  98. data/lib/ruby_llm/providers/openai/chat.rb +83 -0
  99. data/lib/ruby_llm/providers/openai/embeddings.rb +33 -0
  100. data/lib/ruby_llm/providers/openai/images.rb +38 -0
  101. data/lib/ruby_llm/providers/openai/media.rb +80 -0
  102. data/lib/ruby_llm/providers/openai/models.rb +39 -0
  103. data/lib/ruby_llm/providers/openai/streaming.rb +41 -0
  104. data/lib/ruby_llm/providers/openai/tools.rb +78 -0
  105. data/lib/ruby_llm/providers/openai.rb +42 -0
  106. data/lib/ruby_llm/providers/openrouter/models.rb +73 -0
  107. data/lib/ruby_llm/providers/openrouter.rb +26 -0
  108. data/lib/ruby_llm/providers/perplexity/capabilities.rb +137 -0
  109. data/lib/ruby_llm/providers/perplexity/chat.rb +16 -0
  110. data/lib/ruby_llm/providers/perplexity/models.rb +42 -0
  111. data/lib/ruby_llm/providers/perplexity.rb +48 -0
  112. data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
  113. data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
  114. data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
  115. data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
  116. data/lib/ruby_llm/providers/vertexai.rb +55 -0
  117. data/lib/ruby_llm/railtie.rb +41 -0
  118. data/lib/ruby_llm/stream_accumulator.rb +97 -0
  119. data/lib/ruby_llm/streaming.rb +153 -0
  120. data/lib/ruby_llm/tool.rb +83 -0
  121. data/lib/ruby_llm/tool_call.rb +22 -0
  122. data/lib/ruby_llm/utils.rb +45 -0
  123. data/lib/ruby_llm/version.rb +5 -0
  124. data/lib/ruby_llm.rb +97 -0
  125. data/lib/tasks/models.rake +525 -0
  126. data/lib/tasks/release.rake +67 -0
  127. data/lib/tasks/ruby_llm.rake +15 -0
  128. data/lib/tasks/vcr.rake +92 -0
  129. metadata +291 -0
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ class Bedrock
6
+ # Determines capabilities and pricing for AWS Bedrock models
7
+ module Capabilities
8
+ module_function
9
+
10
+ def context_window_for(model_id)
11
+ case model_id
12
+ when /anthropic\.claude-2/ then 100_000
13
+ else 200_000
14
+ end
15
+ end
16
+
17
+ def max_tokens_for(_model_id)
18
+ 4_096
19
+ end
20
+
21
+ def input_price_for(model_id)
22
+ PRICES.dig(model_family(model_id), :input) || default_input_price
23
+ end
24
+
25
+ def output_price_for(model_id)
26
+ PRICES.dig(model_family(model_id), :output) || default_output_price
27
+ end
28
+
29
+ def supports_chat?(model_id)
30
+ model_id.match?(/anthropic\.claude/)
31
+ end
32
+
33
+ def supports_streaming?(model_id)
34
+ model_id.match?(/anthropic\.claude/)
35
+ end
36
+
37
+ def supports_images?(model_id)
38
+ model_id.match?(/anthropic\.claude/)
39
+ end
40
+
41
+ def supports_vision?(model_id)
42
+ model_id.match?(/anthropic\.claude/)
43
+ end
44
+
45
+ def supports_functions?(model_id)
46
+ model_id.match?(/anthropic\.claude/)
47
+ end
48
+
49
+ def supports_audio?(_model_id)
50
+ false
51
+ end
52
+
53
+ def supports_json_mode?(model_id)
54
+ model_id.match?(/anthropic\.claude/)
55
+ end
56
+
57
+ def format_display_name(model_id)
58
+ model_id.then { |id| humanize(id) }
59
+ end
60
+
61
+ def model_type(_model_id)
62
+ 'chat'
63
+ end
64
+
65
+ def supports_structured_output?(_model_id)
66
+ false
67
+ end
68
+
69
+ # Model family patterns for capability lookup
70
+ MODEL_FAMILIES = {
71
+ /anthropic\.claude-3-opus/ => :claude3_opus,
72
+ /anthropic\.claude-3-sonnet/ => :claude3_sonnet,
73
+ /anthropic\.claude-3-5-sonnet/ => :claude3_sonnet,
74
+ /anthropic\.claude-3-7-sonnet/ => :claude3_sonnet,
75
+ /anthropic\.claude-3-haiku/ => :claude3_haiku,
76
+ /anthropic\.claude-3-5-haiku/ => :claude3_5_haiku,
77
+ /anthropic\.claude-v2/ => :claude2,
78
+ /anthropic\.claude-instant/ => :claude_instant
79
+ }.freeze
80
+
81
+ def model_family(model_id)
82
+ MODEL_FAMILIES.find { |pattern, _family| model_id.match?(pattern) }&.last || :other
83
+ end
84
+
85
+ # Pricing information for Bedrock models (per million tokens)
86
+ PRICES = {
87
+ claude3_opus: { input: 15.0, output: 75.0 },
88
+ claude3_sonnet: { input: 3.0, output: 15.0 },
89
+ claude3_haiku: { input: 0.25, output: 1.25 },
90
+ claude3_5_haiku: { input: 0.8, output: 4.0 },
91
+ claude2: { input: 8.0, output: 24.0 },
92
+ claude_instant: { input: 0.8, output: 2.4 }
93
+ }.freeze
94
+
95
+ def default_input_price
96
+ 0.1
97
+ end
98
+
99
+ def default_output_price
100
+ 0.2
101
+ end
102
+
103
+ def humanize(id)
104
+ id.tr('-', ' ')
105
+ .split('.')
106
+ .last
107
+ .split
108
+ .map(&:capitalize)
109
+ .join(' ')
110
+ end
111
+
112
+ def modalities_for(model_id)
113
+ modalities = {
114
+ input: ['text'],
115
+ output: ['text']
116
+ }
117
+
118
+ if model_id.match?(/anthropic\.claude/) && supports_vision?(model_id)
119
+ modalities[:input] << 'image'
120
+ modalities[:input] << 'pdf'
121
+ end
122
+
123
+ modalities
124
+ end
125
+
126
+ def capabilities_for(model_id)
127
+ capabilities = []
128
+
129
+ capabilities << 'streaming' if model_id.match?(/anthropic\.claude/)
130
+
131
+ capabilities << 'function_calling' if supports_functions?(model_id)
132
+
133
+ capabilities << 'reasoning' if model_id.match?(/claude-3-7/)
134
+
135
+ if model_id.match?(/claude-3\.5|claude-3-7/)
136
+ capabilities << 'batch'
137
+ capabilities << 'citations'
138
+ end
139
+
140
+ capabilities
141
+ end
142
+
143
+ def pricing_for(model_id)
144
+ family = model_family(model_id)
145
+ prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
146
+
147
+ standard_pricing = {
148
+ input_per_million: prices[:input],
149
+ output_per_million: prices[:output]
150
+ }
151
+
152
+ batch_pricing = {
153
+ input_per_million: prices[:input] * 0.5,
154
+ output_per_million: prices[:output] * 0.5
155
+ }
156
+
157
+ {
158
+ text_tokens: {
159
+ standard: standard_pricing,
160
+ batch: batch_pricing
161
+ }
162
+ }
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ class Bedrock
6
+ # Chat methods for the AWS Bedrock API implementation
7
+ module Chat
8
+ module_function
9
+
10
+ def sync_response(connection, payload, additional_headers = {})
11
+ signature = sign_request("#{connection.connection.url_prefix}#{completion_url}", payload:)
12
+ response = connection.post completion_url, payload do |req|
13
+ req.headers.merge! build_headers(signature.headers, streaming: block_given?)
14
+ req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
15
+ end
16
+ Anthropic::Chat.parse_completion_response response
17
+ end
18
+
19
+ def format_message(msg)
20
+ if msg.tool_call?
21
+ Anthropic::Tools.format_tool_call(msg)
22
+ elsif msg.tool_result?
23
+ Anthropic::Tools.format_tool_result(msg)
24
+ else
25
+ format_basic_message(msg)
26
+ end
27
+ end
28
+
29
+ def format_basic_message(msg)
30
+ {
31
+ role: Anthropic::Chat.convert_role(msg.role),
32
+ content: Media.format_content(msg.content)
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ def completion_url
39
+ "model/#{@model_id}/invoke"
40
+ end
41
+
42
+ def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil) # rubocop:disable Lint/UnusedMethodArgument,Metrics/ParameterLists
43
+ @model_id = model.id
44
+
45
+ system_messages, chat_messages = Anthropic::Chat.separate_messages(messages)
46
+ system_content = Anthropic::Chat.build_system_content(system_messages)
47
+
48
+ build_base_payload(chat_messages, model).tap do |payload|
49
+ Anthropic::Chat.add_optional_fields(payload, system_content:, tools:, temperature:)
50
+ end
51
+ end
52
+
53
+ def build_base_payload(chat_messages, model)
54
+ {
55
+ anthropic_version: 'bedrock-2023-05-31',
56
+ messages: chat_messages.map { |msg| format_message(msg) },
57
+ max_tokens: model.max_tokens || 4096
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ class Bedrock
6
+ # Media handling methods for the Bedrock API integration
7
+ # NOTE: Bedrock does not support url attachments
8
+ module Media
9
+ extend Anthropic::Media
10
+
11
+ module_function
12
+
13
+ def format_content(content)
14
+ return [Anthropic::Media.format_text(content.to_json)] if content.is_a?(Hash) || content.is_a?(Array)
15
+ return [Anthropic::Media.format_text(content)] unless content.is_a?(Content)
16
+
17
+ parts = []
18
+ parts << Anthropic::Media.format_text(content.text) if content.text
19
+
20
+ content.attachments.each do |attachment|
21
+ case attachment.type
22
+ when :image
23
+ parts << format_image(attachment)
24
+ when :pdf
25
+ parts << format_pdf(attachment)
26
+ when :text
27
+ parts << Anthropic::Media.format_text_file(attachment)
28
+ else
29
+ raise UnsupportedAttachmentError, attachment.type
30
+ end
31
+ end
32
+
33
+ parts
34
+ end
35
+
36
+ def format_image(image)
37
+ {
38
+ type: 'image',
39
+ source: {
40
+ type: 'base64',
41
+ media_type: image.mime_type,
42
+ data: image.encoded
43
+ }
44
+ }
45
+ end
46
+
47
+ def format_pdf(pdf)
48
+ {
49
+ type: 'document',
50
+ source: {
51
+ type: 'base64',
52
+ media_type: pdf.mime_type,
53
+ data: pdf.encoded
54
+ }
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ class Bedrock
6
+ # Models methods for the AWS Bedrock API implementation
7
+ module Models
8
+ def list_models
9
+ mgmt_api_base = "https://bedrock.#{@config.bedrock_region}.amazonaws.com"
10
+ full_models_url = "#{mgmt_api_base}/#{models_url}"
11
+ signature = sign_request(full_models_url, method: :get)
12
+ response = @connection.get(full_models_url) do |req|
13
+ req.headers.merge! signature.headers
14
+ end
15
+
16
+ parse_list_models_response(response, slug, capabilities)
17
+ end
18
+
19
+ module_function
20
+
21
+ def models_url
22
+ 'foundation-models'
23
+ end
24
+
25
+ def parse_list_models_response(response, slug, capabilities)
26
+ models = Array(response.body['modelSummaries'])
27
+
28
+ models.select { |m| m['modelId'].include?('claude') }.map do |model_data|
29
+ model_id = model_data['modelId']
30
+
31
+ Model::Info.new(
32
+ id: model_id_with_region(model_id, model_data),
33
+ name: model_data['modelName'] || capabilities.format_display_name(model_id),
34
+ provider: slug,
35
+ family: capabilities.model_family(model_id),
36
+ created_at: nil,
37
+ context_window: capabilities.context_window_for(model_id),
38
+ max_output_tokens: capabilities.max_tokens_for(model_id),
39
+ modalities: capabilities.modalities_for(model_id),
40
+ capabilities: capabilities.capabilities_for(model_id),
41
+ pricing: capabilities.pricing_for(model_id),
42
+ metadata: {
43
+ provider_name: model_data['providerName'],
44
+ inference_types: model_data['inferenceTypesSupported'] || [],
45
+ streaming_supported: model_data['responseStreamingSupported'] || false,
46
+ input_modalities: model_data['inputModalities'] || [],
47
+ output_modalities: model_data['outputModalities'] || []
48
+ }
49
+ )
50
+ end
51
+ end
52
+
53
+ def create_model_info(model_data, slug, _capabilities)
54
+ model_id = model_data['modelId']
55
+
56
+ Model::Info.new(
57
+ id: model_id_with_region(model_id, model_data),
58
+ name: model_data['modelName'] || model_id,
59
+ provider: slug,
60
+ family: 'claude',
61
+ created_at: nil,
62
+ context_window: 200_000,
63
+ max_output_tokens: 4096,
64
+ modalities: { input: ['text'], output: ['text'] },
65
+ capabilities: [],
66
+ pricing: {},
67
+ metadata: {}
68
+ )
69
+ end
70
+
71
+ def model_id_with_region(model_id, model_data)
72
+ return model_id unless model_data['inferenceTypesSupported']&.include?('INFERENCE_PROFILE')
73
+ return model_id if model_data['inferenceTypesSupported']&.include?('ON_DEMAND')
74
+
75
+ desired_region_prefix = inference_profile_region_prefix
76
+
77
+ # Return unchanged if model already has the correct region prefix
78
+ return model_id if model_id.start_with?("#{desired_region_prefix}.")
79
+
80
+ # Remove any existing region prefix (e.g., "us.", "eu.", "ap.")
81
+ clean_model_id = model_id.sub(/^[a-z]{2}\./, '')
82
+
83
+ # Apply the desired region prefix
84
+ "#{desired_region_prefix}.#{clean_model_id}"
85
+ end
86
+
87
+ def inference_profile_region_prefix
88
+ # Extract region prefix from bedrock_region (e.g., "eu-west-3" -> "eu")
89
+ region = @config.bedrock_region.to_s
90
+ return 'us' if region.empty? # Default fallback
91
+
92
+ # Take first two characters as the region prefix
93
+ region[0, 2]
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end